diff options
826 files changed, 38858 insertions, 17846 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-led-flash b/Documentation/ABI/testing/sysfs-class-led-flash new file mode 100644 index 000000000000..220a0270b47b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-flash @@ -0,0 +1,80 @@ +What: /sys/class/leds/<led>/flash_brightness +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read/write + Set the brightness of this LED in the flash strobe mode, in + microamperes. The file is created only for the flash LED devices + that support setting flash brightness. + + The value is between 0 and + /sys/class/leds/<led>/max_flash_brightness. + +What: /sys/class/leds/<led>/max_flash_brightness +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read only + Maximum brightness level for this LED in the flash strobe mode, + in microamperes. + +What: /sys/class/leds/<led>/flash_timeout +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read/write + Hardware timeout for flash, in microseconds. The flash strobe + is stopped after this period of time has passed from the start + of the strobe. The file is created only for the flash LED + devices that support setting flash timeout. + +What: /sys/class/leds/<led>/max_flash_timeout +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read only + Maximum flash timeout for this LED, in microseconds. + +What: /sys/class/leds/<led>/flash_strobe +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read/write + Flash strobe state. When written with 1 it triggers flash strobe + and when written with 0 it turns the flash off. + + On read 1 means that flash is currently strobing and 0 means + that flash is off. + +What: /sys/class/leds/<led>/flash_fault +Date: March 2015 +KernelVersion: 4.0 +Contact: Jacek Anaszewski <j.anaszewski@samsung.com> +Description: read only + Space separated list of flash faults that may have occurred. + Flash faults are re-read after strobing the flash. Possible + flash faults: + + * led-over-voltage - flash controller voltage to the flash LED + has exceeded the limit specific to the flash controller + * flash-timeout-exceeded - the flash strobe was still on when + the timeout set by the user has expired; not all flash + controllers may set this in all such conditions + * controller-over-temperature - the flash controller has + overheated + * controller-short-circuit - the short circuit protection + of the flash controller has been triggered + * led-power-supply-over-current - current in the LED power + supply has exceeded the limit specific to the flash + controller + * indicator-led-fault - the flash controller has detected + a short or open circuit condition on the indicator LED + * led-under-voltage - flash controller voltage to the flash + LED has been below the minimum limit specific to + the flash + * controller-under-voltage - the input voltage of the flash + controller is below the limit under which strobing the + flash at full current will not be possible; + the condition persists until this flag is no longer set + * led-over-temperature - the temperature of the LED has exceeded + its allowed upper limit diff --git a/Documentation/DocBook/crypto-API.tmpl b/Documentation/DocBook/crypto-API.tmpl index 04a8c24ead47..efc8d90a9a3f 100644 --- a/Documentation/DocBook/crypto-API.tmpl +++ b/Documentation/DocBook/crypto-API.tmpl @@ -509,6 +509,270 @@ select it due to the used type and mask field. </para> </sect1> + + <sect1><title>Internal Structure of Kernel Crypto API</title> + + <para> + The kernel crypto API has an internal structure where a cipher + implementation may use many layers and indirections. This section + shall help to clarify how the kernel crypto API uses + various components to implement the complete cipher. + </para> + + <para> + The following subsections explain the internal structure based + on existing cipher implementations. The first section addresses + the most complex scenario where all other scenarios form a logical + subset. + </para> + + <sect2><title>Generic AEAD Cipher Structure</title> + + <para> + The following ASCII art decomposes the kernel crypto API layers + when using the AEAD cipher with the automated IV generation. The + shown example is used by the IPSEC layer. + </para> + + <para> + For other use cases of AEAD ciphers, the ASCII art applies as + well, but the caller may not use the GIVCIPHER interface. In + this case, the caller must generate the IV. + </para> + + <para> + The depicted example decomposes the AEAD cipher of GCM(AES) based + on the generic C implementations (gcm.c, aes-generic.c, ctr.c, + ghash-generic.c, seqiv.c). The generic implementation serves as an + example showing the complete logic of the kernel crypto API. + </para> + + <para> + It is possible that some streamlined cipher implementations (like + AES-NI) provide implementations merging aspects which in the view + of the kernel crypto API cannot be decomposed into layers any more. + In case of the AES-NI implementation, the CTR mode, the GHASH + implementation and the AES cipher are all merged into one cipher + implementation registered with the kernel crypto API. In this case, + the concept described by the following ASCII art applies too. However, + the decomposition of GCM into the individual sub-components + by the kernel crypto API is not done any more. + </para> + + <para> + Each block in the following ASCII art is an independent cipher + instance obtained from the kernel crypto API. Each block + is accessed by the caller or by other blocks using the API functions + defined by the kernel crypto API for the cipher implementation type. + </para> + + <para> + The blocks below indicate the cipher type as well as the specific + logic implemented in the cipher. + </para> + + <para> + The ASCII art picture also indicates the call structure, i.e. who + calls which component. The arrows point to the invoked block + where the caller uses the API applicable to the cipher type + specified for the block. + </para> + + <programlisting> +<![CDATA[ +kernel crypto API | IPSEC Layer + | ++-----------+ | +| | (1) +| givcipher | <----------------------------------- esp_output +| (seqiv) | ---+ ++-----------+ | + | (2) ++-----------+ | +| | <--+ (2) +| aead | <----------------------------------- esp_input +| (gcm) | ------------+ ++-----------+ | + | (3) | (5) + v v ++-----------+ +-----------+ +| | | | +| ablkcipher| | ahash | +| (ctr) | ---+ | (ghash) | ++-----------+ | +-----------+ + | ++-----------+ | (4) +| | <--+ +| cipher | +| (aes) | ++-----------+ +]]> + </programlisting> + + <para> + The following call sequence is applicable when the IPSEC layer + triggers an encryption operation with the esp_output function. During + configuration, the administrator set up the use of rfc4106(gcm(aes)) as + the cipher for ESP. The following call sequence is now depicted in the + ASCII art above: + </para> + + <orderedlist> + <listitem> + <para> + esp_output() invokes crypto_aead_givencrypt() to trigger an encryption + operation of the GIVCIPHER implementation. + </para> + + <para> + In case of GCM, the SEQIV implementation is registered as GIVCIPHER + in crypto_rfc4106_alloc(). + </para> + + <para> + The SEQIV performs its operation to generate an IV where the core + function is seqiv_geniv(). + </para> + </listitem> + + <listitem> + <para> + Now, SEQIV uses the AEAD API function calls to invoke the associated + AEAD cipher. In our case, during the instantiation of SEQIV, the + cipher handle for GCM is provided to SEQIV. This means that SEQIV + invokes AEAD cipher operations with the GCM cipher handle. + </para> + + <para> + During instantiation of the GCM handle, the CTR(AES) and GHASH + ciphers are instantiated. The cipher handles for CTR(AES) and GHASH + are retained for later use. + </para> + + <para> + The GCM implementation is responsible to invoke the CTR mode AES and + the GHASH cipher in the right manner to implement the GCM + specification. + </para> + </listitem> + + <listitem> + <para> + The GCM AEAD cipher type implementation now invokes the ABLKCIPHER API + with the instantiated CTR(AES) cipher handle. + </para> + + <para> + During instantiation of the CTR(AES) cipher, the CIPHER type + implementation of AES is instantiated. The cipher handle for AES is + retained. + </para> + + <para> + That means that the ABLKCIPHER implementation of CTR(AES) only + implements the CTR block chaining mode. After performing the block + chaining operation, the CIPHER implementation of AES is invoked. + </para> + </listitem> + + <listitem> + <para> + The ABLKCIPHER of CTR(AES) now invokes the CIPHER API with the AES + cipher handle to encrypt one block. + </para> + </listitem> + + <listitem> + <para> + The GCM AEAD implementation also invokes the GHASH cipher + implementation via the AHASH API. + </para> + </listitem> + </orderedlist> + + <para> + When the IPSEC layer triggers the esp_input() function, the same call + sequence is followed with the only difference that the operation starts + with step (2). + </para> + </sect2> + + <sect2><title>Generic Block Cipher Structure</title> + <para> + Generic block ciphers follow the same concept as depicted with the ASCII + art picture above. + </para> + + <para> + For example, CBC(AES) is implemented with cbc.c, and aes-generic.c. The + ASCII art picture above applies as well with the difference that only + step (4) is used and the ABLKCIPHER block chaining mode is CBC. + </para> + </sect2> + + <sect2><title>Generic Keyed Message Digest Structure</title> + <para> + Keyed message digest implementations again follow the same concept as + depicted in the ASCII art picture above. + </para> + + <para> + For example, HMAC(SHA256) is implemented with hmac.c and + sha256_generic.c. The following ASCII art illustrates the + implementation: + </para> + + <programlisting> +<![CDATA[ +kernel crypto API | Caller + | ++-----------+ (1) | +| | <------------------ some_function +| ahash | +| (hmac) | ---+ ++-----------+ | + | (2) ++-----------+ | +| | <--+ +| shash | +| (sha256) | ++-----------+ +]]> + </programlisting> + + <para> + The following call sequence is applicable when a caller triggers + an HMAC operation: + </para> + + <orderedlist> + <listitem> + <para> + The AHASH API functions are invoked by the caller. The HMAC + implementation performs its operation as needed. + </para> + + <para> + During initialization of the HMAC cipher, the SHASH cipher type of + SHA256 is instantiated. The cipher handle for the SHA256 instance is + retained. + </para> + + <para> + At one time, the HMAC implementation requires a SHA256 operation + where the SHA256 cipher handle is used. + </para> + </listitem> + + <listitem> + <para> + The HMAC instance now invokes the SHASH API with the SHA256 + cipher handle to calculate the message digest. + </para> + </listitem> + </orderedlist> + </sect2> + </sect1> </chapter> <chapter id="Development"><title>Developing Cipher Algorithms</title> @@ -808,6 +1072,602 @@ </sect1> </chapter> + <chapter id="User"><title>User Space Interface</title> + <sect1><title>Introduction</title> + <para> + The concepts of the kernel crypto API visible to kernel space is fully + applicable to the user space interface as well. Therefore, the kernel + crypto API high level discussion for the in-kernel use cases applies + here as well. + </para> + + <para> + The major difference, however, is that user space can only act as a + consumer and never as a provider of a transformation or cipher algorithm. + </para> + + <para> + The following covers the user space interface exported by the kernel + crypto API. A working example of this description is libkcapi that + can be obtained from [1]. That library can be used by user space + applications that require cryptographic services from the kernel. + </para> + + <para> + Some details of the in-kernel kernel crypto API aspects do not + apply to user space, however. This includes the difference between + synchronous and asynchronous invocations. The user space API call + is fully synchronous. + </para> + + <para> + [1] http://www.chronox.de/libkcapi.html + </para> + + </sect1> + + <sect1><title>User Space API General Remarks</title> + <para> + The kernel crypto API is accessible from user space. Currently, + the following ciphers are accessible: + </para> + + <itemizedlist> + <listitem> + <para>Message digest including keyed message digest (HMAC, CMAC)</para> + </listitem> + + <listitem> + <para>Symmetric ciphers</para> + </listitem> + + <listitem> + <para>AEAD ciphers</para> + </listitem> + + <listitem> + <para>Random Number Generators</para> + </listitem> + </itemizedlist> + + <para> + The interface is provided via socket type using the type AF_ALG. + In addition, the setsockopt option type is SOL_ALG. In case the + user space header files do not export these flags yet, use the + following macros: + </para> + + <programlisting> +#ifndef AF_ALG +#define AF_ALG 38 +#endif +#ifndef SOL_ALG +#define SOL_ALG 279 +#endif + </programlisting> + + <para> + A cipher is accessed with the same name as done for the in-kernel + API calls. This includes the generic vs. unique naming schema for + ciphers as well as the enforcement of priorities for generic names. + </para> + + <para> + To interact with the kernel crypto API, a socket must be + created by the user space application. User space invokes the cipher + operation with the send()/write() system call family. The result of the + cipher operation is obtained with the read()/recv() system call family. + </para> + + <para> + The following API calls assume that the socket descriptor + is already opened by the user space application and discusses only + the kernel crypto API specific invocations. + </para> + + <para> + To initialize the socket interface, the following sequence has to + be performed by the consumer: + </para> + + <orderedlist> + <listitem> + <para> + Create a socket of type AF_ALG with the struct sockaddr_alg + parameter specified below for the different cipher types. + </para> + </listitem> + + <listitem> + <para> + Invoke bind with the socket descriptor + </para> + </listitem> + + <listitem> + <para> + Invoke accept with the socket descriptor. The accept system call + returns a new file descriptor that is to be used to interact with + the particular cipher instance. When invoking send/write or recv/read + system calls to send data to the kernel or obtain data from the + kernel, the file descriptor returned by accept must be used. + </para> + </listitem> + </orderedlist> + </sect1> + + <sect1><title>In-place Cipher operation</title> + <para> + Just like the in-kernel operation of the kernel crypto API, the user + space interface allows the cipher operation in-place. That means that + the input buffer used for the send/write system call and the output + buffer used by the read/recv system call may be one and the same. + This is of particular interest for symmetric cipher operations where a + copying of the output data to its final destination can be avoided. + </para> + + <para> + If a consumer on the other hand wants to maintain the plaintext and + the ciphertext in different memory locations, all a consumer needs + to do is to provide different memory pointers for the encryption and + decryption operation. + </para> + </sect1> + + <sect1><title>Message Digest API</title> + <para> + The message digest type to be used for the cipher operation is + selected when invoking the bind syscall. bind requires the caller + to provide a filled struct sockaddr data structure. This data + structure must be filled as follows: + </para> + + <programlisting> +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", /* this selects the hash logic in the kernel */ + .salg_name = "sha1" /* this is the cipher name */ +}; + </programlisting> + + <para> + The salg_type value "hash" applies to message digests and keyed + message digests. Though, a keyed message digest is referenced by + the appropriate salg_name. Please see below for the setsockopt + interface that explains how the key can be set for a keyed message + digest. + </para> + + <para> + Using the send() system call, the application provides the data that + should be processed with the message digest. The send system call + allows the following flags to be specified: + </para> + + <itemizedlist> + <listitem> + <para> + MSG_MORE: If this flag is set, the send system call acts like a + message digest update function where the final hash is not + yet calculated. If the flag is not set, the send system call + calculates the final message digest immediately. + </para> + </listitem> + </itemizedlist> + + <para> + With the recv() system call, the application can read the message + digest from the kernel crypto API. If the buffer is too small for the + message digest, the flag MSG_TRUNC is set by the kernel. + </para> + + <para> + In order to set a message digest key, the calling application must use + the setsockopt() option of ALG_SET_KEY. If the key is not set the HMAC + operation is performed without the initial HMAC state change caused by + the key. + </para> + </sect1> + + <sect1><title>Symmetric Cipher API</title> + <para> + The operation is very similar to the message digest discussion. + During initialization, the struct sockaddr data structure must be + filled as follows: + </para> + + <programlisting> +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "skcipher", /* this selects the symmetric cipher */ + .salg_name = "cbc(aes)" /* this is the cipher name */ +}; + </programlisting> + + <para> + Before data can be sent to the kernel using the write/send system + call family, the consumer must set the key. The key setting is + described with the setsockopt invocation below. + </para> + + <para> + Using the sendmsg() system call, the application provides the data that should be processed for encryption or decryption. In addition, the IV is + specified with the data structure provided by the sendmsg() system call. + </para> + + <para> + The sendmsg system call parameter of struct msghdr is embedded into the + struct cmsghdr data structure. See recv(2) and cmsg(3) for more + information on how the cmsghdr data structure is used together with the + send/recv system call family. That cmsghdr data structure holds the + following information specified with a separate header instances: + </para> + + <itemizedlist> + <listitem> + <para> + specification of the cipher operation type with one of these flags: + </para> + <itemizedlist> + <listitem> + <para>ALG_OP_ENCRYPT - encryption of data</para> + </listitem> + <listitem> + <para>ALG_OP_DECRYPT - decryption of data</para> + </listitem> + </itemizedlist> + </listitem> + + <listitem> + <para> + specification of the IV information marked with the flag ALG_SET_IV + </para> + </listitem> + </itemizedlist> + + <para> + The send system call family allows the following flag to be specified: + </para> + + <itemizedlist> + <listitem> + <para> + MSG_MORE: If this flag is set, the send system call acts like a + cipher update function where more input data is expected + with a subsequent invocation of the send system call. + </para> + </listitem> + </itemizedlist> + + <para> + Note: The kernel reports -EINVAL for any unexpected data. The caller + must make sure that all data matches the constraints given in + /proc/crypto for the selected cipher. + </para> + + <para> + With the recv() system call, the application can read the result of + the cipher operation from the kernel crypto API. The output buffer + must be at least as large as to hold all blocks of the encrypted or + decrypted data. If the output data size is smaller, only as many + blocks are returned that fit into that output buffer size. + </para> + </sect1> + + <sect1><title>AEAD Cipher API</title> + <para> + The operation is very similar to the symmetric cipher discussion. + During initialization, the struct sockaddr data structure must be + filled as follows: + </para> + + <programlisting> +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "aead", /* this selects the symmetric cipher */ + .salg_name = "gcm(aes)" /* this is the cipher name */ +}; + </programlisting> + + <para> + Before data can be sent to the kernel using the write/send system + call family, the consumer must set the key. The key setting is + described with the setsockopt invocation below. + </para> + + <para> + In addition, before data can be sent to the kernel using the + write/send system call family, the consumer must set the authentication + tag size. To set the authentication tag size, the caller must use the + setsockopt invocation described below. + </para> + + <para> + Using the sendmsg() system call, the application provides the data that should be processed for encryption or decryption. In addition, the IV is + specified with the data structure provided by the sendmsg() system call. + </para> + + <para> + The sendmsg system call parameter of struct msghdr is embedded into the + struct cmsghdr data structure. See recv(2) and cmsg(3) for more + information on how the cmsghdr data structure is used together with the + send/recv system call family. That cmsghdr data structure holds the + following information specified with a separate header instances: + </para> + + <itemizedlist> + <listitem> + <para> + specification of the cipher operation type with one of these flags: + </para> + <itemizedlist> + <listitem> + <para>ALG_OP_ENCRYPT - encryption of data</para> + </listitem> + <listitem> + <para>ALG_OP_DECRYPT - decryption of data</para> + </listitem> + </itemizedlist> + </listitem> + + <listitem> + <para> + specification of the IV information marked with the flag ALG_SET_IV + </para> + </listitem> + + <listitem> + <para> + specification of the associated authentication data (AAD) with the + flag ALG_SET_AEAD_ASSOCLEN. The AAD is sent to the kernel together + with the plaintext / ciphertext. See below for the memory structure. + </para> + </listitem> + </itemizedlist> + + <para> + The send system call family allows the following flag to be specified: + </para> + + <itemizedlist> + <listitem> + <para> + MSG_MORE: If this flag is set, the send system call acts like a + cipher update function where more input data is expected + with a subsequent invocation of the send system call. + </para> + </listitem> + </itemizedlist> + + <para> + Note: The kernel reports -EINVAL for any unexpected data. The caller + must make sure that all data matches the constraints given in + /proc/crypto for the selected cipher. + </para> + + <para> + With the recv() system call, the application can read the result of + the cipher operation from the kernel crypto API. The output buffer + must be at least as large as defined with the memory structure below. + If the output data size is smaller, the cipher operation is not performed. + </para> + + <para> + The authenticated decryption operation may indicate an integrity error. + Such breach in integrity is marked with the -EBADMSG error code. + </para> + + <sect2><title>AEAD Memory Structure</title> + <para> + The AEAD cipher operates with the following information that + is communicated between user and kernel space as one data stream: + </para> + + <itemizedlist> + <listitem> + <para>plaintext or ciphertext</para> + </listitem> + + <listitem> + <para>associated authentication data (AAD)</para> + </listitem> + + <listitem> + <para>authentication tag</para> + </listitem> + </itemizedlist> + + <para> + The sizes of the AAD and the authentication tag are provided with + the sendmsg and setsockopt calls (see there). As the kernel knows + the size of the entire data stream, the kernel is now able to + calculate the right offsets of the data components in the data + stream. + </para> + + <para> + The user space caller must arrange the aforementioned information + in the following order: + </para> + + <itemizedlist> + <listitem> + <para> + AEAD encryption input: AAD || plaintext + </para> + </listitem> + + <listitem> + <para> + AEAD decryption input: AAD || ciphertext || authentication tag + </para> + </listitem> + </itemizedlist> + + <para> + The output buffer the user space caller provides must be at least as + large to hold the following data: + </para> + + <itemizedlist> + <listitem> + <para> + AEAD encryption output: ciphertext || authentication tag + </para> + </listitem> + + <listitem> + <para> + AEAD decryption output: plaintext + </para> + </listitem> + </itemizedlist> + </sect2> + </sect1> + + <sect1><title>Random Number Generator API</title> + <para> + Again, the operation is very similar to the other APIs. + During initialization, the struct sockaddr data structure must be + filled as follows: + </para> + + <programlisting> +struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "rng", /* this selects the symmetric cipher */ + .salg_name = "drbg_nopr_sha256" /* this is the cipher name */ +}; + </programlisting> + + <para> + Depending on the RNG type, the RNG must be seeded. The seed is provided + using the setsockopt interface to set the key. For example, the + ansi_cprng requires a seed. The DRBGs do not require a seed, but + may be seeded. + </para> + + <para> + Using the read()/recvmsg() system calls, random numbers can be obtained. + The kernel generates at most 128 bytes in one call. If user space + requires more data, multiple calls to read()/recvmsg() must be made. + </para> + + <para> + WARNING: The user space caller may invoke the initially mentioned + accept system call multiple times. In this case, the returned file + descriptors have the same state. + </para> + + </sect1> + + <sect1><title>Zero-Copy Interface</title> + <para> + In addition to the send/write/read/recv system call familty, the AF_ALG + interface can be accessed with the zero-copy interface of splice/vmsplice. + As the name indicates, the kernel tries to avoid a copy operation into + kernel space. + </para> + + <para> + The zero-copy operation requires data to be aligned at the page boundary. + Non-aligned data can be used as well, but may require more operations of + the kernel which would defeat the speed gains obtained from the zero-copy + interface. + </para> + + <para> + The system-interent limit for the size of one zero-copy operation is + 16 pages. If more data is to be sent to AF_ALG, user space must slice + the input into segments with a maximum size of 16 pages. + </para> + + <para> + Zero-copy can be used with the following code example (a complete working + example is provided with libkcapi): + </para> + + <programlisting> +int pipes[2]; + +pipe(pipes); +/* input data in iov */ +vmsplice(pipes[1], iov, iovlen, SPLICE_F_GIFT); +/* opfd is the file descriptor returned from accept() system call */ +splice(pipes[0], NULL, opfd, NULL, ret, 0); +read(opfd, out, outlen); + </programlisting> + + </sect1> + + <sect1><title>Setsockopt Interface</title> + <para> + In addition to the read/recv and send/write system call handling + to send and retrieve data subject to the cipher operation, a consumer + also needs to set the additional information for the cipher operation. + This additional information is set using the setsockopt system call + that must be invoked with the file descriptor of the open cipher + (i.e. the file descriptor returned by the accept system call). + </para> + + <para> + Each setsockopt invocation must use the level SOL_ALG. + </para> + + <para> + The setsockopt interface allows setting the following data using + the mentioned optname: + </para> + + <itemizedlist> + <listitem> + <para> + ALG_SET_KEY -- Setting the key. Key setting is applicable to: + </para> + <itemizedlist> + <listitem> + <para>the skcipher cipher type (symmetric ciphers)</para> + </listitem> + <listitem> + <para>the hash cipher type (keyed message digests)</para> + </listitem> + <listitem> + <para>the AEAD cipher type</para> + </listitem> + <listitem> + <para>the RNG cipher type to provide the seed</para> + </listitem> + </itemizedlist> + </listitem> + + <listitem> + <para> + ALG_SET_AEAD_AUTHSIZE -- Setting the authentication tag size + for AEAD ciphers. For a encryption operation, the authentication + tag of the given size will be generated. For a decryption operation, + the provided ciphertext is assumed to contain an authentication tag + of the given size (see section about AEAD memory layout below). + </para> + </listitem> + </itemizedlist> + + </sect1> + + <sect1><title>User space API example</title> + <para> + Please see [1] for libkcapi which provides an easy-to-use wrapper + around the aforementioned Netlink kernel interface. [1] also contains + a test application that invokes all libkcapi API calls. + </para> + + <para> + [1] http://www.chronox.de/libkcapi.html + </para> + + </sect1> + + </chapter> + <chapter id="API"><title>Programming Interface</title> <sect1><title>Block Cipher Context Data Structures</title> !Pinclude/linux/crypto.h Block Cipher Context Data Structures diff --git a/Documentation/crypto/crypto-API-userspace.txt b/Documentation/crypto/crypto-API-userspace.txt deleted file mode 100644 index ac619cd90300..000000000000 --- a/Documentation/crypto/crypto-API-userspace.txt +++ /dev/null @@ -1,205 +0,0 @@ -Introduction -============ - -The concepts of the kernel crypto API visible to kernel space is fully -applicable to the user space interface as well. Therefore, the kernel crypto API -high level discussion for the in-kernel use cases applies here as well. - -The major difference, however, is that user space can only act as a consumer -and never as a provider of a transformation or cipher algorithm. - -The following covers the user space interface exported by the kernel crypto -API. A working example of this description is libkcapi that can be obtained from -[1]. That library can be used by user space applications that require -cryptographic services from the kernel. - -Some details of the in-kernel kernel crypto API aspects do not -apply to user space, however. This includes the difference between synchronous -and asynchronous invocations. The user space API call is fully synchronous. -In addition, only a subset of all cipher types are available as documented -below. - - -User space API general remarks -============================== - -The kernel crypto API is accessible from user space. Currently, the following -ciphers are accessible: - - * Message digest including keyed message digest (HMAC, CMAC) - - * Symmetric ciphers - -Note, AEAD ciphers are currently not supported via the symmetric cipher -interface. - -The interface is provided via Netlink using the type AF_ALG. In addition, the -setsockopt option type is SOL_ALG. In case the user space header files do not -export these flags yet, use the following macros: - -#ifndef AF_ALG -#define AF_ALG 38 -#endif -#ifndef SOL_ALG -#define SOL_ALG 279 -#endif - -A cipher is accessed with the same name as done for the in-kernel API calls. -This includes the generic vs. unique naming schema for ciphers as well as the -enforcement of priorities for generic names. - -To interact with the kernel crypto API, a Netlink socket must be created by -the user space application. User space invokes the cipher operation with the -send/write system call family. The result of the cipher operation is obtained -with the read/recv system call family. - -The following API calls assume that the Netlink socket descriptor is already -opened by the user space application and discusses only the kernel crypto API -specific invocations. - -To initialize a Netlink interface, the following sequence has to be performed -by the consumer: - - 1. Create a socket of type AF_ALG with the struct sockaddr_alg parameter - specified below for the different cipher types. - - 2. Invoke bind with the socket descriptor - - 3. Invoke accept with the socket descriptor. The accept system call - returns a new file descriptor that is to be used to interact with - the particular cipher instance. When invoking send/write or recv/read - system calls to send data to the kernel or obtain data from the - kernel, the file descriptor returned by accept must be used. - -In-place cipher operation -========================= - -Just like the in-kernel operation of the kernel crypto API, the user space -interface allows the cipher operation in-place. That means that the input buffer -used for the send/write system call and the output buffer used by the read/recv -system call may be one and the same. This is of particular interest for -symmetric cipher operations where a copying of the output data to its final -destination can be avoided. - -If a consumer on the other hand wants to maintain the plaintext and the -ciphertext in different memory locations, all a consumer needs to do is to -provide different memory pointers for the encryption and decryption operation. - -Message digest API -================== - -The message digest type to be used for the cipher operation is selected when -invoking the bind syscall. bind requires the caller to provide a filled -struct sockaddr data structure. This data structure must be filled as follows: - -struct sockaddr_alg sa = { - .salg_family = AF_ALG, - .salg_type = "hash", /* this selects the hash logic in the kernel */ - .salg_name = "sha1" /* this is the cipher name */ -}; - -The salg_type value "hash" applies to message digests and keyed message digests. -Though, a keyed message digest is referenced by the appropriate salg_name. -Please see below for the setsockopt interface that explains how the key can be -set for a keyed message digest. - -Using the send() system call, the application provides the data that should be -processed with the message digest. The send system call allows the following -flags to be specified: - - * MSG_MORE: If this flag is set, the send system call acts like a - message digest update function where the final hash is not - yet calculated. If the flag is not set, the send system call - calculates the final message digest immediately. - -With the recv() system call, the application can read the message digest from -the kernel crypto API. If the buffer is too small for the message digest, the -flag MSG_TRUNC is set by the kernel. - -In order to set a message digest key, the calling application must use the -setsockopt() option of ALG_SET_KEY. If the key is not set the HMAC operation is -performed without the initial HMAC state change caused by the key. - - -Symmetric cipher API -==================== - -The operation is very similar to the message digest discussion. During -initialization, the struct sockaddr data structure must be filled as follows: - -struct sockaddr_alg sa = { - .salg_family = AF_ALG, - .salg_type = "skcipher", /* this selects the symmetric cipher */ - .salg_name = "cbc(aes)" /* this is the cipher name */ -}; - -Before data can be sent to the kernel using the write/send system call family, -the consumer must set the key. The key setting is described with the setsockopt -invocation below. - -Using the sendmsg() system call, the application provides the data that should -be processed for encryption or decryption. In addition, the IV is specified -with the data structure provided by the sendmsg() system call. - -The sendmsg system call parameter of struct msghdr is embedded into the -struct cmsghdr data structure. See recv(2) and cmsg(3) for more information -on how the cmsghdr data structure is used together with the send/recv system -call family. That cmsghdr data structure holds the following information -specified with a separate header instances: - - * specification of the cipher operation type with one of these flags: - ALG_OP_ENCRYPT - encryption of data - ALG_OP_DECRYPT - decryption of data - - * specification of the IV information marked with the flag ALG_SET_IV - -The send system call family allows the following flag to be specified: - - * MSG_MORE: If this flag is set, the send system call acts like a - cipher update function where more input data is expected - with a subsequent invocation of the send system call. - -Note: The kernel reports -EINVAL for any unexpected data. The caller must -make sure that all data matches the constraints given in /proc/crypto for the -selected cipher. - -With the recv() system call, the application can read the result of the -cipher operation from the kernel crypto API. The output buffer must be at least -as large as to hold all blocks of the encrypted or decrypted data. If the output -data size is smaller, only as many blocks are returned that fit into that -output buffer size. - -Setsockopt interface -==================== - -In addition to the read/recv and send/write system call handling to send and -retrieve data subject to the cipher operation, a consumer also needs to set -the additional information for the cipher operation. This additional information -is set using the setsockopt system call that must be invoked with the file -descriptor of the open cipher (i.e. the file descriptor returned by the -accept system call). - -Each setsockopt invocation must use the level SOL_ALG. - -The setsockopt interface allows setting the following data using the mentioned -optname: - - * ALG_SET_KEY -- Setting the key. Key setting is applicable to: - - - the skcipher cipher type (symmetric ciphers) - - - the hash cipher type (keyed message digests) - -User space API example -====================== - -Please see [1] for libkcapi which provides an easy-to-use wrapper around the -aforementioned Netlink kernel interface. [1] also contains a test application -that invokes all libkcapi API calls. - -[1] http://www.chronox.de/libkcapi.html - -Author -====== - -Stephan Mueller <smueller@chronox.de> diff --git a/Documentation/devicetree/bindings/crypto/img-hash.txt b/Documentation/devicetree/bindings/crypto/img-hash.txt new file mode 100644 index 000000000000..91a3d757d641 --- /dev/null +++ b/Documentation/devicetree/bindings/crypto/img-hash.txt @@ -0,0 +1,27 @@ +Imagination Technologies hardware hash accelerator + +The hash accelerator provides hardware hashing acceleration for +SHA1, SHA224, SHA256 and MD5 hashes + +Required properties: + +- compatible : "img,hash-accelerator" +- reg : Offset and length of the register set for the module, and the DMA port +- interrupts : The designated IRQ line for the hashing module. +- dmas : DMA specifier as per Documentation/devicetree/bindings/dma/dma.txt +- dma-names : Should be "tx" +- clocks : Clock specifiers +- clock-names : "sys" Used to clock the hash block registers + "hash" Used to clock data through the accelerator + +Example: + + hash: hash@18149600 { + compatible = "img,hash-accelerator"; + reg = <0x18149600 0x100>, <0x18101100 0x4>; + interrupts = <GIC_SHARED 59 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma 8 0xffffffff 0>; + dma-names = "tx"; + clocks = <&cr_periph SYS_CLK_HASH>, <&clk_periph PERIPH_CLK_ROM>; + clock-names = "sys", "hash"; + }; diff --git a/Documentation/devicetree/bindings/hwrng/brcm,iproc-rng200.txt b/Documentation/devicetree/bindings/hwrng/brcm,iproc-rng200.txt new file mode 100644 index 000000000000..e25a456664b9 --- /dev/null +++ b/Documentation/devicetree/bindings/hwrng/brcm,iproc-rng200.txt @@ -0,0 +1,12 @@ +HWRNG support for the iproc-rng200 driver + +Required properties: +- compatible : "brcm,iproc-rng200" +- reg : base address and size of control register block + +Example: + +rng { + compatible = "brcm,iproc-rng200"; + reg = <0x18032000 0x28>; +}; diff --git a/Documentation/devicetree/bindings/leds/common.txt b/Documentation/devicetree/bindings/leds/common.txt index 34811c57db69..747c53805eec 100644 --- a/Documentation/devicetree/bindings/leds/common.txt +++ b/Documentation/devicetree/bindings/leds/common.txt @@ -14,8 +14,10 @@ Optional properties for child nodes: - led-sources : List of device current outputs the LED is connected to. The outputs are identified by the numbers that must be defined in the LED device binding documentation. -- label : The label for this LED. If omitted, the label is - taken from the node name (excluding the unit address). +- label : The label for this LED. If omitted, the label is taken from the node + name (excluding the unit address). It has to uniquely identify + a device, i.e. no other LED class device can be assigned the same + label. - linux,default-trigger : This parameter, if present, is a string defining the trigger assigned to the LED. Current triggers are: diff --git a/Documentation/devicetree/bindings/leds/leds-gpio.txt b/Documentation/devicetree/bindings/leds/leds-gpio.txt index f77148f372ea..fea1ebfe24a9 100644 --- a/Documentation/devicetree/bindings/leds/leds-gpio.txt +++ b/Documentation/devicetree/bindings/leds/leds-gpio.txt @@ -26,16 +26,18 @@ LED sub-node properties: Examples: +#include <dt-bindings/gpio/gpio.h> + leds { compatible = "gpio-leds"; hdd { label = "IDE Activity"; - gpios = <&mcu_pio 0 1>; /* Active low */ + gpios = <&mcu_pio 0 GPIO_ACTIVE_LOW>; linux,default-trigger = "ide-disk"; }; fault { - gpios = <&mcu_pio 1 0>; + gpios = <&mcu_pio 1 GPIO_ACTIVE_HIGH>; /* Keep LED on if BIOS detected hardware fault */ default-state = "keep"; }; @@ -44,11 +46,11 @@ leds { run-control { compatible = "gpio-leds"; red { - gpios = <&mpc8572 6 0>; + gpios = <&mpc8572 6 GPIO_ACTIVE_HIGH>; default-state = "off"; }; green { - gpios = <&mpc8572 7 0>; + gpios = <&mpc8572 7 GPIO_ACTIVE_HIGH>; default-state = "on"; }; }; @@ -57,7 +59,7 @@ leds { compatible = "gpio-leds"; charger-led { - gpios = <&gpio1 2 0>; + gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; linux,default-trigger = "max8903-charger-charging"; retain-state-suspended; }; diff --git a/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt new file mode 100644 index 000000000000..a85a964d61f5 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/leds-pm8941-wled.txt @@ -0,0 +1,43 @@ +Binding for Qualcomm PM8941 WLED driver + +Required properties: +- compatible: should be "qcom,pm8941-wled" +- reg: slave address + +Optional properties: +- label: The label for this led + See Documentation/devicetree/bindings/leds/common.txt +- linux,default-trigger: Default trigger assigned to the LED + See Documentation/devicetree/bindings/leds/common.txt +- qcom,cs-out: bool; enable current sink output +- qcom,cabc: bool; enable content adaptive backlight control +- qcom,ext-gen: bool; use externally generated modulator signal to dim +- qcom,current-limit: mA; per-string current limit; value from 0 to 25 + default: 20mA +- qcom,current-boost-limit: mA; boost current limit; one of: + 105, 385, 525, 805, 980, 1260, 1400, 1680 + default: 805mA +- qcom,switching-freq: kHz; switching frequency; one of: + 600, 640, 685, 738, 800, 872, 960, 1066, 1200, 1371, + 1600, 1920, 2400, 3200, 4800, 9600, + default: 1600kHz +- qcom,ovp: V; Over-voltage protection limit; one of: + 27, 29, 32, 35 + default: 29V +- qcom,num-strings: #; number of led strings attached; value from 1 to 3 + default: 2 + +Example: + +pm8941-wled@d800 { + compatible = "qcom,pm8941-wled"; + reg = <0xd800>; + label = "backlight"; + + qcom,cs-out; + qcom,current-limit = <20>; + qcom,current-boost-limit = <805>; + qcom,switching-freq = <1600>; + qcom,ovp = <29>; + qcom,num-strings = <2>; +}; diff --git a/Documentation/devicetree/bindings/security/tpm/st33zp24-spi.txt b/Documentation/devicetree/bindings/security/tpm/st33zp24-spi.txt new file mode 100644 index 000000000000..158b0165e01c --- /dev/null +++ b/Documentation/devicetree/bindings/security/tpm/st33zp24-spi.txt @@ -0,0 +1,34 @@ +* STMicroelectronics SAS. ST33ZP24 TPM SoC + +Required properties: +- compatible: Should be "st,st33zp24-spi". +- spi-max-frequency: Maximum SPI frequency (<= 10000000). + +Optional ST33ZP24 Properties: +- interrupt-parent: phandle for the interrupt gpio controller +- interrupts: GPIO interrupt to which the chip is connected +- lpcpd-gpios: Output GPIO pin used for ST33ZP24 power management D1/D2 state. +If set, power must be present when the platform is going into sleep/hibernate mode. + +Optional SoC Specific Properties: +- pinctrl-names: Contains only one value - "default". +- pintctrl-0: Specifies the pin control groups used for this controller. + +Example (for ARM-based BeagleBoard xM with ST33ZP24 on SPI4): + +&mcspi4 { + + status = "okay"; + + st33zp24@0 { + + compatible = "st,st33zp24-spi"; + + spi-max-frequency = <10000000>; + + interrupt-parent = <&gpio5>; + interrupts = <7 IRQ_TYPE_LEVEL_HIGH>; + + lpcpd-gpios = <&gpio5 15 GPIO_ACTIVE_HIGH>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt index b41433386e2f..b623d50004fb 100644 --- a/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt +++ b/Documentation/devicetree/bindings/sound/ingenic,jz4740-i2s.txt @@ -1,7 +1,7 @@ Ingenic JZ4740 I2S controller Required properties: -- compatible : "ingenic,jz4740-i2s" +- compatible : "ingenic,jz4740-i2s" or "ingenic,jz4780-i2s" - reg : I2S registers location and length - clocks : AIC and I2S PLL clock specifiers. - clock-names: "aic" and "i2s" diff --git a/Documentation/devicetree/bindings/sound/max98925.txt b/Documentation/devicetree/bindings/sound/max98925.txt new file mode 100644 index 000000000000..27be63e2aa0d --- /dev/null +++ b/Documentation/devicetree/bindings/sound/max98925.txt @@ -0,0 +1,22 @@ +max98925 audio CODEC + +This device supports I2C. + +Required properties: + + - compatible : "maxim,max98925" + + - vmon-slot-no : slot number used to send voltage information + + - imon-slot-no : slot number used to send current information + + - reg : the I2C address of the device for I2C + +Example: + +codec: max98925@1a { + compatible = "maxim,max98925"; + vmon-slot-no = <0>; + imon-slot-no = <2>; + reg = <0x1a>; +}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt index c949abc2992f..c3495beba358 100644 --- a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-max98090.txt @@ -18,6 +18,7 @@ Required properties: * Headphones * Speakers * Mic Jack + * Int Mic - nvidia,i2s-controller : The phandle of the Tegra I2S controller that's connected to the CODEC. diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt new file mode 100644 index 000000000000..e00732dac939 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.txt @@ -0,0 +1,43 @@ +* Qualcomm Technologies LPASS CPU DAI + +This node models the Qualcomm Technologies Low-Power Audio SubSystem (LPASS). + +Required properties: + +- compatible : "qcom,lpass-cpu" +- clocks : Must contain an entry for each entry in clock-names. +- clock-names : A list which must include the following entries: + * "ahbix-clk" + * "mi2s-osr-clk" + * "mi2s-bit-clk" +- interrupts : Must contain an entry for each entry in + interrupt-names. +- interrupt-names : A list which must include the following entries: + * "lpass-irq-lpaif" +- pinctrl-N : One property must exist for each entry in + pinctrl-names. See ../pinctrl/pinctrl-bindings.txt + for details of the property values. +- pinctrl-names : Must contain a "default" entry. +- reg : Must contain an address for each entry in reg-names. +- reg-names : A list which must include the following entries: + * "lpass-lpaif" + +Optional properties: + +- qcom,adsp : Phandle for the audio DSP node + +Example: + +lpass@28100000 { + compatible = "qcom,lpass-cpu"; + clocks = <&lcc AHBIX_CLK>, <&lcc MI2S_OSR_CLK>, <&lcc MI2S_BIT_CLK>; + clock-names = "ahbix-clk", "mi2s-osr-clk", "mi2s-bit-clk"; + interrupts = <0 85 1>; + interrupt-names = "lpass-irq-lpaif"; + pinctrl-names = "default", "idle"; + pinctrl-0 = <&mi2s_default>; + pinctrl-1 = <&mi2s_idle>; + reg = <0x28100000 0x10000>; + reg-names = "lpass-lpaif"; + qcom,adsp = <&adsp>; +}; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt index 2dd690bc19cc..f316ce1f214a 100644 --- a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt +++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -29,9 +29,17 @@ SSI subnode properties: - shared-pin : if shared clock pin - pio-transfer : use PIO transfer mode - no-busif : BUSIF is not ussed when [mem -> SSI] via DMA case +- dma : Should contain Audio DMAC entry +- dma-names : SSI case "rx" (=playback), "tx" (=capture) + SSIU case "rxu" (=playback), "txu" (=capture) SRC subnode properties: -no properties at this point +- dma : Should contain Audio DMAC entry +- dma-names : "rx" (=playback), "tx" (=capture) + +DVC subnode properties: +- dma : Should contain Audio DMAC entry +- dma-names : "tx" (=playback/capture) DAI subnode properties: - playback : list of playback modules @@ -45,56 +53,145 @@ rcar_sound: rcar_sound@ec500000 { reg = <0 0xec500000 0 0x1000>, /* SCU */ <0 0xec5a0000 0 0x100>, /* ADG */ <0 0xec540000 0 0x1000>, /* SSIU */ - <0 0xec541000 0 0x1280>; /* SSI */ + <0 0xec541000 0 0x1280>, /* SSI */ + <0 0xec740000 0 0x200>; /* Audio DMAC peri peri*/ + reg-names = "scu", "adg", "ssiu", "ssi", "audmapp"; + + clocks = <&mstp10_clks R8A7790_CLK_SSI_ALL>, + <&mstp10_clks R8A7790_CLK_SSI9>, <&mstp10_clks R8A7790_CLK_SSI8>, + <&mstp10_clks R8A7790_CLK_SSI7>, <&mstp10_clks R8A7790_CLK_SSI6>, + <&mstp10_clks R8A7790_CLK_SSI5>, <&mstp10_clks R8A7790_CLK_SSI4>, + <&mstp10_clks R8A7790_CLK_SSI3>, <&mstp10_clks R8A7790_CLK_SSI2>, + <&mstp10_clks R8A7790_CLK_SSI1>, <&mstp10_clks R8A7790_CLK_SSI0>, + <&mstp10_clks R8A7790_CLK_SCU_SRC9>, <&mstp10_clks R8A7790_CLK_SCU_SRC8>, + <&mstp10_clks R8A7790_CLK_SCU_SRC7>, <&mstp10_clks R8A7790_CLK_SCU_SRC6>, + <&mstp10_clks R8A7790_CLK_SCU_SRC5>, <&mstp10_clks R8A7790_CLK_SCU_SRC4>, + <&mstp10_clks R8A7790_CLK_SCU_SRC3>, <&mstp10_clks R8A7790_CLK_SCU_SRC2>, + <&mstp10_clks R8A7790_CLK_SCU_SRC1>, <&mstp10_clks R8A7790_CLK_SCU_SRC0>, + <&mstp10_clks R8A7790_CLK_SCU_DVC0>, <&mstp10_clks R8A7790_CLK_SCU_DVC1>, + <&audio_clk_a>, <&audio_clk_b>, <&audio_clk_c>, <&m2_clk>; + clock-names = "ssi-all", + "ssi.9", "ssi.8", "ssi.7", "ssi.6", "ssi.5", + "ssi.4", "ssi.3", "ssi.2", "ssi.1", "ssi.0", + "src.9", "src.8", "src.7", "src.6", "src.5", + "src.4", "src.3", "src.2", "src.1", "src.0", + "dvc.0", "dvc.1", + "clk_a", "clk_b", "clk_c", "clk_i"; rcar_sound,dvc { - dvc0: dvc@0 { }; - dvc1: dvc@1 { }; + dvc0: dvc@0 { + dmas = <&audma0 0xbc>; + dma-names = "tx"; + }; + dvc1: dvc@1 { + dmas = <&audma0 0xbe>; + dma-names = "tx"; + }; }; rcar_sound,src { - src0: src@0 { }; - src1: src@1 { }; - src2: src@2 { }; - src3: src@3 { }; - src4: src@4 { }; - src5: src@5 { }; - src6: src@6 { }; - src7: src@7 { }; - src8: src@8 { }; - src9: src@9 { }; + src0: src@0 { + interrupts = <0 352 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x85>, <&audma1 0x9a>; + dma-names = "rx", "tx"; + }; + src1: src@1 { + interrupts = <0 353 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x87>, <&audma1 0x9c>; + dma-names = "rx", "tx"; + }; + src2: src@2 { + interrupts = <0 354 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x89>, <&audma1 0x9e>; + dma-names = "rx", "tx"; + }; + src3: src@3 { + interrupts = <0 355 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x8b>, <&audma1 0xa0>; + dma-names = "rx", "tx"; + }; + src4: src@4 { + interrupts = <0 356 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x8d>, <&audma1 0xb0>; + dma-names = "rx", "tx"; + }; + src5: src@5 { + interrupts = <0 357 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x8f>, <&audma1 0xb2>; + dma-names = "rx", "tx"; + }; + src6: src@6 { + interrupts = <0 358 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x91>, <&audma1 0xb4>; + dma-names = "rx", "tx"; + }; + src7: src@7 { + interrupts = <0 359 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x93>, <&audma1 0xb6>; + dma-names = "rx", "tx"; + }; + src8: src@8 { + interrupts = <0 360 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x95>, <&audma1 0xb8>; + dma-names = "rx", "tx"; + }; + src9: src@9 { + interrupts = <0 361 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x97>, <&audma1 0xba>; + dma-names = "rx", "tx"; + }; }; rcar_sound,ssi { ssi0: ssi@0 { interrupts = <0 370 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x01>, <&audma1 0x02>, <&audma0 0x15>, <&audma1 0x16>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi1: ssi@1 { interrupts = <0 371 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x03>, <&audma1 0x04>, <&audma0 0x49>, <&audma1 0x4a>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi2: ssi@2 { interrupts = <0 372 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x05>, <&audma1 0x06>, <&audma0 0x63>, <&audma1 0x64>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi3: ssi@3 { interrupts = <0 373 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x07>, <&audma1 0x08>, <&audma0 0x6f>, <&audma1 0x70>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi4: ssi@4 { interrupts = <0 374 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x09>, <&audma1 0x0a>, <&audma0 0x71>, <&audma1 0x72>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi5: ssi@5 { interrupts = <0 375 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x0b>, <&audma1 0x0c>, <&audma0 0x73>, <&audma1 0x74>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi6: ssi@6 { interrupts = <0 376 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x0d>, <&audma1 0x0e>, <&audma0 0x75>, <&audma1 0x76>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi7: ssi@7 { interrupts = <0 377 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x0f>, <&audma1 0x10>, <&audma0 0x79>, <&audma1 0x7a>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi8: ssi@8 { interrupts = <0 378 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x11>, <&audma1 0x12>, <&audma0 0x7b>, <&audma1 0x7c>; + dma-names = "rx", "tx", "rxu", "txu"; }; ssi9: ssi@9 { interrupts = <0 379 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&audma0 0x13>, <&audma1 0x14>, <&audma0 0x7d>, <&audma1 0x7e>; + dma-names = "rx", "tx", "rxu", "txu"; }; }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt new file mode 100644 index 000000000000..c64155027288 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt @@ -0,0 +1,67 @@ +Renesas Sampling Rate Convert Sound Card: + +Renesas Sampling Rate Convert Sound Card specifies audio DAI connections of SoC <-> codec. + +Required properties: + +- compatible : "renesas,rsrc-card,<board>" + Examples with soctypes are: + - "renesas,rsrc-card,lager" + - "renesas,rsrc-card,koelsch" +Optional properties: + +- card_name : User specified audio sound card name, one string + property. +- cpu : CPU sub-node +- codec : CODEC sub-node + +Optional subnode properties: + +- format : CPU/CODEC common audio format. + "i2s", "right_j", "left_j" , "dsp_a" + "dsp_b", "ac97", "pdm", "msb", "lsb" +- frame-master : Indicates dai-link frame master. + phandle to a cpu or codec subnode. +- bitclock-master : Indicates dai-link bit clock master. + phandle to a cpu or codec subnode. +- bitclock-inversion : bool property. Add this if the + dai-link uses bit clock inversion. +- frame-inversion : bool property. Add this if the + dai-link uses frame clock inversion. +- convert-rate : platform specified sampling rate convert + +Required CPU/CODEC subnodes properties: + +- sound-dai : phandle and port of CPU/CODEC + +Optional CPU/CODEC subnodes properties: + +- clocks / system-clock-frequency : specify subnode's clock if needed. + it can be specified via "clocks" if system has + clock node (= common clock), or "system-clock-frequency" + (if system doens't support common clock) + If a clock is specified, it is + enabled with clk_prepare_enable() + in dai startup() and disabled with + clk_disable_unprepare() in dai + shutdown(). + +Example + +sound { + compatible = "renesas,rsrc-card,lager"; + + card-name = "rsnd-ak4643"; + format = "left_j"; + bitclock-master = <&sndcodec>; + frame-master = <&sndcodec>; + + sndcpu: cpu { + sound-dai = <&rcar_sound>; + }; + + sndcodec: codec { + sound-dai = <&ak4643>; + system-clock-frequency = <11289600>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/storm.txt b/Documentation/devicetree/bindings/sound/storm.txt new file mode 100644 index 000000000000..062a4c185fa9 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/storm.txt @@ -0,0 +1,23 @@ +* Sound complex for Storm boards + +Models a soundcard for Storm boards with the Qualcomm Technologies IPQ806x SOC +connected to a MAX98357A DAC via I2S. + +Required properties: + +- compatible : "google,storm-audio" +- cpu : Phandle of the CPU DAI +- codec : Phandle of the codec DAI + +Optional properties: + +- qcom,model : The user-visible name of this sound card. + +Example: + +sound { + compatible = "google,storm-audio"; + qcom,model = "ipq806x-storm"; + cpu = <&lpass_cpu>; + codec = <&max98357a>; +}; diff --git a/Documentation/devicetree/bindings/sound/wm8804.txt b/Documentation/devicetree/bindings/sound/wm8804.txt index 4d3a56f38adc..6fd124b16496 100644 --- a/Documentation/devicetree/bindings/sound/wm8804.txt +++ b/Documentation/devicetree/bindings/sound/wm8804.txt @@ -10,6 +10,13 @@ Required properties: - reg : the I2C address of the device for I2C, the chip select number for SPI. + - PVDD-supply, DVDD-supply : Power supplies for the device, as covered + in Documentation/devicetree/bindings/regulator/regulator.txt + +Optional properties: + + - wlf,reset-gpio: A GPIO specifier for the GPIO controlling the reset pin + Example: codec: wm8804@1a { diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 6d1e8eeb5990..e1e2bbd7a404 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -289,6 +289,10 @@ IRQ devm_request_irq() devm_request_threaded_irq() +LED + devm_led_classdev_register() + devm_led_classdev_unregister() + MDIO devm_mdiobus_alloc() devm_mdiobus_alloc_size() diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking index 8bb8a7ee0f99..7c3f187d48bf 100644 --- a/Documentation/filesystems/Locking +++ b/Documentation/filesystems/Locking @@ -429,8 +429,6 @@ prototypes: loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); - ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); - ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting index fa2db081505e..e69274de8d0c 100644 --- a/Documentation/filesystems/porting +++ b/Documentation/filesystems/porting @@ -471,3 +471,15 @@ in your dentry operations instead. [mandatory] f_dentry is gone; use f_path.dentry, or, better yet, see if you can avoid it entirely. +-- +[mandatory] + never call ->read() and ->write() directly; use __vfs_{read,write} or + wrappers; instead of checking for ->write or ->read being NULL, look for + FMODE_CAN_{WRITE,READ} in file->f_mode. +-- +[mandatory] + do _not_ use new_sync_{read,write} for ->read/->write; leave it NULL + instead. +-- +[mandatory] + ->aio_read/->aio_write are gone. Use ->read_iter/->write_iter. diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt index 966b22829f3b..207cdca68bed 100644 --- a/Documentation/filesystems/vfs.txt +++ b/Documentation/filesystems/vfs.txt @@ -804,8 +804,6 @@ struct file_operations { loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); - ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); - ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); @@ -838,14 +836,10 @@ otherwise noted. read: called by read(2) and related system calls - aio_read: vectored, possibly asynchronous read - read_iter: possibly asynchronous read with iov_iter as destination write: called by write(2) and related system calls - aio_write: vectored, possibly asynchronous write - write_iter: possibly asynchronous write with iov_iter as source iterate: called when the VFS needs to read the directory contents diff --git a/Documentation/leds/leds-class-flash.txt b/Documentation/leds/leds-class-flash.txt new file mode 100644 index 000000000000..19bb67355424 --- /dev/null +++ b/Documentation/leds/leds-class-flash.txt @@ -0,0 +1,22 @@ + +Flash LED handling under Linux +============================== + +Some LED devices provide two modes - torch and flash. In the LED subsystem +those modes are supported by LED class (see Documentation/leds/leds-class.txt) +and LED Flash class respectively. The torch mode related features are enabled +by default and the flash ones only if a driver declares it by setting +LED_DEV_CAP_FLASH flag. + +In order to enable the support for flash LEDs CONFIG_LEDS_CLASS_FLASH symbol +must be defined in the kernel config. A LED Flash class driver must be +registered in the LED subsystem with led_classdev_flash_register function. + +Following sysfs attributes are exposed for controlling flash LED devices: +(see Documentation/ABI/testing/sysfs-class-led-flash) + - flash_brightness + - max_flash_brightness + - flash_timeout + - max_flash_timeout + - flash_strobe + - flash_fault diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index b6ef7e9dba30..abc82f85215b 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -33,11 +33,18 @@ The current git repository for Smack user space is: git://github.com/smack-team/smack.git This should make and install on most modern distributions. -There are three commands included in smackutil: +There are five commands included in smackutil: -smackload - properly formats data for writing to /smack/load -smackcipso - properly formats data for writing to /smack/cipso chsmack - display or set Smack extended attribute values +smackctl - load the Smack access rules +smackaccess - report if a process with one label has access + to an object with another + +These two commands are obsolete with the introduction of +the smackfs/load2 and smackfs/cipso2 interfaces. + +smackload - properly formats data for writing to smackfs/load +smackcipso - properly formats data for writing to smackfs/cipso In keeping with the intent of Smack, configuration data is minimal and not strictly required. The most important @@ -47,9 +54,9 @@ of this, but it can be manually as well. Add this line to /etc/fstab: - smackfs /smack smackfs smackfsdef=* 0 0 + smackfs /sys/fs/smackfs smackfs defaults 0 0 -and create the /smack directory for mounting. +The /sys/fs/smackfs directory is created by the kernel. Smack uses extended attributes (xattrs) to store labels on filesystem objects. The attributes are stored in the extended attribute security @@ -92,13 +99,13 @@ There are multiple ways to set a Smack label on a file: # attr -S -s SMACK64 -V "value" path # chsmack -a value path -A process can see the smack label it is running with by +A process can see the Smack label it is running with by reading /proc/self/attr/current. A process with CAP_MAC_ADMIN -can set the process smack by writing there. +can set the process Smack by writing there. Most Smack configuration is accomplished by writing to files -in the smackfs filesystem. This pseudo-filesystem is usually -mounted on /smack. +in the smackfs filesystem. This pseudo-filesystem is mounted +on /sys/fs/smackfs. access This interface reports whether a subject with the specified @@ -206,23 +213,30 @@ onlycap file or cleared by writing "-" to the file. ptrace This is used to define the current ptrace policy - 0 - default: this is the policy that relies on smack access rules. + 0 - default: this is the policy that relies on Smack access rules. For the PTRACE_READ a subject needs to have a read access on object. For the PTRACE_ATTACH a read-write access is required. 1 - exact: this is the policy that limits PTRACE_ATTACH. Attach is only allowed when subject's and object's labels are equal. - PTRACE_READ is not affected. Can be overriden with CAP_SYS_PTRACE. + PTRACE_READ is not affected. Can be overridden with CAP_SYS_PTRACE. 2 - draconian: this policy behaves like the 'exact' above with an - exception that it can't be overriden with CAP_SYS_PTRACE. + exception that it can't be overridden with CAP_SYS_PTRACE. revoke-subject Writing a Smack label here sets the access to '-' for all access rules with that subject label. +unconfined + If the kernel is configured with CONFIG_SECURITY_SMACK_BRINGUP + a process with CAP_MAC_ADMIN can write a label into this interface. + Thereafter, accesses that involve that label will be logged and + the access permitted if it wouldn't be otherwise. Note that this + is dangerous and can ruin the proper labeling of your system. + It should never be used in production. You can add access rules in /etc/smack/accesses. They take the form: subjectlabel objectlabel access -access is a combination of the letters rwxa which specify the +access is a combination of the letters rwxatb which specify the kind of access permitted a subject with subjectlabel on an object with objectlabel. If there is no rule no access is allowed. @@ -318,8 +332,9 @@ each of the subject and the object. Labels -Smack labels are ASCII character strings, one to twenty-three characters in -length. Single character labels using special characters, that being anything +Smack labels are ASCII character strings. They can be up to 255 characters +long, but keeping them to twenty-three characters is recommended. +Single character labels using special characters, that being anything other than a letter or digit, are reserved for use by the Smack development team. Smack labels are unstructured, case sensitive, and the only operation ever performed on them is comparison for equality. Smack labels cannot @@ -335,10 +350,9 @@ There are some predefined labels: ? Pronounced "huh", a single question mark character. @ Pronounced "web", a single at sign character. -Every task on a Smack system is assigned a label. System tasks, such as -init(8) and systems daemons, are run with the floor ("_") label. User tasks -are assigned labels according to the specification found in the -/etc/smack/user configuration file. +Every task on a Smack system is assigned a label. The Smack label +of a process will usually be assigned by the system initialization +mechanism. Access Rules @@ -393,6 +407,7 @@ describe access modes: w: indicates that write access should be granted. x: indicates that execute access should be granted. t: indicates that the rule requests transmutation. + b: indicates that the rule should be reported for bring-up. Uppercase values for the specification letters are allowed as well. Access mode specifications can be in any order. Examples of acceptable rules @@ -402,6 +417,7 @@ are: Secret Unclass R Manager Game x User HR w + Snap Crackle rwxatb New Old rRrRr Closed Off - @@ -413,7 +429,7 @@ Examples of unacceptable rules are: Spaces are not allowed in labels. Since a subject always has access to files with the same label specifying a rule for that case is pointless. Only -valid letters (rwxatRWXAT) and the dash ('-') character are allowed in +valid letters (rwxatbRWXATB) and the dash ('-') character are allowed in access specifications. The dash is a placeholder, so "a-r" is the same as "ar". A lone dash is used to specify that no access should be allowed. @@ -462,16 +478,11 @@ receiver. The receiver is not required to have read access to the sender. Setting Access Rules The configuration file /etc/smack/accesses contains the rules to be set at -system startup. The contents are written to the special file /smack/load. -Rules can be written to /smack/load at any time and take effect immediately. -For any pair of subject and object labels there can be only one rule, with the -most recently specified overriding any earlier specification. - -The program smackload is provided to ensure data is formatted -properly when written to /smack/load. This program reads lines -of the form - - subjectlabel objectlabel mode. +system startup. The contents are written to the special file +/sys/fs/smackfs/load2. Rules can be added at any time and take effect +immediately. For any pair of subject and object labels there can be only +one rule, with the most recently specified overriding any earlier +specification. Task Attribute @@ -488,7 +499,10 @@ only be changed by a process with privilege. Privilege -A process with CAP_MAC_OVERRIDE is privileged. +A process with CAP_MAC_OVERRIDE or CAP_MAC_ADMIN is privileged. +CAP_MAC_OVERRIDE allows the process access to objects it would +be denied otherwise. CAP_MAC_ADMIN allows a process to change +Smack data, including rules and attributes. Smack Networking @@ -510,14 +524,14 @@ intervention. Unlabeled packets that come into the system will be given the ambient label. Smack requires configuration in the case where packets from a system that is -not smack that speaks CIPSO may be encountered. Usually this will be a Trusted +not Smack that speaks CIPSO may be encountered. Usually this will be a Trusted Solaris system, but there are other, less widely deployed systems out there. CIPSO provides 3 important values, a Domain Of Interpretation (DOI), a level, and a category set with each packet. The DOI is intended to identify a group of systems that use compatible labeling schemes, and the DOI specified on the -smack system must match that of the remote system or packets will be -discarded. The DOI is 3 by default. The value can be read from /smack/doi and -can be changed by writing to /smack/doi. +Smack system must match that of the remote system or packets will be +discarded. The DOI is 3 by default. The value can be read from +/sys/fs/smackfs/doi and can be changed by writing to /sys/fs/smackfs/doi. The label and category set are mapped to a Smack label as defined in /etc/smack/cipso. @@ -539,15 +553,13 @@ The ":" and "," characters are permitted in a Smack label but have no special meaning. The mapping of Smack labels to CIPSO values is defined by writing to -/smack/cipso. Again, the format of data written to this special file -is highly restrictive, so the program smackcipso is provided to -ensure the writes are done properly. This program takes mappings -on the standard input and sends them to /smack/cipso properly. +/sys/fs/smackfs/cipso2. In addition to explicit mappings Smack supports direct CIPSO mappings. One CIPSO level is used to indicate that the category set passed in the packet is in fact an encoding of the Smack label. The level used is 250 by default. The -value can be read from /smack/direct and changed by writing to /smack/direct. +value can be read from /sys/fs/smackfs/direct and changed by writing to +/sys/fs/smackfs/direct. Socket Attributes @@ -565,8 +577,8 @@ sockets. Smack Netlabel Exceptions You will often find that your labeled application has to talk to the outside, -unlabeled world. To do this there's a special file /smack/netlabel where you can -add some exceptions in the form of : +unlabeled world. To do this there's a special file /sys/fs/smackfs/netlabel +where you can add some exceptions in the form of : @IP1 LABEL1 or @IP2/MASK LABEL2 @@ -574,22 +586,22 @@ It means that your application will have unlabeled access to @IP1 if it has write access on LABEL1, and access to the subnet @IP2/MASK if it has write access on LABEL2. -Entries in the /smack/netlabel file are matched by longest mask first, like in -classless IPv4 routing. +Entries in the /sys/fs/smackfs/netlabel file are matched by longest mask +first, like in classless IPv4 routing. A special label '@' and an option '-CIPSO' can be used there : @ means Internet, any application with any label has access to it -CIPSO means standard CIPSO networking If you don't know what CIPSO is and don't plan to use it, you can just do : -echo 127.0.0.1 -CIPSO > /smack/netlabel -echo 0.0.0.0/0 @ > /smack/netlabel +echo 127.0.0.1 -CIPSO > /sys/fs/smackfs/netlabel +echo 0.0.0.0/0 @ > /sys/fs/smackfs/netlabel If you use CIPSO on your 192.168.0.0/16 local network and need also unlabeled Internet access, you can have : -echo 127.0.0.1 -CIPSO > /smack/netlabel -echo 192.168.0.0/16 -CIPSO > /smack/netlabel -echo 0.0.0.0/0 @ > /smack/netlabel +echo 127.0.0.1 -CIPSO > /sys/fs/smackfs/netlabel +echo 192.168.0.0/16 -CIPSO > /sys/fs/smackfs/netlabel +echo 0.0.0.0/0 @ > /sys/fs/smackfs/netlabel Writing Applications for Smack @@ -676,7 +688,7 @@ Smack auditing If you want Smack auditing of security events, you need to set CONFIG_AUDIT in your kernel configuration. By default, all denied events will be audited. You can change this behavior by -writing a single character to the /smack/logging file : +writing a single character to the /sys/fs/smackfs/logging file : 0 : no logging 1 : log denied (default) 2 : log accepted @@ -686,3 +698,20 @@ Events are logged as 'key=value' pairs, for each event you at least will get the subject, the object, the rights requested, the action, the kernel function that triggered the event, plus other pairs depending on the type of event audited. + +Bringup Mode + +Bringup mode provides logging features that can make application +configuration and system bringup easier. Configure the kernel with +CONFIG_SECURITY_SMACK_BRINGUP to enable these features. When bringup +mode is enabled accesses that succeed due to rules marked with the "b" +access mode will logged. When a new label is introduced for processes +rules can be added aggressively, marked with the "b". The logging allows +tracking of which rules actual get used for that label. + +Another feature of bringup mode is the "unconfined" option. Writing +a label to /sys/fs/smackfs/unconfined makes subjects with that label +able to access any object, and objects with that label accessible to +all subjects. Any access that is granted because a label is unconfined +is logged. This feature is dangerous, as files and directories may +be created in places they couldn't if the policy were being enforced. diff --git a/Documentation/sound/alsa/ControlNames.txt b/Documentation/sound/alsa/ControlNames.txt index 79a6127863ca..3fc1cf50d28e 100644 --- a/Documentation/sound/alsa/ControlNames.txt +++ b/Documentation/sound/alsa/ControlNames.txt @@ -71,11 +71,11 @@ SOURCE: HDMI/DP (either HDMI or DisplayPort) Exceptions (deprecated): - [Digital] Capture Source - [Digital] Capture Switch (aka input gain switch) - [Digital] Capture Volume (aka input gain volume) - [Digital] Playback Switch (aka output gain switch) - [Digital] Playback Volume (aka output gain volume) + [Analogue|Digital] Capture Source + [Analogue|Digital] Capture Switch (aka input gain switch) + [Analogue|Digital] Capture Volume (aka input gain volume) + [Analogue|Digital] Playback Switch (aka output gain switch) + [Analogue|Digital] Playback Volume (aka output gain volume) Tone Control - Switch Tone Control - Bass Tone Control - Treble diff --git a/Documentation/sound/alsa/HD-Audio.txt b/Documentation/sound/alsa/HD-Audio.txt index 42a0a39b77e6..e7193aac669c 100644 --- a/Documentation/sound/alsa/HD-Audio.txt +++ b/Documentation/sound/alsa/HD-Audio.txt @@ -466,7 +466,11 @@ The generic parser supports the following hints: - add_jack_modes (bool): add "xxx Jack Mode" enum controls to each I/O jack for allowing to change the headphone amp and mic bias VREF capabilities -- power_down_unused (bool): power down the unused widgets +- power_save_node (bool): advanced power management for each widget, + controlling the power sate (D0/D3) of each widget node depending on + the actual pin and stream states +- power_down_unused (bool): power down the unused widgets, a subset of + power_save_node, and will be dropped in future - add_hp_mic (bool): add the headphone to capture source if possible - hp_mic_detect (bool): enable/disable the hp/mic shared input for a single built-in mic case; default true diff --git a/Documentation/sound/alsa/timestamping.txt b/Documentation/sound/alsa/timestamping.txt new file mode 100644 index 000000000000..0b191a23f534 --- /dev/null +++ b/Documentation/sound/alsa/timestamping.txt @@ -0,0 +1,200 @@ +The ALSA API can provide two different system timestamps: + +- Trigger_tstamp is the system time snapshot taken when the .trigger +callback is invoked. This snapshot is taken by the ALSA core in the +general case, but specific hardware may have synchronization +capabilities or conversely may only be able to provide a correct +estimate with a delay. In the latter two cases, the low-level driver +is responsible for updating the trigger_tstamp at the most appropriate +and precise moment. Applications should not rely solely on the first +trigger_tstamp but update their internal calculations if the driver +provides a refined estimate with a delay. + +- tstamp is the current system timestamp updated during the last +event or application query. +The difference (tstamp - trigger_tstamp) defines the elapsed time. + +The ALSA API provides reports two basic pieces of information, avail +and delay, which combined with the trigger and current system +timestamps allow for applications to keep track of the 'fullness' of +the ring buffer and the amount of queued samples. + +The use of these different pointers and time information depends on +the application needs: + +- 'avail' reports how much can be written in the ring buffer +- 'delay' reports the time it will take to hear a new sample after all +queued samples have been played out. + +When timestamps are enabled, the avail/delay information is reported +along with a snapshot of system time. Applications can select from +CLOCK_REALTIME (NTP corrections including going backwards), +CLOCK_MONOTONIC (NTP corrections but never going backwards), +CLOCK_MONOTIC_RAW (without NTP corrections) and change the mode +dynamically with sw_params + + +The ALSA API also provide an audio_tstamp which reflects the passage +of time as measured by different components of audio hardware. In +ascii-art, this could be represented as follows (for the playback +case): + + +--------------------------------------------------------------> time + ^ ^ ^ ^ ^ + | | | | | + analog link dma app FullBuffer + time time time time time + | | | | | + |< codec delay >|<--hw delay-->|<queued samples>|<---avail->| + |<----------------- delay---------------------->| | + |<----ring buffer length---->| + +The analog time is taken at the last stage of the playback, as close +as possible to the actual transducer + +The link time is taken at the output of the SOC/chipset as the samples +are pushed on a link. The link time can be directly measured if +supported in hardware by sample counters or wallclocks (e.g. with +HDAudio 24MHz or PTP clock for networked solutions) or indirectly +estimated (e.g. with the frame counter in USB). + +The DMA time is measured using counters - typically the least reliable +of all measurements due to the bursty natured of DMA transfers. + +The app time corresponds to the time tracked by an application after +writing in the ring buffer. + +The application can query what the hardware supports, define which +audio time it wants reported by selecting the relevant settings in +audio_tstamp_config fields, get an estimate of the timestamp +accuracy. It can also request the delay-to-analog be included in the +measurement. Direct access to the link time is very interesting on +platforms that provide an embedded DSP; measuring directly the link +time with dedicated hardware, possibly synchronized with system time, +removes the need to keep track of internal DSP processing times and +latency. + +In case the application requests an audio tstamp that is not supported +in hardware/low-level driver, the type is overridden as DEFAULT and the +timestamp will report the DMA time based on the hw_pointer value. + +For backwards compatibility with previous implementations that did not +provide timestamp selection, with a zero-valued COMPAT timestamp type +the results will default to the HDAudio wall clock for playback +streams and to the DMA time (hw_ptr) in all other cases. + +The audio timestamp accuracy can be returned to user-space, so that +appropriate decisions are made: + +- for dma time (default), the granularity of the transfers can be + inferred from the steps between updates and in turn provide + information on how much the application pointer can be rewound + safely. + +- the link time can be used to track long-term drifts between audio + and system time using the (tstamp-trigger_tstamp)/audio_tstamp + ratio, the precision helps define how much smoothing/low-pass + filtering is required. The link time can be either reset on startup + or reported as is (the latter being useful to compare progress of + different streams - but may require the wallclock to be always + running and not wrap-around during idle periods). If supported in + hardware, the absolute link time could also be used to define a + precise start time (patches WIP) + +- including the delay in the audio timestamp may + counter-intuitively not increase the precision of timestamps, e.g. if a + codec includes variable-latency DSP processing or a chain of + hardware components the delay is typically not known with precision. + +The accuracy is reported in nanosecond units (using an unsigned 32-bit +word), which gives a max precision of 4.29s, more than enough for +audio applications... + +Due to the varied nature of timestamping needs, even for a single +application, the audio_tstamp_config can be changed dynamically. In +the STATUS ioctl, the parameters are read-only and do not allow for +any application selection. To work around this limitation without +impacting legacy applications, a new STATUS_EXT ioctl is introduced +with read/write parameters. ALSA-lib will be modified to make use of +STATUS_EXT and effectively deprecate STATUS. + +The ALSA API only allows for a single audio timestamp to be reported +at a time. This is a conscious design decision, reading the audio +timestamps from hardware registers or from IPC takes time, the more +timestamps are read the more imprecise the combined measurements +are. To avoid any interpretation issues, a single (system, audio) +timestamp is reported. Applications that need different timestamps +will be required to issue multiple queries and perform an +interpolation of the results + +In some hardware-specific configuration, the system timestamp is +latched by a low-level audio subsytem, and the information provided +back to the driver. Due to potential delays in the communication with +the hardware, there is a risk of misalignment with the avail and delay +information. To make sure applications are not confused, a +driver_timestamp field is added in the snd_pcm_status structure; this +timestamp shows when the information is put together by the driver +before returning from the STATUS and STATUS_EXT ioctl. in most cases +this driver_timestamp will be identical to the regular system tstamp. + +Examples of typestamping with HDaudio: + +1. DMA timestamp, no compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 +playback: systime: 341121338 nsec, audio time 342000000 nsec, systime delta -878662 +playback: systime: 426236663 nsec, audio time 427187500 nsec, systime delta -950837 +playback: systime: 597080580 nsec, audio time 598000000 nsec, systime delta -919420 +playback: systime: 682059782 nsec, audio time 683020833 nsec, systime delta -961051 +playback: systime: 852896415 nsec, audio time 853854166 nsec, systime delta -957751 +playback: systime: 937903344 nsec, audio time 938854166 nsec, systime delta -950822 + +2. DMA timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=1 -d +playback: systime: 341053347 nsec, audio time 341062500 nsec, systime delta -9153 +playback: systime: 426072447 nsec, audio time 426062500 nsec, systime delta 9947 +playback: systime: 596899518 nsec, audio time 596895833 nsec, systime delta 3685 +playback: systime: 681915317 nsec, audio time 681916666 nsec, systime delta -1349 +playback: systime: 852741306 nsec, audio time 852750000 nsec, systime delta -8694 + +3. link timestamp, compensation for DMA+analog delay +$ ./audio_time -p --ts_type=2 -d +playback: systime: 341060004 nsec, audio time 341062791 nsec, systime delta -2787 +playback: systime: 426242074 nsec, audio time 426244875 nsec, systime delta -2801 +playback: systime: 597080992 nsec, audio time 597084583 nsec, systime delta -3591 +playback: systime: 682084512 nsec, audio time 682088291 nsec, systime delta -3779 +playback: systime: 852936229 nsec, audio time 852940916 nsec, systime delta -4687 +playback: systime: 938107562 nsec, audio time 938112708 nsec, systime delta -5146 + +Example 1 shows that the timestamp at the DMA level is close to 1ms +ahead of the actual playback time (as a side time this sort of +measurement can help define rewind safeguards). Compensating for the +DMA-link delay in example 2 helps remove the hardware buffering abut +the information is still very jittery, with up to one sample of +error. In example 3 where the timestamps are measured with the link +wallclock, the timestamps show a monotonic behavior and a lower +dispersion. + +Example 3 and 4 are with USB audio class. Example 3 shows a high +offset between audio time and system time due to buffering. Example 4 +shows how compensating for the delay exposes a 1ms accuracy (due to +the use of the frame counter by the driver) + +Example 3: DMA timestamp, no compensation for delay, delta of ~5ms +$ ./audio_time -p -Dhw:1 -t1 +playback: systime: 120174019 nsec, audio time 125000000 nsec, systime delta -4825981 +playback: systime: 245041136 nsec, audio time 250000000 nsec, systime delta -4958864 +playback: systime: 370106088 nsec, audio time 375000000 nsec, systime delta -4893912 +playback: systime: 495040065 nsec, audio time 500000000 nsec, systime delta -4959935 +playback: systime: 620038179 nsec, audio time 625000000 nsec, systime delta -4961821 +playback: systime: 745087741 nsec, audio time 750000000 nsec, systime delta -4912259 +playback: systime: 870037336 nsec, audio time 875000000 nsec, systime delta -4962664 + +Example 4: DMA timestamp, compensation for delay, delay of ~1ms +$ ./audio_time -p -Dhw:1 -t1 -d +playback: systime: 120190520 nsec, audio time 120000000 nsec, systime delta 190520 +playback: systime: 245036740 nsec, audio time 244000000 nsec, systime delta 1036740 +playback: systime: 370034081 nsec, audio time 369000000 nsec, systime delta 1034081 +playback: systime: 495159907 nsec, audio time 494000000 nsec, systime delta 1159907 +playback: systime: 620098824 nsec, audio time 619000000 nsec, systime delta 1098824 +playback: systime: 745031847 nsec, audio time 744000000 nsec, systime delta 1031847 @@ -13,8 +13,9 @@ define sed-y s:->::; p;}" endef -quiet_cmd_offsets = GEN $@ -define cmd_offsets +# Use filechk to avoid rebuilds when a header changes, but the resulting file +# does not +define filechk_offsets (set -e; \ echo "#ifndef $2"; \ echo "#define $2"; \ @@ -24,9 +25,9 @@ define cmd_offsets echo " * This file was generated by Kbuild"; \ echo " */"; \ echo ""; \ - sed -ne $(sed-y) $<; \ + sed -ne $(sed-y); \ echo ""; \ - echo "#endif" ) > $@ + echo "#endif" ) endef ##### @@ -35,16 +36,15 @@ endef bounds-file := include/generated/bounds.h always := $(bounds-file) -targets := $(bounds-file) kernel/bounds.s +targets := kernel/bounds.s # We use internal kbuild rules to avoid the "is up to date" message from make kernel/bounds.s: kernel/bounds.c FORCE $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c) -$(obj)/$(bounds-file): kernel/bounds.s Kbuild - $(Q)mkdir -p $(dir $@) - $(call cmd,offsets,__LINUX_BOUNDS_H__) +$(obj)/$(bounds-file): kernel/bounds.s FORCE + $(call filechk,offsets,__LINUX_BOUNDS_H__) ##### # 2) Generate asm-offsets.h @@ -53,7 +53,6 @@ $(obj)/$(bounds-file): kernel/bounds.s Kbuild offsets-file := include/generated/asm-offsets.h always += $(offsets-file) -targets += $(offsets-file) targets += arch/$(SRCARCH)/kernel/asm-offsets.s # We use internal kbuild rules to avoid the "is up to date" message from make @@ -62,8 +61,8 @@ arch/$(SRCARCH)/kernel/asm-offsets.s: arch/$(SRCARCH)/kernel/asm-offsets.c \ $(Q)mkdir -p $(dir $@) $(call if_changed_dep,cc_s_c) -$(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s Kbuild - $(call cmd,offsets,__ASM_OFFSETS_H__) +$(obj)/$(offsets-file): arch/$(SRCARCH)/kernel/asm-offsets.s FORCE + $(call filechk,offsets,__ASM_OFFSETS_H__) ##### # 3) Check for missing system calls diff --git a/MAINTAINERS b/MAINTAINERS index 61740322d465..56a432d51119 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1764,7 +1764,7 @@ S: Supported F: drivers/tty/serial/atmel_serial.c ATMEL Audio ALSA driver -M: Bo Shen <voice.shen@atmel.com> +M: Nicolas Ferre <nicolas.ferre@atmel.com> L: alsa-devel@alsa-project.org (moderated for non-subscribers) S: Supported F: sound/soc/atmel @@ -2823,6 +2823,7 @@ L: linux-crypto@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/herbert/crypto-2.6.git S: Maintained F: Documentation/crypto/ +F: Documentation/DocBook/crypto-API.tmpl F: arch/*/crypto/ F: crypto/ F: drivers/crypto/ @@ -5298,6 +5299,13 @@ F: drivers/char/ipmi/ F: include/linux/ipmi* F: include/uapi/linux/ipmi* +QCOM AUDIO (ASoC) DRIVERS +M: Patrick Lai <plai@codeaurora.org> +M: Banajit Goswami <bgoswami@codeaurora.org> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Supported +F: sound/soc/qcom/ + IPS SCSI RAID DRIVER M: Adaptec OEM Raid Solutions <aacraid@adaptec.com> L: linux-scsi@vger.kernel.org @@ -9965,6 +9973,7 @@ F: drivers/media/pci/tw68/ TPM DEVICE DRIVER M: Peter Huewe <peterhuewe@gmx.de> M: Marcel Selhorst <tpmdd@selhorst.net> +R: Jason Gunthorpe <jgunthorpe@obsidianresearch.com> W: http://tpmdd.sourceforge.net L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers) Q: git git://github.com/PeterHuewe/linux-tpmdd.git @@ -10,9 +10,10 @@ NAME = Hurr durr I'ma sheep # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. -# Do not use make's built-in rules and variables -# (this increases performance and avoids hard-to-debug behaviour); -MAKEFLAGS += -rR +# o Do not use make's built-in rules and variables +# (this increases performance and avoids hard-to-debug behaviour); +# o Look for make include files relative to root of kernel src +MAKEFLAGS += -rR --include-dir=$(CURDIR) # Avoid funny character set dependencies unexport LC_ALL @@ -344,12 +345,9 @@ endif export COMPILER endif -# Look for make include files relative to root of kernel src -MAKEFLAGS += --include-dir=$(srctree) - # We need some generic definitions (do not try to remake the file). -$(srctree)/scripts/Kbuild.include: ; -include $(srctree)/scripts/Kbuild.include +scripts/Kbuild.include: ; +include scripts/Kbuild.include # Make variables (CC, etc...) AS = $(CROSS_COMPILE)as @@ -533,7 +531,7 @@ ifeq ($(config-targets),1) # Read arch specific Makefile to set KBUILD_DEFCONFIG as needed. # KBUILD_DEFCONFIG may point out an alternative default configuration # used for 'make defconfig' -include $(srctree)/arch/$(SRCARCH)/Makefile +include arch/$(SRCARCH)/Makefile export KBUILD_DEFCONFIG KBUILD_KCONFIG config: scripts_basic outputmakefile FORCE @@ -609,7 +607,7 @@ endif # $(dot-config) # Defaults to vmlinux, but the arch makefile usually adds further targets all: vmlinux -include $(srctree)/arch/$(SRCARCH)/Makefile +include arch/$(SRCARCH)/Makefile KBUILD_CFLAGS += $(call cc-option,-fno-delete-null-pointer-checks,) @@ -782,8 +780,8 @@ ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-goto.sh $(CC)), y) KBUILD_AFLAGS += -DCC_HAVE_ASM_GOTO endif -include $(srctree)/scripts/Makefile.kasan -include $(srctree)/scripts/Makefile.extrawarn +include scripts/Makefile.kasan +include scripts/Makefile.extrawarn # Add user supplied CPPFLAGS, AFLAGS and CFLAGS as the last assignments KBUILD_CPPFLAGS += $(KCPPFLAGS) @@ -1027,12 +1025,6 @@ headerdep: $(srctree)/scripts/headerdep.pl -I$(srctree)/include # --------------------------------------------------------------------------- - -PHONY += depend dep -depend dep: - @echo '*** Warning: make $@ is unnecessary now.' - -# --------------------------------------------------------------------------- # Firmware install INSTALL_FW_PATH=$(INSTALL_MOD_PATH)/lib/firmware export INSTALL_FW_PATH diff --git a/arch/alpha/include/asm/thread_info.h b/arch/alpha/include/asm/thread_info.h index d5b98ab514bb..32e920a83ae5 100644 --- a/arch/alpha/include/asm/thread_info.h +++ b/arch/alpha/include/asm/thread_info.h @@ -18,7 +18,6 @@ struct thread_info { unsigned int flags; /* low level flags */ unsigned int ieee_state; /* see fpu.h */ - struct exec_domain *exec_domain; /* execution domain */ mm_segment_t addr_limit; /* thread address space */ unsigned cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -35,7 +34,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .addr_limit = KERNEL_DS, \ .preempt_count = INIT_PREEMPT_COUNT, \ } diff --git a/arch/arc/include/asm/thread_info.h b/arch/arc/include/asm/thread_info.h index 1163a1838ac1..aca0d5a45c7b 100644 --- a/arch/arc/include/asm/thread_info.h +++ b/arch/arc/include/asm/thread_info.h @@ -43,7 +43,6 @@ struct thread_info { int preempt_count; /* 0 => preemptable, <0 => BUG */ struct task_struct *task; /* main task structure */ mm_segment_t addr_limit; /* thread address space */ - struct exec_domain *exec_domain;/* execution domain */ __u32 cpu; /* current CPU */ unsigned long thr_ptr; /* TLS ptr */ }; @@ -56,7 +55,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/arc/kernel/signal.c b/arch/arc/kernel/signal.c index edda76fae83f..2251fb4bbfd7 100644 --- a/arch/arc/kernel/signal.c +++ b/arch/arc/kernel/signal.c @@ -171,18 +171,6 @@ static inline void __user *get_sigframe(struct ksignal *ksig, return frame; } -/* - * translate the signal - */ -static inline int map_sig(int sig) -{ - struct thread_info *thread = current_thread_info(); - if (thread->exec_domain && thread->exec_domain->signal_invmap - && sig < 32) - sig = thread->exec_domain->signal_invmap[sig]; - return sig; -} - static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { @@ -231,7 +219,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) return err; /* #1 arg to the user Signal handler */ - regs->r0 = map_sig(ksig->sig); + regs->r0 = ksig->sig; /* setup PC of user space signal handler */ regs->ret = (unsigned long)ksig->ka.sa.sa_handler; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index da1266c53c13..392e7ae69452 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2133,16 +2133,6 @@ menu "Userspace binary formats" source "fs/Kconfig.binfmt" -config ARTHUR - tristate "RISC OS personality" - depends on !AEABI - help - Say Y here to include the kernel code necessary if you want to run - Acorn RISC OS/Arthur binaries under Linux. This code is still very - experimental; if this sounds frightening, say N and sleep in peace. - You can also say M here to compile this support as a module (which - will be called arthur). - endmenu menu "Power management options" @@ -2175,6 +2165,9 @@ source "arch/arm/Kconfig.debug" source "security/Kconfig" source "crypto/Kconfig" +if CRYPTO +source "arch/arm/crypto/Kconfig" +endif source "lib/Kconfig" diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index ec2f8065f955..9eca7aee927f 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -12,7 +12,7 @@ # ifneq ($(MACHINE),) -include $(srctree)/$(MACHINE)/Makefile.boot +include $(MACHINE)/Makefile.boot endif # Note: the following conditions must always be true: diff --git a/arch/arm/configs/badge4_defconfig b/arch/arm/configs/badge4_defconfig index 0494c8f229a2..d59009878312 100644 --- a/arch/arm/configs/badge4_defconfig +++ b/arch/arm/configs/badge4_defconfig @@ -12,7 +12,6 @@ CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_FPE_NWFPE=y CONFIG_BINFMT_AOUT=m CONFIG_BINFMT_MISC=m -CONFIG_ARTHUR=m CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm/crypto/Kconfig b/arch/arm/crypto/Kconfig new file mode 100644 index 000000000000..8da2207b0072 --- /dev/null +++ b/arch/arm/crypto/Kconfig @@ -0,0 +1,130 @@ + +menuconfig ARM_CRYPTO + bool "ARM Accelerated Cryptographic Algorithms" + depends on ARM + help + Say Y here to choose from a selection of cryptographic algorithms + implemented using ARM specific CPU features or instructions. + +if ARM_CRYPTO + +config CRYPTO_SHA1_ARM + tristate "SHA1 digest algorithm (ARM-asm)" + select CRYPTO_SHA1 + select CRYPTO_HASH + help + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented + using optimized ARM assembler. + +config CRYPTO_SHA1_ARM_NEON + tristate "SHA1 digest algorithm (ARM NEON)" + depends on KERNEL_MODE_NEON + select CRYPTO_SHA1_ARM + select CRYPTO_SHA1 + select CRYPTO_HASH + help + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented + using optimized ARM NEON assembly, when NEON instructions are + available. + +config CRYPTO_SHA1_ARM_CE + tristate "SHA1 digest algorithm (ARM v8 Crypto Extensions)" + depends on KERNEL_MODE_NEON + select CRYPTO_SHA1_ARM + select CRYPTO_HASH + help + SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented + using special ARMv8 Crypto Extensions. + +config CRYPTO_SHA2_ARM_CE + tristate "SHA-224/256 digest algorithm (ARM v8 Crypto Extensions)" + depends on KERNEL_MODE_NEON + select CRYPTO_SHA256_ARM + select CRYPTO_HASH + help + SHA-256 secure hash standard (DFIPS 180-2) implemented + using special ARMv8 Crypto Extensions. + +config CRYPTO_SHA256_ARM + tristate "SHA-224/256 digest algorithm (ARM-asm and NEON)" + select CRYPTO_HASH + depends on !CPU_V7M + help + SHA-256 secure hash standard (DFIPS 180-2) implemented + using optimized ARM assembler and NEON, when available. + +config CRYPTO_SHA512_ARM_NEON + tristate "SHA384 and SHA512 digest algorithm (ARM NEON)" + depends on KERNEL_MODE_NEON + select CRYPTO_SHA512 + select CRYPTO_HASH + help + SHA-512 secure hash standard (DFIPS 180-2) implemented + using ARM NEON instructions, when available. + + This version of SHA implements a 512 bit hash with 256 bits of + security against collision attacks. + + This code also includes SHA-384, a 384 bit hash with 192 bits + of security against collision attacks. + +config CRYPTO_AES_ARM + tristate "AES cipher algorithms (ARM-asm)" + depends on ARM + select CRYPTO_ALGAPI + select CRYPTO_AES + help + Use optimized AES assembler routines for ARM platforms. + + 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_AES_ARM_BS + tristate "Bit sliced AES using NEON instructions" + depends on KERNEL_MODE_NEON + select CRYPTO_ALGAPI + select CRYPTO_AES_ARM + select CRYPTO_ABLK_HELPER + help + Use a faster and more secure NEON based implementation of AES in CBC, + CTR and XTS modes + + Bit sliced AES gives around 45% speedup on Cortex-A15 for CTR mode + and for XTS mode encryption, CBC and XTS mode decryption speedup is + around 25%. (CBC encryption speed is not affected by this driver.) + This implementation does not rely on any lookup tables so it is + believed to be invulnerable to cache timing attacks. + +config CRYPTO_AES_ARM_CE + tristate "Accelerated AES using ARMv8 Crypto Extensions" + depends on KERNEL_MODE_NEON + select CRYPTO_ALGAPI + select CRYPTO_ABLK_HELPER + help + Use an implementation of AES in CBC, CTR and XTS modes that uses + ARMv8 Crypto Extensions + +config CRYPTO_GHASH_ARM_CE + tristate "PMULL-accelerated GHASH using ARMv8 Crypto Extensions" + depends on KERNEL_MODE_NEON + select CRYPTO_HASH + select CRYPTO_CRYPTD + help + Use an implementation of GHASH (used by the GCM AEAD chaining mode) + that uses the 64x64 to 128 bit polynomial multiplication (vmull.p64) + that is part of the ARMv8 Crypto Extensions + +endif diff --git a/arch/arm/crypto/Makefile b/arch/arm/crypto/Makefile index b48fa341648d..6ea828241fcb 100644 --- a/arch/arm/crypto/Makefile +++ b/arch/arm/crypto/Makefile @@ -6,13 +6,35 @@ obj-$(CONFIG_CRYPTO_AES_ARM) += aes-arm.o obj-$(CONFIG_CRYPTO_AES_ARM_BS) += aes-arm-bs.o obj-$(CONFIG_CRYPTO_SHA1_ARM) += sha1-arm.o obj-$(CONFIG_CRYPTO_SHA1_ARM_NEON) += sha1-arm-neon.o +obj-$(CONFIG_CRYPTO_SHA256_ARM) += sha256-arm.o obj-$(CONFIG_CRYPTO_SHA512_ARM_NEON) += sha512-arm-neon.o +ce-obj-$(CONFIG_CRYPTO_AES_ARM_CE) += aes-arm-ce.o +ce-obj-$(CONFIG_CRYPTO_SHA1_ARM_CE) += sha1-arm-ce.o +ce-obj-$(CONFIG_CRYPTO_SHA2_ARM_CE) += sha2-arm-ce.o +ce-obj-$(CONFIG_CRYPTO_GHASH_ARM_CE) += ghash-arm-ce.o + +ifneq ($(ce-obj-y)$(ce-obj-m),) +ifeq ($(call as-instr,.fpu crypto-neon-fp-armv8,y,n),y) +obj-y += $(ce-obj-y) +obj-m += $(ce-obj-m) +else +$(warning These ARMv8 Crypto Extensions modules need binutils 2.23 or higher) +$(warning $(ce-obj-y) $(ce-obj-m)) +endif +endif + aes-arm-y := aes-armv4.o aes_glue.o aes-arm-bs-y := aesbs-core.o aesbs-glue.o sha1-arm-y := sha1-armv4-large.o sha1_glue.o sha1-arm-neon-y := sha1-armv7-neon.o sha1_neon_glue.o +sha256-arm-neon-$(CONFIG_KERNEL_MODE_NEON) := sha256_neon_glue.o +sha256-arm-y := sha256-core.o sha256_glue.o $(sha256-arm-neon-y) sha512-arm-neon-y := sha512-armv7-neon.o sha512_neon_glue.o +sha1-arm-ce-y := sha1-ce-core.o sha1-ce-glue.o +sha2-arm-ce-y := sha2-ce-core.o sha2-ce-glue.o +aes-arm-ce-y := aes-ce-core.o aes-ce-glue.o +ghash-arm-ce-y := ghash-ce-core.o ghash-ce-glue.o quiet_cmd_perl = PERL $@ cmd_perl = $(PERL) $(<) > $(@) @@ -20,4 +42,7 @@ quiet_cmd_perl = PERL $@ $(src)/aesbs-core.S_shipped: $(src)/bsaes-armv7.pl $(call cmd,perl) -.PRECIOUS: $(obj)/aesbs-core.S +$(src)/sha256-core.S_shipped: $(src)/sha256-armv4.pl + $(call cmd,perl) + +.PRECIOUS: $(obj)/aesbs-core.S $(obj)/sha256-core.S diff --git a/arch/arm/crypto/aes-ce-core.S b/arch/arm/crypto/aes-ce-core.S new file mode 100644 index 000000000000..8cfa468ee570 --- /dev/null +++ b/arch/arm/crypto/aes-ce-core.S @@ -0,0 +1,518 @@ +/* + * aes-ce-core.S - AES in CBC/CTR/XTS mode using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + + .text + .fpu crypto-neon-fp-armv8 + .align 3 + + .macro enc_round, state, key + aese.8 \state, \key + aesmc.8 \state, \state + .endm + + .macro dec_round, state, key + aesd.8 \state, \key + aesimc.8 \state, \state + .endm + + .macro enc_dround, key1, key2 + enc_round q0, \key1 + enc_round q0, \key2 + .endm + + .macro dec_dround, key1, key2 + dec_round q0, \key1 + dec_round q0, \key2 + .endm + + .macro enc_fround, key1, key2, key3 + enc_round q0, \key1 + aese.8 q0, \key2 + veor q0, q0, \key3 + .endm + + .macro dec_fround, key1, key2, key3 + dec_round q0, \key1 + aesd.8 q0, \key2 + veor q0, q0, \key3 + .endm + + .macro enc_dround_3x, key1, key2 + enc_round q0, \key1 + enc_round q1, \key1 + enc_round q2, \key1 + enc_round q0, \key2 + enc_round q1, \key2 + enc_round q2, \key2 + .endm + + .macro dec_dround_3x, key1, key2 + dec_round q0, \key1 + dec_round q1, \key1 + dec_round q2, \key1 + dec_round q0, \key2 + dec_round q1, \key2 + dec_round q2, \key2 + .endm + + .macro enc_fround_3x, key1, key2, key3 + enc_round q0, \key1 + enc_round q1, \key1 + enc_round q2, \key1 + aese.8 q0, \key2 + aese.8 q1, \key2 + aese.8 q2, \key2 + veor q0, q0, \key3 + veor q1, q1, \key3 + veor q2, q2, \key3 + .endm + + .macro dec_fround_3x, key1, key2, key3 + dec_round q0, \key1 + dec_round q1, \key1 + dec_round q2, \key1 + aesd.8 q0, \key2 + aesd.8 q1, \key2 + aesd.8 q2, \key2 + veor q0, q0, \key3 + veor q1, q1, \key3 + veor q2, q2, \key3 + .endm + + .macro do_block, dround, fround + cmp r3, #12 @ which key size? + vld1.8 {q10-q11}, [ip]! + \dround q8, q9 + vld1.8 {q12-q13}, [ip]! + \dround q10, q11 + vld1.8 {q10-q11}, [ip]! + \dround q12, q13 + vld1.8 {q12-q13}, [ip]! + \dround q10, q11 + blo 0f @ AES-128: 10 rounds + vld1.8 {q10-q11}, [ip]! + beq 1f @ AES-192: 12 rounds + \dround q12, q13 + vld1.8 {q12-q13}, [ip] + \dround q10, q11 +0: \fround q12, q13, q14 + bx lr + +1: \dround q12, q13 + \fround q10, q11, q14 + bx lr + .endm + + /* + * Internal, non-AAPCS compliant functions that implement the core AES + * transforms. These should preserve all registers except q0 - q2 and ip + * Arguments: + * q0 : first in/output block + * q1 : second in/output block (_3x version only) + * q2 : third in/output block (_3x version only) + * q8 : first round key + * q9 : secound round key + * ip : address of 3rd round key + * q14 : final round key + * r3 : number of rounds + */ + .align 6 +aes_encrypt: + add ip, r2, #32 @ 3rd round key +.Laes_encrypt_tweak: + do_block enc_dround, enc_fround +ENDPROC(aes_encrypt) + + .align 6 +aes_decrypt: + add ip, r2, #32 @ 3rd round key + do_block dec_dround, dec_fround +ENDPROC(aes_decrypt) + + .align 6 +aes_encrypt_3x: + add ip, r2, #32 @ 3rd round key + do_block enc_dround_3x, enc_fround_3x +ENDPROC(aes_encrypt_3x) + + .align 6 +aes_decrypt_3x: + add ip, r2, #32 @ 3rd round key + do_block dec_dround_3x, dec_fround_3x +ENDPROC(aes_decrypt_3x) + + .macro prepare_key, rk, rounds + add ip, \rk, \rounds, lsl #4 + vld1.8 {q8-q9}, [\rk] @ load first 2 round keys + vld1.8 {q14}, [ip] @ load last round key + .endm + + /* + * aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks) + * aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks) + */ +ENTRY(ce_aes_ecb_encrypt) + push {r4, lr} + ldr r4, [sp, #8] + prepare_key r2, r3 +.Lecbencloop3x: + subs r4, r4, #3 + bmi .Lecbenc1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + bl aes_encrypt_3x + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lecbencloop3x +.Lecbenc1x: + adds r4, r4, #3 + beq .Lecbencout +.Lecbencloop: + vld1.8 {q0}, [r1, :64]! + bl aes_encrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lecbencloop +.Lecbencout: + pop {r4, pc} +ENDPROC(ce_aes_ecb_encrypt) + +ENTRY(ce_aes_ecb_decrypt) + push {r4, lr} + ldr r4, [sp, #8] + prepare_key r2, r3 +.Lecbdecloop3x: + subs r4, r4, #3 + bmi .Lecbdec1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + bl aes_decrypt_3x + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lecbdecloop3x +.Lecbdec1x: + adds r4, r4, #3 + beq .Lecbdecout +.Lecbdecloop: + vld1.8 {q0}, [r1, :64]! + bl aes_decrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lecbdecloop +.Lecbdecout: + pop {r4, pc} +ENDPROC(ce_aes_ecb_decrypt) + + /* + * aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 iv[]) + * aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 iv[]) + */ +ENTRY(ce_aes_cbc_encrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q0}, [r5] + prepare_key r2, r3 +.Lcbcencloop: + vld1.8 {q1}, [r1, :64]! @ get next pt block + veor q0, q0, q1 @ ..and xor with iv + bl aes_encrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lcbcencloop + vst1.8 {q0}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_cbc_encrypt) + +ENTRY(ce_aes_cbc_decrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q6}, [r5] @ keep iv in q6 + prepare_key r2, r3 +.Lcbcdecloop3x: + subs r4, r4, #3 + bmi .Lcbcdec1x + vld1.8 {q0-q1}, [r1, :64]! + vld1.8 {q2}, [r1, :64]! + vmov q3, q0 + vmov q4, q1 + vmov q5, q2 + bl aes_decrypt_3x + veor q0, q0, q6 + veor q1, q1, q3 + veor q2, q2, q4 + vmov q6, q5 + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + b .Lcbcdecloop3x +.Lcbcdec1x: + adds r4, r4, #3 + beq .Lcbcdecout + vmov q15, q14 @ preserve last round key +.Lcbcdecloop: + vld1.8 {q0}, [r1, :64]! @ get next ct block + veor q14, q15, q6 @ combine prev ct with last key + vmov q6, q0 + bl aes_decrypt + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + bne .Lcbcdecloop +.Lcbcdecout: + vst1.8 {q6}, [r5] @ keep iv in q6 + pop {r4-r6, pc} +ENDPROC(ce_aes_cbc_decrypt) + + /* + * aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], int rounds, + * int blocks, u8 ctr[]) + */ +ENTRY(ce_aes_ctr_encrypt) + push {r4-r6, lr} + ldrd r4, r5, [sp, #16] + vld1.8 {q6}, [r5] @ load ctr + prepare_key r2, r3 + vmov r6, s27 @ keep swabbed ctr in r6 + rev r6, r6 + cmn r6, r4 @ 32 bit overflow? + bcs .Lctrloop +.Lctrloop3x: + subs r4, r4, #3 + bmi .Lctr1x + add r6, r6, #1 + vmov q0, q6 + vmov q1, q6 + rev ip, r6 + add r6, r6, #1 + vmov q2, q6 + vmov s7, ip + rev ip, r6 + add r6, r6, #1 + vmov s11, ip + vld1.8 {q3-q4}, [r1, :64]! + vld1.8 {q5}, [r1, :64]! + bl aes_encrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + rev ip, r6 + vst1.8 {q0-q1}, [r0, :64]! + vst1.8 {q2}, [r0, :64]! + vmov s27, ip + b .Lctrloop3x +.Lctr1x: + adds r4, r4, #3 + beq .Lctrout +.Lctrloop: + vmov q0, q6 + bl aes_encrypt + subs r4, r4, #1 + bmi .Lctrhalfblock @ blocks < 0 means 1/2 block + vld1.8 {q3}, [r1, :64]! + veor q3, q0, q3 + vst1.8 {q3}, [r0, :64]! + + adds r6, r6, #1 @ increment BE ctr + rev ip, r6 + vmov s27, ip + bcs .Lctrcarry + teq r4, #0 + bne .Lctrloop +.Lctrout: + vst1.8 {q6}, [r5] + pop {r4-r6, pc} + +.Lctrhalfblock: + vld1.8 {d1}, [r1, :64] + veor d0, d0, d1 + vst1.8 {d0}, [r0, :64] + pop {r4-r6, pc} + +.Lctrcarry: + .irp sreg, s26, s25, s24 + vmov ip, \sreg @ load next word of ctr + rev ip, ip @ ... to handle the carry + adds ip, ip, #1 + rev ip, ip + vmov \sreg, ip + bcc 0f + .endr +0: teq r4, #0 + beq .Lctrout + b .Lctrloop +ENDPROC(ce_aes_ctr_encrypt) + + /* + * aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, + * int blocks, u8 iv[], u8 const rk2[], int first) + * aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], int rounds, + * int blocks, u8 iv[], u8 const rk2[], int first) + */ + + .macro next_tweak, out, in, const, tmp + vshr.s64 \tmp, \in, #63 + vand \tmp, \tmp, \const + vadd.u64 \out, \in, \in + vext.8 \tmp, \tmp, \tmp, #8 + veor \out, \out, \tmp + .endm + + .align 3 +.Lxts_mul_x: + .quad 1, 0x87 + +ce_aes_xts_init: + vldr d14, .Lxts_mul_x + vldr d15, .Lxts_mul_x + 8 + + ldrd r4, r5, [sp, #16] @ load args + ldr r6, [sp, #28] + vld1.8 {q0}, [r5] @ load iv + teq r6, #1 @ start of a block? + bxne lr + + @ Encrypt the IV in q0 with the second AES key. This should only + @ be done at the start of a block. + ldr r6, [sp, #24] @ load AES key 2 + prepare_key r6, r3 + add ip, r6, #32 @ 3rd round key of key 2 + b .Laes_encrypt_tweak @ tail call +ENDPROC(ce_aes_xts_init) + +ENTRY(ce_aes_xts_encrypt) + push {r4-r6, lr} + + bl ce_aes_xts_init @ run shared prologue + prepare_key r2, r3 + vmov q3, q0 + + teq r6, #0 @ start of a block? + bne .Lxtsenc3x + +.Lxtsencloop3x: + next_tweak q3, q3, q7, q6 +.Lxtsenc3x: + subs r4, r4, #3 + bmi .Lxtsenc1x + vld1.8 {q0-q1}, [r1, :64]! @ get 3 pt blocks + vld1.8 {q2}, [r1, :64]! + next_tweak q4, q3, q7, q6 + veor q0, q0, q3 + next_tweak q5, q4, q7, q6 + veor q1, q1, q4 + veor q2, q2, q5 + bl aes_encrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + vst1.8 {q0-q1}, [r0, :64]! @ write 3 ct blocks + vst1.8 {q2}, [r0, :64]! + vmov q3, q5 + teq r4, #0 + beq .Lxtsencout + b .Lxtsencloop3x +.Lxtsenc1x: + adds r4, r4, #3 + beq .Lxtsencout +.Lxtsencloop: + vld1.8 {q0}, [r1, :64]! + veor q0, q0, q3 + bl aes_encrypt + veor q0, q0, q3 + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + beq .Lxtsencout + next_tweak q3, q3, q7, q6 + b .Lxtsencloop +.Lxtsencout: + vst1.8 {q3}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_xts_encrypt) + + +ENTRY(ce_aes_xts_decrypt) + push {r4-r6, lr} + + bl ce_aes_xts_init @ run shared prologue + prepare_key r2, r3 + vmov q3, q0 + + teq r6, #0 @ start of a block? + bne .Lxtsdec3x + +.Lxtsdecloop3x: + next_tweak q3, q3, q7, q6 +.Lxtsdec3x: + subs r4, r4, #3 + bmi .Lxtsdec1x + vld1.8 {q0-q1}, [r1, :64]! @ get 3 ct blocks + vld1.8 {q2}, [r1, :64]! + next_tweak q4, q3, q7, q6 + veor q0, q0, q3 + next_tweak q5, q4, q7, q6 + veor q1, q1, q4 + veor q2, q2, q5 + bl aes_decrypt_3x + veor q0, q0, q3 + veor q1, q1, q4 + veor q2, q2, q5 + vst1.8 {q0-q1}, [r0, :64]! @ write 3 pt blocks + vst1.8 {q2}, [r0, :64]! + vmov q3, q5 + teq r4, #0 + beq .Lxtsdecout + b .Lxtsdecloop3x +.Lxtsdec1x: + adds r4, r4, #3 + beq .Lxtsdecout +.Lxtsdecloop: + vld1.8 {q0}, [r1, :64]! + veor q0, q0, q3 + add ip, r2, #32 @ 3rd round key + bl aes_decrypt + veor q0, q0, q3 + vst1.8 {q0}, [r0, :64]! + subs r4, r4, #1 + beq .Lxtsdecout + next_tweak q3, q3, q7, q6 + b .Lxtsdecloop +.Lxtsdecout: + vst1.8 {q3}, [r5] + pop {r4-r6, pc} +ENDPROC(ce_aes_xts_decrypt) + + /* + * u32 ce_aes_sub(u32 input) - use the aese instruction to perform the + * AES sbox substitution on each byte in + * 'input' + */ +ENTRY(ce_aes_sub) + vdup.32 q1, r0 + veor q0, q0, q0 + aese.8 q0, q1 + vmov r0, s0 + bx lr +ENDPROC(ce_aes_sub) + + /* + * void ce_aes_invert(u8 *dst, u8 *src) - perform the Inverse MixColumns + * operation on round key *src + */ +ENTRY(ce_aes_invert) + vld1.8 {q0}, [r1] + aesimc.8 q0, q0 + vst1.8 {q0}, [r0] + bx lr +ENDPROC(ce_aes_invert) diff --git a/arch/arm/crypto/aes-ce-glue.c b/arch/arm/crypto/aes-ce-glue.c new file mode 100644 index 000000000000..b445a5d56f43 --- /dev/null +++ b/arch/arm/crypto/aes-ce-glue.c @@ -0,0 +1,524 @@ +/* + * aes-ce-glue.c - wrapper code for ARMv8 AES + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <asm/hwcap.h> +#include <asm/neon.h> +#include <asm/hwcap.h> +#include <crypto/aes.h> +#include <crypto/ablk_helper.h> +#include <crypto/algapi.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +/* defined in aes-ce-core.S */ +asmlinkage u32 ce_aes_sub(u32 input); +asmlinkage void ce_aes_invert(void *dst, void *src); + +asmlinkage void ce_aes_ecb_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks); +asmlinkage void ce_aes_ecb_decrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks); + +asmlinkage void ce_aes_cbc_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 iv[]); +asmlinkage void ce_aes_cbc_decrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 iv[]); + +asmlinkage void ce_aes_ctr_encrypt(u8 out[], u8 const in[], u8 const rk[], + int rounds, int blocks, u8 ctr[]); + +asmlinkage void ce_aes_xts_encrypt(u8 out[], u8 const in[], u8 const rk1[], + int rounds, int blocks, u8 iv[], + u8 const rk2[], int first); +asmlinkage void ce_aes_xts_decrypt(u8 out[], u8 const in[], u8 const rk1[], + int rounds, int blocks, u8 iv[], + u8 const rk2[], int first); + +struct aes_block { + u8 b[AES_BLOCK_SIZE]; +}; + +static int num_rounds(struct crypto_aes_ctx *ctx) +{ + /* + * # of rounds specified by AES: + * 128 bit key 10 rounds + * 192 bit key 12 rounds + * 256 bit key 14 rounds + * => n byte key => 6 + (n/4) rounds + */ + return 6 + ctx->key_length / 4; +} + +static int ce_aes_expandkey(struct crypto_aes_ctx *ctx, const u8 *in_key, + unsigned int key_len) +{ + /* + * The AES key schedule round constants + */ + static u8 const rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, + }; + + u32 kwords = key_len / sizeof(u32); + struct aes_block *key_enc, *key_dec; + int i, j; + + if (key_len != AES_KEYSIZE_128 && + key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) + return -EINVAL; + + memcpy(ctx->key_enc, in_key, key_len); + ctx->key_length = key_len; + + kernel_neon_begin(); + for (i = 0; i < sizeof(rcon); i++) { + u32 *rki = ctx->key_enc + (i * kwords); + u32 *rko = rki + kwords; + + rko[0] = ror32(ce_aes_sub(rki[kwords - 1]), 8); + rko[0] = rko[0] ^ rki[0] ^ rcon[i]; + rko[1] = rko[0] ^ rki[1]; + rko[2] = rko[1] ^ rki[2]; + rko[3] = rko[2] ^ rki[3]; + + if (key_len == AES_KEYSIZE_192) { + if (i >= 7) + break; + rko[4] = rko[3] ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + } else if (key_len == AES_KEYSIZE_256) { + if (i >= 6) + break; + rko[4] = ce_aes_sub(rko[3]) ^ rki[4]; + rko[5] = rko[4] ^ rki[5]; + rko[6] = rko[5] ^ rki[6]; + rko[7] = rko[6] ^ rki[7]; + } + } + + /* + * Generate the decryption keys for the Equivalent Inverse Cipher. + * This involves reversing the order of the round keys, and applying + * the Inverse Mix Columns transformation on all but the first and + * the last one. + */ + key_enc = (struct aes_block *)ctx->key_enc; + key_dec = (struct aes_block *)ctx->key_dec; + j = num_rounds(ctx); + + key_dec[0] = key_enc[j]; + for (i = 1, j--; j > 0; i++, j--) + ce_aes_invert(key_dec + i, key_enc + j); + key_dec[i] = key_enc[0]; + + kernel_neon_end(); + return 0; +} + +static int ce_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct crypto_aes_ctx *ctx = crypto_tfm_ctx(tfm); + int ret; + + ret = ce_aes_expandkey(ctx, in_key, key_len); + if (!ret) + return 0; + + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; +} + +struct crypto_aes_xts_ctx { + struct crypto_aes_ctx key1; + struct crypto_aes_ctx __aligned(8) key2; +}; + +static int xts_set_key(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct crypto_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm); + int ret; + + ret = ce_aes_expandkey(&ctx->key1, in_key, key_len / 2); + if (!ret) + ret = ce_aes_expandkey(&ctx->key2, &in_key[key_len / 2], + key_len / 2); + if (!ret) + return 0; + + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; +} + +static int ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ecb_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_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 = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ecb_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_dec, num_rounds(ctx), blocks); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_cbc_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks, + walk.iv); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_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 = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int blocks; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_cbc_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_dec, num_rounds(ctx), blocks, + walk.iv); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + return err; +} + +static int ctr_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + int err, blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + + kernel_neon_begin(); + while ((blocks = (walk.nbytes / AES_BLOCK_SIZE))) { + ce_aes_ctr_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key_enc, num_rounds(ctx), blocks, + walk.iv); + nbytes -= blocks * AES_BLOCK_SIZE; + if (nbytes && nbytes == walk.nbytes % AES_BLOCK_SIZE) + break; + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + if (nbytes) { + u8 *tdst = walk.dst.virt.addr + blocks * AES_BLOCK_SIZE; + u8 *tsrc = walk.src.virt.addr + blocks * AES_BLOCK_SIZE; + u8 __aligned(8) tail[AES_BLOCK_SIZE]; + + /* + * Minimum alignment is 8 bytes, so if nbytes is <= 8, we need + * to tell aes_ctr_encrypt() to only read half a block. + */ + blocks = (nbytes <= 8) ? -1 : 1; + + ce_aes_ctr_encrypt(tail, tsrc, (u8 *)ctx->key_enc, + num_rounds(ctx), blocks, walk.iv); + memcpy(tdst, tail, nbytes); + err = blkcipher_walk_done(desc, &walk, 0); + } + kernel_neon_end(); + + return err; +} + +static int xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + int err, first, rounds = num_rounds(&ctx->key1); + struct blkcipher_walk walk; + unsigned int blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { + ce_aes_xts_encrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key1.key_enc, rounds, blocks, + walk.iv, (u8 *)ctx->key2.key_enc, first); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + + return err; +} + +static int xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct crypto_aes_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + int err, first, rounds = num_rounds(&ctx->key1); + struct blkcipher_walk walk; + unsigned int blocks; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + kernel_neon_begin(); + for (first = 1; (blocks = (walk.nbytes / AES_BLOCK_SIZE)); first = 0) { + ce_aes_xts_decrypt(walk.dst.virt.addr, walk.src.virt.addr, + (u8 *)ctx->key1.key_dec, rounds, blocks, + walk.iv, (u8 *)ctx->key2.key_enc, first); + err = blkcipher_walk_done(desc, &walk, + walk.nbytes % AES_BLOCK_SIZE); + } + kernel_neon_end(); + + return err; +} + +static struct crypto_alg aes_algs[] = { { + .cra_name = "__ecb-aes-ce", + .cra_driver_name = "__driver-ecb-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = ecb_encrypt, + .decrypt = ecb_decrypt, + }, +}, { + .cra_name = "__cbc-aes-ce", + .cra_driver_name = "__driver-cbc-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = cbc_encrypt, + .decrypt = cbc_decrypt, + }, +}, { + .cra_name = "__ctr-aes-ce", + .cra_driver_name = "__driver-ctr-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct crypto_aes_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ce_aes_setkey, + .encrypt = ctr_encrypt, + .decrypt = ctr_encrypt, + }, +}, { + .cra_name = "__xts-aes-ce", + .cra_driver_name = "__driver-xts-aes-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct crypto_aes_xts_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_blkcipher = { + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = xts_set_key, + .encrypt = xts_encrypt, + .decrypt = xts_decrypt, + }, +}, { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_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, + } +}, { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_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, + } +}, { + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_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, + } +}, { + .cra_name = "xts(aes)", + .cra_driver_name = "xts-aes-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER|CRYPTO_ALG_ASYNC, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct async_helper_ctx), + .cra_alignmask = 7, + .cra_type = &crypto_ablkcipher_type, + .cra_module = THIS_MODULE, + .cra_init = ablk_init, + .cra_exit = ablk_exit, + .cra_ablkcipher = { + .min_keysize = 2 * AES_MIN_KEY_SIZE, + .max_keysize = 2 * AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ablk_set_key, + .encrypt = ablk_encrypt, + .decrypt = ablk_decrypt, + } +} }; + +static int __init aes_init(void) +{ + if (!(elf_hwcap2 & HWCAP2_AES)) + return -ENODEV; + return crypto_register_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +static void __exit aes_exit(void) +{ + crypto_unregister_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +module_init(aes_init); +module_exit(aes_exit); diff --git a/arch/arm/crypto/aesbs-glue.c b/arch/arm/crypto/aesbs-glue.c index 15468fbbdea3..6d685298690e 100644 --- a/arch/arm/crypto/aesbs-glue.c +++ b/arch/arm/crypto/aesbs-glue.c @@ -301,7 +301,8 @@ static struct crypto_alg aesbs_algs[] = { { .cra_name = "__cbc-aes-neonbs", .cra_driver_name = "__driver-cbc-aes-neonbs", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aesbs_cbc_ctx), .cra_alignmask = 7, @@ -319,7 +320,8 @@ static struct crypto_alg aesbs_algs[] = { { .cra_name = "__ctr-aes-neonbs", .cra_driver_name = "__driver-ctr-aes-neonbs", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct aesbs_ctr_ctx), .cra_alignmask = 7, @@ -337,7 +339,8 @@ static struct crypto_alg aesbs_algs[] = { { .cra_name = "__xts-aes-neonbs", .cra_driver_name = "__driver-xts-aes-neonbs", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aesbs_xts_ctx), .cra_alignmask = 7, diff --git a/arch/arm/crypto/ghash-ce-core.S b/arch/arm/crypto/ghash-ce-core.S new file mode 100644 index 000000000000..f6ab8bcc9efe --- /dev/null +++ b/arch/arm/crypto/ghash-ce-core.S @@ -0,0 +1,94 @@ +/* + * Accelerated GHASH implementation with ARMv8 vmull.p64 instructions. + * + * Copyright (C) 2015 Linaro Ltd. <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + + SHASH .req q0 + SHASH2 .req q1 + T1 .req q2 + T2 .req q3 + MASK .req q4 + XL .req q5 + XM .req q6 + XH .req q7 + IN1 .req q7 + + SHASH_L .req d0 + SHASH_H .req d1 + SHASH2_L .req d2 + T1_L .req d4 + MASK_L .req d8 + XL_L .req d10 + XL_H .req d11 + XM_L .req d12 + XM_H .req d13 + XH_L .req d14 + + .text + .fpu crypto-neon-fp-armv8 + + /* + * void pmull_ghash_update(int blocks, u64 dg[], const char *src, + * struct ghash_key const *k, const char *head) + */ +ENTRY(pmull_ghash_update) + vld1.64 {SHASH}, [r3] + vld1.64 {XL}, [r1] + vmov.i8 MASK, #0xe1 + vext.8 SHASH2, SHASH, SHASH, #8 + vshl.u64 MASK, MASK, #57 + veor SHASH2, SHASH2, SHASH + + /* do the head block first, if supplied */ + ldr ip, [sp] + teq ip, #0 + beq 0f + vld1.64 {T1}, [ip] + teq r0, #0 + b 1f + +0: vld1.64 {T1}, [r2]! + subs r0, r0, #1 + +1: /* multiply XL by SHASH in GF(2^128) */ +#ifndef CONFIG_CPU_BIG_ENDIAN + vrev64.8 T1, T1 +#endif + vext.8 T2, XL, XL, #8 + vext.8 IN1, T1, T1, #8 + veor T1, T1, T2 + veor XL, XL, IN1 + + vmull.p64 XH, SHASH_H, XL_H @ a1 * b1 + veor T1, T1, XL + vmull.p64 XL, SHASH_L, XL_L @ a0 * b0 + vmull.p64 XM, SHASH2_L, T1_L @ (a1 + a0)(b1 + b0) + + vext.8 T1, XL, XH, #8 + veor T2, XL, XH + veor XM, XM, T1 + veor XM, XM, T2 + vmull.p64 T2, XL_L, MASK_L + + vmov XH_L, XM_H + vmov XM_H, XL_L + + veor XL, XM, T2 + vext.8 T2, XL, XL, #8 + vmull.p64 XL, XL_L, MASK_L + veor T2, T2, XH + veor XL, XL, T2 + + bne 0b + + vst1.64 {XL}, [r1] + bx lr +ENDPROC(pmull_ghash_update) diff --git a/arch/arm/crypto/ghash-ce-glue.c b/arch/arm/crypto/ghash-ce-glue.c new file mode 100644 index 000000000000..03a39fe29246 --- /dev/null +++ b/arch/arm/crypto/ghash-ce-glue.c @@ -0,0 +1,320 @@ +/* + * Accelerated GHASH implementation with ARMv8 vmull.p64 instructions. + * + * Copyright (C) 2015 Linaro Ltd. <ard.biesheuvel@linaro.org> + * + * 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 <asm/hwcap.h> +#include <asm/neon.h> +#include <asm/simd.h> +#include <asm/unaligned.h> +#include <crypto/cryptd.h> +#include <crypto/internal/hash.h> +#include <crypto/gf128mul.h> +#include <linux/crypto.h> +#include <linux/module.h> + +MODULE_DESCRIPTION("GHASH secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +#define GHASH_BLOCK_SIZE 16 +#define GHASH_DIGEST_SIZE 16 + +struct ghash_key { + u64 a; + u64 b; +}; + +struct ghash_desc_ctx { + u64 digest[GHASH_DIGEST_SIZE/sizeof(u64)]; + u8 buf[GHASH_BLOCK_SIZE]; + u32 count; +}; + +struct ghash_async_ctx { + struct cryptd_ahash *cryptd_tfm; +}; + +asmlinkage void pmull_ghash_update(int blocks, u64 dg[], const char *src, + struct ghash_key const *k, const char *head); + +static int ghash_init(struct shash_desc *desc) +{ + struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); + + *ctx = (struct ghash_desc_ctx){}; + return 0; +} + +static int ghash_update(struct shash_desc *desc, const u8 *src, + unsigned int len) +{ + struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; + + ctx->count += len; + + if ((partial + len) >= GHASH_BLOCK_SIZE) { + struct ghash_key *key = crypto_shash_ctx(desc->tfm); + int blocks; + + if (partial) { + int p = GHASH_BLOCK_SIZE - partial; + + memcpy(ctx->buf + partial, src, p); + src += p; + len -= p; + } + + blocks = len / GHASH_BLOCK_SIZE; + len %= GHASH_BLOCK_SIZE; + + kernel_neon_begin(); + pmull_ghash_update(blocks, ctx->digest, src, key, + partial ? ctx->buf : NULL); + kernel_neon_end(); + src += blocks * GHASH_BLOCK_SIZE; + partial = 0; + } + if (len) + memcpy(ctx->buf + partial, src, len); + return 0; +} + +static int ghash_final(struct shash_desc *desc, u8 *dst) +{ + struct ghash_desc_ctx *ctx = shash_desc_ctx(desc); + unsigned int partial = ctx->count % GHASH_BLOCK_SIZE; + + if (partial) { + struct ghash_key *key = crypto_shash_ctx(desc->tfm); + + memset(ctx->buf + partial, 0, GHASH_BLOCK_SIZE - partial); + kernel_neon_begin(); + pmull_ghash_update(1, ctx->digest, ctx->buf, key, NULL); + kernel_neon_end(); + } + put_unaligned_be64(ctx->digest[1], dst); + put_unaligned_be64(ctx->digest[0], dst + 8); + + *ctx = (struct ghash_desc_ctx){}; + return 0; +} + +static int ghash_setkey(struct crypto_shash *tfm, + const u8 *inkey, unsigned int keylen) +{ + struct ghash_key *key = crypto_shash_ctx(tfm); + u64 a, b; + + if (keylen != GHASH_BLOCK_SIZE) { + crypto_shash_set_flags(tfm, CRYPTO_TFM_RES_BAD_KEY_LEN); + return -EINVAL; + } + + /* perform multiplication by 'x' in GF(2^128) */ + b = get_unaligned_be64(inkey); + a = get_unaligned_be64(inkey + 8); + + key->a = (a << 1) | (b >> 63); + key->b = (b << 1) | (a >> 63); + + if (b >> 63) + key->b ^= 0xc200000000000000UL; + + return 0; +} + +static struct shash_alg ghash_alg = { + .digestsize = GHASH_DIGEST_SIZE, + .init = ghash_init, + .update = ghash_update, + .final = ghash_final, + .setkey = ghash_setkey, + .descsize = sizeof(struct ghash_desc_ctx), + .base = { + .cra_name = "ghash", + .cra_driver_name = "__driver-ghash-ce", + .cra_priority = 0, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_INTERNAL, + .cra_blocksize = GHASH_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ghash_key), + .cra_module = THIS_MODULE, + }, +}; + +static int ghash_async_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm); + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm; + + if (!may_use_simd()) { + memcpy(cryptd_req, req, sizeof(*req)); + ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base); + return crypto_ahash_init(cryptd_req); + } else { + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct crypto_shash *child = cryptd_ahash_child(cryptd_tfm); + + desc->tfm = child; + desc->flags = req->base.flags; + return crypto_shash_init(desc); + } +} + +static int ghash_async_update(struct ahash_request *req) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + + if (!may_use_simd()) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm); + struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm; + + memcpy(cryptd_req, req, sizeof(*req)); + ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base); + return crypto_ahash_update(cryptd_req); + } else { + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + return shash_ahash_update(req, desc); + } +} + +static int ghash_async_final(struct ahash_request *req) +{ + struct ahash_request *cryptd_req = ahash_request_ctx(req); + + if (!may_use_simd()) { + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm); + struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm; + + memcpy(cryptd_req, req, sizeof(*req)); + ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base); + return crypto_ahash_final(cryptd_req); + } else { + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + return crypto_shash_final(desc, req->result); + } +} + +static int ghash_async_digest(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm); + struct ahash_request *cryptd_req = ahash_request_ctx(req); + struct cryptd_ahash *cryptd_tfm = ctx->cryptd_tfm; + + if (!may_use_simd()) { + memcpy(cryptd_req, req, sizeof(*req)); + ahash_request_set_tfm(cryptd_req, &cryptd_tfm->base); + return crypto_ahash_digest(cryptd_req); + } else { + struct shash_desc *desc = cryptd_shash_desc(cryptd_req); + struct crypto_shash *child = cryptd_ahash_child(cryptd_tfm); + + desc->tfm = child; + desc->flags = req->base.flags; + return shash_ahash_digest(req, desc); + } +} + +static int ghash_async_setkey(struct crypto_ahash *tfm, const u8 *key, + unsigned int keylen) +{ + struct ghash_async_ctx *ctx = crypto_ahash_ctx(tfm); + struct crypto_ahash *child = &ctx->cryptd_tfm->base; + int err; + + crypto_ahash_clear_flags(child, CRYPTO_TFM_REQ_MASK); + crypto_ahash_set_flags(child, crypto_ahash_get_flags(tfm) + & CRYPTO_TFM_REQ_MASK); + err = crypto_ahash_setkey(child, key, keylen); + crypto_ahash_set_flags(tfm, crypto_ahash_get_flags(child) + & CRYPTO_TFM_RES_MASK); + + return err; +} + +static int ghash_async_init_tfm(struct crypto_tfm *tfm) +{ + struct cryptd_ahash *cryptd_tfm; + struct ghash_async_ctx *ctx = crypto_tfm_ctx(tfm); + + cryptd_tfm = cryptd_alloc_ahash("__driver-ghash-ce", + CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); + if (IS_ERR(cryptd_tfm)) + return PTR_ERR(cryptd_tfm); + ctx->cryptd_tfm = cryptd_tfm; + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct ahash_request) + + crypto_ahash_reqsize(&cryptd_tfm->base)); + + return 0; +} + +static void ghash_async_exit_tfm(struct crypto_tfm *tfm) +{ + struct ghash_async_ctx *ctx = crypto_tfm_ctx(tfm); + + cryptd_free_ahash(ctx->cryptd_tfm); +} + +static struct ahash_alg ghash_async_alg = { + .init = ghash_async_init, + .update = ghash_async_update, + .final = ghash_async_final, + .setkey = ghash_async_setkey, + .digest = ghash_async_digest, + .halg.digestsize = GHASH_DIGEST_SIZE, + .halg.base = { + .cra_name = "ghash", + .cra_driver_name = "ghash-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, + .cra_blocksize = GHASH_BLOCK_SIZE, + .cra_type = &crypto_ahash_type, + .cra_ctxsize = sizeof(struct ghash_async_ctx), + .cra_module = THIS_MODULE, + .cra_init = ghash_async_init_tfm, + .cra_exit = ghash_async_exit_tfm, + }, +}; + +static int __init ghash_ce_mod_init(void) +{ + int err; + + if (!(elf_hwcap2 & HWCAP2_PMULL)) + return -ENODEV; + + err = crypto_register_shash(&ghash_alg); + if (err) + return err; + err = crypto_register_ahash(&ghash_async_alg); + if (err) + goto err_shash; + + return 0; + +err_shash: + crypto_unregister_shash(&ghash_alg); + return err; +} + +static void __exit ghash_ce_mod_exit(void) +{ + crypto_unregister_ahash(&ghash_async_alg); + crypto_unregister_shash(&ghash_alg); +} + +module_init(ghash_ce_mod_init); +module_exit(ghash_ce_mod_exit); diff --git a/arch/arm/crypto/sha1-ce-core.S b/arch/arm/crypto/sha1-ce-core.S new file mode 100644 index 000000000000..b623f51ccbcf --- /dev/null +++ b/arch/arm/crypto/sha1-ce-core.S @@ -0,0 +1,125 @@ +/* + * sha1-ce-core.S - SHA-1 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd. + * Author: Ard Biesheuvel <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + + .text + .fpu crypto-neon-fp-armv8 + + k0 .req q0 + k1 .req q1 + k2 .req q2 + k3 .req q3 + + ta0 .req q4 + ta1 .req q5 + tb0 .req q5 + tb1 .req q4 + + dga .req q6 + dgb .req q7 + dgbs .req s28 + + dg0 .req q12 + dg1a0 .req q13 + dg1a1 .req q14 + dg1b0 .req q14 + dg1b1 .req q13 + + .macro add_only, op, ev, rc, s0, dg1 + .ifnb \s0 + vadd.u32 tb\ev, q\s0, \rc + .endif + sha1h.32 dg1b\ev, dg0 + .ifb \dg1 + sha1\op\().32 dg0, dg1a\ev, ta\ev + .else + sha1\op\().32 dg0, \dg1, ta\ev + .endif + .endm + + .macro add_update, op, ev, rc, s0, s1, s2, s3, dg1 + sha1su0.32 q\s0, q\s1, q\s2 + add_only \op, \ev, \rc, \s1, \dg1 + sha1su1.32 q\s0, q\s3 + .endm + + .align 6 +.Lsha1_rcon: + .word 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999 + .word 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1 + .word 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc + .word 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6 + + /* + * void sha1_ce_transform(struct sha1_state *sst, u8 const *src, + * int blocks); + */ +ENTRY(sha1_ce_transform) + /* load round constants */ + adr ip, .Lsha1_rcon + vld1.32 {k0-k1}, [ip, :128]! + vld1.32 {k2-k3}, [ip, :128] + + /* load state */ + vld1.32 {dga}, [r0] + vldr dgbs, [r0, #16] + + /* load input */ +0: vld1.32 {q8-q9}, [r1]! + vld1.32 {q10-q11}, [r1]! + subs r2, r2, #1 + +#ifndef CONFIG_CPU_BIG_ENDIAN + vrev32.8 q8, q8 + vrev32.8 q9, q9 + vrev32.8 q10, q10 + vrev32.8 q11, q11 +#endif + + vadd.u32 ta0, q8, k0 + vmov dg0, dga + + add_update c, 0, k0, 8, 9, 10, 11, dgb + add_update c, 1, k0, 9, 10, 11, 8 + add_update c, 0, k0, 10, 11, 8, 9 + add_update c, 1, k0, 11, 8, 9, 10 + add_update c, 0, k1, 8, 9, 10, 11 + + add_update p, 1, k1, 9, 10, 11, 8 + add_update p, 0, k1, 10, 11, 8, 9 + add_update p, 1, k1, 11, 8, 9, 10 + add_update p, 0, k1, 8, 9, 10, 11 + add_update p, 1, k2, 9, 10, 11, 8 + + add_update m, 0, k2, 10, 11, 8, 9 + add_update m, 1, k2, 11, 8, 9, 10 + add_update m, 0, k2, 8, 9, 10, 11 + add_update m, 1, k2, 9, 10, 11, 8 + add_update m, 0, k3, 10, 11, 8, 9 + + add_update p, 1, k3, 11, 8, 9, 10 + add_only p, 0, k3, 9 + add_only p, 1, k3, 10 + add_only p, 0, k3, 11 + add_only p, 1 + + /* update state */ + vadd.u32 dga, dga, dg0 + vadd.u32 dgb, dgb, dg1a0 + bne 0b + + /* store new state */ + vst1.32 {dga}, [r0] + vstr dgbs, [r0, #16] + bx lr +ENDPROC(sha1_ce_transform) diff --git a/arch/arm/crypto/sha1-ce-glue.c b/arch/arm/crypto/sha1-ce-glue.c new file mode 100644 index 000000000000..80bc2fcd241a --- /dev/null +++ b/arch/arm/crypto/sha1-ce-glue.c @@ -0,0 +1,96 @@ +/* + * sha1-ce-glue.c - SHA-1 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <crypto/sha1_base.h> +#include <linux/crypto.h> +#include <linux/module.h> + +#include <asm/hwcap.h> +#include <asm/neon.h> +#include <asm/simd.h> + +#include "sha1.h" + +MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +asmlinkage void sha1_ce_transform(struct sha1_state *sst, u8 const *src, + int blocks); + +static int sha1_ce_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + if (!may_use_simd() || + (sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE) + return sha1_update_arm(desc, data, len); + + kernel_neon_begin(); + sha1_base_do_update(desc, data, len, sha1_ce_transform); + kernel_neon_end(); + + return 0; +} + +static int sha1_ce_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + if (!may_use_simd()) + return sha1_finup_arm(desc, data, len, out); + + kernel_neon_begin(); + if (len) + sha1_base_do_update(desc, data, len, sha1_ce_transform); + sha1_base_do_finalize(desc, sha1_ce_transform); + kernel_neon_end(); + + return sha1_base_finish(desc, out); +} + +static int sha1_ce_final(struct shash_desc *desc, u8 *out) +{ + return sha1_ce_finup(desc, NULL, 0, out); +} + +static struct shash_alg alg = { + .init = sha1_base_init, + .update = sha1_ce_update, + .final = sha1_ce_final, + .finup = sha1_ce_finup, + .descsize = sizeof(struct sha1_state), + .digestsize = SHA1_DIGEST_SIZE, + .base = { + .cra_name = "sha1", + .cra_driver_name = "sha1-ce", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init sha1_ce_mod_init(void) +{ + if (!(elf_hwcap2 & HWCAP2_SHA1)) + return -ENODEV; + return crypto_register_shash(&alg); +} + +static void __exit sha1_ce_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(sha1_ce_mod_init); +module_exit(sha1_ce_mod_fini); diff --git a/arch/arm/include/asm/crypto/sha1.h b/arch/arm/crypto/sha1.h index 75e6a417416b..ffd8bd08b1a7 100644 --- a/arch/arm/include/asm/crypto/sha1.h +++ b/arch/arm/crypto/sha1.h @@ -7,4 +7,7 @@ extern int sha1_update_arm(struct shash_desc *desc, const u8 *data, unsigned int len); +extern int sha1_finup_arm(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out); + #endif diff --git a/arch/arm/crypto/sha1_glue.c b/arch/arm/crypto/sha1_glue.c index e31b0440c613..6fc73bf8766d 100644 --- a/arch/arm/crypto/sha1_glue.c +++ b/arch/arm/crypto/sha1_glue.c @@ -22,127 +22,47 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> +#include <crypto/sha1_base.h> #include <asm/byteorder.h> -#include <asm/crypto/sha1.h> +#include "sha1.h" asmlinkage void sha1_block_data_order(u32 *digest, const unsigned char *data, unsigned int rounds); - -static int sha1_init(struct shash_desc *desc) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - - *sctx = (struct sha1_state){ - .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, - }; - - return 0; -} - - -static int __sha1_update(struct sha1_state *sctx, const u8 *data, - unsigned int len, unsigned int partial) -{ - unsigned int done = 0; - - sctx->count += len; - - if (partial) { - done = SHA1_BLOCK_SIZE - partial; - memcpy(sctx->buffer + partial, data, done); - sha1_block_data_order(sctx->state, sctx->buffer, 1); - } - - if (len - done >= SHA1_BLOCK_SIZE) { - const unsigned int rounds = (len - done) / SHA1_BLOCK_SIZE; - sha1_block_data_order(sctx->state, data + done, rounds); - done += rounds * SHA1_BLOCK_SIZE; - } - - memcpy(sctx->buffer, data + done, len - done); - return 0; -} - - int sha1_update_arm(struct shash_desc *desc, const u8 *data, unsigned int len) { - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; - int res; + /* make sure casting to sha1_block_fn() is safe */ + BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0); - /* Handle the fast case right here */ - if (partial + len < SHA1_BLOCK_SIZE) { - sctx->count += len; - memcpy(sctx->buffer + partial, data, len); - return 0; - } - res = __sha1_update(sctx, data, len, partial); - return res; + return sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_block_data_order); } EXPORT_SYMBOL_GPL(sha1_update_arm); - -/* Add padding and return the message digest. */ static int sha1_final(struct shash_desc *desc, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int i, index, padlen; - __be32 *dst = (__be32 *)out; - __be64 bits; - static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; - - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64 and append length */ - index = sctx->count % SHA1_BLOCK_SIZE; - padlen = (index < 56) ? (56 - index) : ((SHA1_BLOCK_SIZE+56) - index); - /* We need to fill a whole block for __sha1_update() */ - if (padlen <= 56) { - sctx->count += padlen; - memcpy(sctx->buffer + index, padding, padlen); - } else { - __sha1_update(sctx, padding, padlen, index); - } - __sha1_update(sctx, (const u8 *)&bits, sizeof(bits), 56); - - /* Store state in digest */ - for (i = 0; i < 5; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof(*sctx)); - return 0; + sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_block_data_order); + return sha1_base_finish(desc, out); } - -static int sha1_export(struct shash_desc *desc, void *out) +int sha1_finup_arm(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - memcpy(out, sctx, sizeof(*sctx)); - return 0; + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_block_data_order); + return sha1_final(desc, out); } - - -static int sha1_import(struct shash_desc *desc, const void *in) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - memcpy(sctx, in, sizeof(*sctx)); - return 0; -} - +EXPORT_SYMBOL_GPL(sha1_finup_arm); static struct shash_alg alg = { .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_init, + .init = sha1_base_init, .update = sha1_update_arm, .final = sha1_final, - .export = sha1_export, - .import = sha1_import, + .finup = sha1_finup_arm, .descsize = sizeof(struct sha1_state), - .statesize = sizeof(struct sha1_state), .base = { .cra_name = "sha1", .cra_driver_name= "sha1-asm", diff --git a/arch/arm/crypto/sha1_neon_glue.c b/arch/arm/crypto/sha1_neon_glue.c index 0b0083757d47..4e22f122f966 100644 --- a/arch/arm/crypto/sha1_neon_glue.c +++ b/arch/arm/crypto/sha1_neon_glue.c @@ -25,147 +25,60 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> -#include <asm/byteorder.h> +#include <crypto/sha1_base.h> #include <asm/neon.h> #include <asm/simd.h> -#include <asm/crypto/sha1.h> +#include "sha1.h" asmlinkage void sha1_transform_neon(void *state_h, const char *data, unsigned int rounds); - -static int sha1_neon_init(struct shash_desc *desc) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - - *sctx = (struct sha1_state){ - .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, - }; - - return 0; -} - -static int __sha1_neon_update(struct shash_desc *desc, const u8 *data, - unsigned int len, unsigned int partial) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int done = 0; - - sctx->count += len; - - if (partial) { - done = SHA1_BLOCK_SIZE - partial; - memcpy(sctx->buffer + partial, data, done); - sha1_transform_neon(sctx->state, sctx->buffer, 1); - } - - if (len - done >= SHA1_BLOCK_SIZE) { - const unsigned int rounds = (len - done) / SHA1_BLOCK_SIZE; - - sha1_transform_neon(sctx->state, data + done, rounds); - done += rounds * SHA1_BLOCK_SIZE; - } - - memcpy(sctx->buffer, data + done, len - done); - - return 0; -} - static int sha1_neon_update(struct shash_desc *desc, const u8 *data, - unsigned int len) + unsigned int len) { struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; - int res; - /* Handle the fast case right here */ - if (partial + len < SHA1_BLOCK_SIZE) { - sctx->count += len; - memcpy(sctx->buffer + partial, data, len); + if (!may_use_simd() || + (sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE) + return sha1_update_arm(desc, data, len); - return 0; - } - - if (!may_use_simd()) { - res = sha1_update_arm(desc, data, len); - } else { - kernel_neon_begin(); - res = __sha1_neon_update(desc, data, len, partial); - kernel_neon_end(); - } - - return res; -} - - -/* Add padding and return the message digest. */ -static int sha1_neon_final(struct shash_desc *desc, u8 *out) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int i, index, padlen; - __be32 *dst = (__be32 *)out; - __be64 bits; - static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; - - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64 and append length */ - index = sctx->count % SHA1_BLOCK_SIZE; - padlen = (index < 56) ? (56 - index) : ((SHA1_BLOCK_SIZE+56) - index); - if (!may_use_simd()) { - sha1_update_arm(desc, padding, padlen); - sha1_update_arm(desc, (const u8 *)&bits, sizeof(bits)); - } else { - kernel_neon_begin(); - /* We need to fill a whole block for __sha1_neon_update() */ - if (padlen <= 56) { - sctx->count += padlen; - memcpy(sctx->buffer + index, padding, padlen); - } else { - __sha1_neon_update(desc, padding, padlen, index); - } - __sha1_neon_update(desc, (const u8 *)&bits, sizeof(bits), 56); - kernel_neon_end(); - } - - /* Store state in digest */ - for (i = 0; i < 5; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof(*sctx)); + kernel_neon_begin(); + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_transform_neon); + kernel_neon_end(); return 0; } -static int sha1_neon_export(struct shash_desc *desc, void *out) +static int sha1_neon_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); + if (!may_use_simd()) + return sha1_finup_arm(desc, data, len, out); - memcpy(out, sctx, sizeof(*sctx)); + kernel_neon_begin(); + if (len) + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_transform_neon); + sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_transform_neon); + kernel_neon_end(); - return 0; + return sha1_base_finish(desc, out); } -static int sha1_neon_import(struct shash_desc *desc, const void *in) +static int sha1_neon_final(struct shash_desc *desc, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - - return 0; + return sha1_neon_finup(desc, NULL, 0, out); } static struct shash_alg alg = { .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_neon_init, + .init = sha1_base_init, .update = sha1_neon_update, .final = sha1_neon_final, - .export = sha1_neon_export, - .import = sha1_neon_import, + .finup = sha1_neon_finup, .descsize = sizeof(struct sha1_state), - .statesize = sizeof(struct sha1_state), .base = { .cra_name = "sha1", .cra_driver_name = "sha1-neon", diff --git a/arch/arm/crypto/sha2-ce-core.S b/arch/arm/crypto/sha2-ce-core.S new file mode 100644 index 000000000000..87ec11a5f405 --- /dev/null +++ b/arch/arm/crypto/sha2-ce-core.S @@ -0,0 +1,125 @@ +/* + * sha2-ce-core.S - SHA-224/256 secure hash using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd. + * Author: Ard Biesheuvel <ard.biesheuvel@linaro.org> + * + * 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/linkage.h> +#include <asm/assembler.h> + + .text + .fpu crypto-neon-fp-armv8 + + k0 .req q7 + k1 .req q8 + rk .req r3 + + ta0 .req q9 + ta1 .req q10 + tb0 .req q10 + tb1 .req q9 + + dga .req q11 + dgb .req q12 + + dg0 .req q13 + dg1 .req q14 + dg2 .req q15 + + .macro add_only, ev, s0 + vmov dg2, dg0 + .ifnb \s0 + vld1.32 {k\ev}, [rk, :128]! + .endif + sha256h.32 dg0, dg1, tb\ev + sha256h2.32 dg1, dg2, tb\ev + .ifnb \s0 + vadd.u32 ta\ev, q\s0, k\ev + .endif + .endm + + .macro add_update, ev, s0, s1, s2, s3 + sha256su0.32 q\s0, q\s1 + add_only \ev, \s1 + sha256su1.32 q\s0, q\s2, q\s3 + .endm + + .align 6 +.Lsha256_rcon: + .word 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5 + .word 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5 + .word 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3 + .word 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174 + .word 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc + .word 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da + .word 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7 + .word 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967 + .word 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13 + .word 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85 + .word 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3 + .word 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070 + .word 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5 + .word 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3 + .word 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208 + .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + + /* + * void sha2_ce_transform(struct sha256_state *sst, u8 const *src, + int blocks); + */ +ENTRY(sha2_ce_transform) + /* load state */ + vld1.32 {dga-dgb}, [r0] + + /* load input */ +0: vld1.32 {q0-q1}, [r1]! + vld1.32 {q2-q3}, [r1]! + subs r2, r2, #1 + +#ifndef CONFIG_CPU_BIG_ENDIAN + vrev32.8 q0, q0 + vrev32.8 q1, q1 + vrev32.8 q2, q2 + vrev32.8 q3, q3 +#endif + + /* load first round constant */ + adr rk, .Lsha256_rcon + vld1.32 {k0}, [rk, :128]! + + vadd.u32 ta0, q0, k0 + vmov dg0, dga + vmov dg1, dgb + + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + add_update 1, 0, 1, 2, 3 + add_update 0, 1, 2, 3, 0 + add_update 1, 2, 3, 0, 1 + add_update 0, 3, 0, 1, 2 + + add_only 1, 1 + add_only 0, 2 + add_only 1, 3 + add_only 0 + + /* update state */ + vadd.u32 dga, dga, dg0 + vadd.u32 dgb, dgb, dg1 + bne 0b + + /* store new state */ + vst1.32 {dga-dgb}, [r0] + bx lr +ENDPROC(sha2_ce_transform) diff --git a/arch/arm/crypto/sha2-ce-glue.c b/arch/arm/crypto/sha2-ce-glue.c new file mode 100644 index 000000000000..0755b2d657f3 --- /dev/null +++ b/arch/arm/crypto/sha2-ce-glue.c @@ -0,0 +1,114 @@ +/* + * sha2-ce-glue.c - SHA-224/SHA-256 using ARMv8 Crypto Extensions + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <crypto/sha256_base.h> +#include <linux/crypto.h> +#include <linux/module.h> + +#include <asm/hwcap.h> +#include <asm/simd.h> +#include <asm/neon.h> +#include <asm/unaligned.h> + +#include "sha256_glue.h" + +MODULE_DESCRIPTION("SHA-224/SHA-256 secure hash using ARMv8 Crypto Extensions"); +MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); +MODULE_LICENSE("GPL v2"); + +asmlinkage void sha2_ce_transform(struct sha256_state *sst, u8 const *src, + int blocks); + +static int sha2_ce_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + if (!may_use_simd() || + (sctx->count % SHA256_BLOCK_SIZE) + len < SHA256_BLOCK_SIZE) + return crypto_sha256_arm_update(desc, data, len); + + kernel_neon_begin(); + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha2_ce_transform); + kernel_neon_end(); + + return 0; +} + +static int sha2_ce_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + if (!may_use_simd()) + return crypto_sha256_arm_finup(desc, data, len, out); + + kernel_neon_begin(); + if (len) + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha2_ce_transform); + sha256_base_do_finalize(desc, (sha256_block_fn *)sha2_ce_transform); + kernel_neon_end(); + + return sha256_base_finish(desc, out); +} + +static int sha2_ce_final(struct shash_desc *desc, u8 *out) +{ + return sha2_ce_finup(desc, NULL, 0, out); +} + +static struct shash_alg algs[] = { { + .init = sha224_base_init, + .update = sha2_ce_update, + .final = sha2_ce_final, + .finup = sha2_ce_finup, + .descsize = sizeof(struct sha256_state), + .digestsize = SHA224_DIGEST_SIZE, + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .init = sha256_base_init, + .update = sha2_ce_update, + .final = sha2_ce_final, + .finup = sha2_ce_finup, + .descsize = sizeof(struct sha256_state), + .digestsize = SHA256_DIGEST_SIZE, + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-ce", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init sha2_ce_mod_init(void) +{ + if (!(elf_hwcap2 & HWCAP2_SHA2)) + return -ENODEV; + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} + +static void __exit sha2_ce_mod_fini(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} + +module_init(sha2_ce_mod_init); +module_exit(sha2_ce_mod_fini); diff --git a/arch/arm/crypto/sha256-armv4.pl b/arch/arm/crypto/sha256-armv4.pl new file mode 100644 index 000000000000..fac0533ea633 --- /dev/null +++ b/arch/arm/crypto/sha256-armv4.pl @@ -0,0 +1,716 @@ +#!/usr/bin/env perl + +# ==================================================================== +# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# +# Permission to use under GPL terms is granted. +# ==================================================================== + +# SHA256 block procedure for ARMv4. May 2007. + +# Performance is ~2x better than gcc 3.4 generated code and in "abso- +# lute" terms is ~2250 cycles per 64-byte block or ~35 cycles per +# byte [on single-issue Xscale PXA250 core]. + +# July 2010. +# +# Rescheduling for dual-issue pipeline resulted in 22% improvement on +# Cortex A8 core and ~20 cycles per processed byte. + +# February 2011. +# +# Profiler-assisted and platform-specific optimization resulted in 16% +# improvement on Cortex A8 core and ~15.4 cycles per processed byte. + +# September 2013. +# +# Add NEON implementation. On Cortex A8 it was measured to process one +# byte in 12.5 cycles or 23% faster than integer-only code. Snapdragon +# S4 does it in 12.5 cycles too, but it's 50% faster than integer-only +# code (meaning that latter performs sub-optimally, nothing was done +# about it). + +# May 2014. +# +# Add ARMv8 code path performing at 2.0 cpb on Apple A7. + +while (($output=shift) && ($output!~/^\w[\w\-]*\.\w+$/)) {} +open STDOUT,">$output"; + +$ctx="r0"; $t0="r0"; +$inp="r1"; $t4="r1"; +$len="r2"; $t1="r2"; +$T1="r3"; $t3="r3"; +$A="r4"; +$B="r5"; +$C="r6"; +$D="r7"; +$E="r8"; +$F="r9"; +$G="r10"; +$H="r11"; +@V=($A,$B,$C,$D,$E,$F,$G,$H); +$t2="r12"; +$Ktbl="r14"; + +@Sigma0=( 2,13,22); +@Sigma1=( 6,11,25); +@sigma0=( 7,18, 3); +@sigma1=(17,19,10); + +sub BODY_00_15 { +my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_; + +$code.=<<___ if ($i<16); +#if __ARM_ARCH__>=7 + @ ldr $t1,[$inp],#4 @ $i +# if $i==15 + str $inp,[sp,#17*4] @ make room for $t4 +# endif + eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]` + add $a,$a,$t2 @ h+=Maj(a,b,c) from the past + eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e) +# ifndef __ARMEB__ + rev $t1,$t1 +# endif +#else + @ ldrb $t1,[$inp,#3] @ $i + add $a,$a,$t2 @ h+=Maj(a,b,c) from the past + ldrb $t2,[$inp,#2] + ldrb $t0,[$inp,#1] + orr $t1,$t1,$t2,lsl#8 + ldrb $t2,[$inp],#4 + orr $t1,$t1,$t0,lsl#16 +# if $i==15 + str $inp,[sp,#17*4] @ make room for $t4 +# endif + eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]` + orr $t1,$t1,$t2,lsl#24 + eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e) +#endif +___ +$code.=<<___; + ldr $t2,[$Ktbl],#4 @ *K256++ + add $h,$h,$t1 @ h+=X[i] + str $t1,[sp,#`$i%16`*4] + eor $t1,$f,$g + add $h,$h,$t0,ror#$Sigma1[0] @ h+=Sigma1(e) + and $t1,$t1,$e + add $h,$h,$t2 @ h+=K256[i] + eor $t1,$t1,$g @ Ch(e,f,g) + eor $t0,$a,$a,ror#`$Sigma0[1]-$Sigma0[0]` + add $h,$h,$t1 @ h+=Ch(e,f,g) +#if $i==31 + and $t2,$t2,#0xff + cmp $t2,#0xf2 @ done? +#endif +#if $i<15 +# if __ARM_ARCH__>=7 + ldr $t1,[$inp],#4 @ prefetch +# else + ldrb $t1,[$inp,#3] +# endif + eor $t2,$a,$b @ a^b, b^c in next round +#else + ldr $t1,[sp,#`($i+2)%16`*4] @ from future BODY_16_xx + eor $t2,$a,$b @ a^b, b^c in next round + ldr $t4,[sp,#`($i+15)%16`*4] @ from future BODY_16_xx +#endif + eor $t0,$t0,$a,ror#`$Sigma0[2]-$Sigma0[0]` @ Sigma0(a) + and $t3,$t3,$t2 @ (b^c)&=(a^b) + add $d,$d,$h @ d+=h + eor $t3,$t3,$b @ Maj(a,b,c) + add $h,$h,$t0,ror#$Sigma0[0] @ h+=Sigma0(a) + @ add $h,$h,$t3 @ h+=Maj(a,b,c) +___ + ($t2,$t3)=($t3,$t2); +} + +sub BODY_16_XX { +my ($i,$a,$b,$c,$d,$e,$f,$g,$h) = @_; + +$code.=<<___; + @ ldr $t1,[sp,#`($i+1)%16`*4] @ $i + @ ldr $t4,[sp,#`($i+14)%16`*4] + mov $t0,$t1,ror#$sigma0[0] + add $a,$a,$t2 @ h+=Maj(a,b,c) from the past + mov $t2,$t4,ror#$sigma1[0] + eor $t0,$t0,$t1,ror#$sigma0[1] + eor $t2,$t2,$t4,ror#$sigma1[1] + eor $t0,$t0,$t1,lsr#$sigma0[2] @ sigma0(X[i+1]) + ldr $t1,[sp,#`($i+0)%16`*4] + eor $t2,$t2,$t4,lsr#$sigma1[2] @ sigma1(X[i+14]) + ldr $t4,[sp,#`($i+9)%16`*4] + + add $t2,$t2,$t0 + eor $t0,$e,$e,ror#`$Sigma1[1]-$Sigma1[0]` @ from BODY_00_15 + add $t1,$t1,$t2 + eor $t0,$t0,$e,ror#`$Sigma1[2]-$Sigma1[0]` @ Sigma1(e) + add $t1,$t1,$t4 @ X[i] +___ + &BODY_00_15(@_); +} + +$code=<<___; +#ifndef __KERNEL__ +# include "arm_arch.h" +#else +# define __ARM_ARCH__ __LINUX_ARM_ARCH__ +# define __ARM_MAX_ARCH__ 7 +#endif + +.text +#if __ARM_ARCH__<7 +.code 32 +#else +.syntax unified +# ifdef __thumb2__ +# define adrl adr +.thumb +# else +.code 32 +# endif +#endif + +.type K256,%object +.align 5 +K256: +.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 +.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 +.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 +.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 +.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc +.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da +.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 +.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 +.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 +.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 +.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 +.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 +.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 +.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 +.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 +.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +.size K256,.-K256 +.word 0 @ terminator +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +.LOPENSSL_armcap: +.word OPENSSL_armcap_P-sha256_block_data_order +#endif +.align 5 + +.global sha256_block_data_order +.type sha256_block_data_order,%function +sha256_block_data_order: +#if __ARM_ARCH__<7 + sub r3,pc,#8 @ sha256_block_data_order +#else + adr r3,sha256_block_data_order +#endif +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) + ldr r12,.LOPENSSL_armcap + ldr r12,[r3,r12] @ OPENSSL_armcap_P + tst r12,#ARMV8_SHA256 + bne .LARMv8 + tst r12,#ARMV7_NEON + bne .LNEON +#endif + add $len,$inp,$len,lsl#6 @ len to point at the end of inp + stmdb sp!,{$ctx,$inp,$len,r4-r11,lr} + ldmia $ctx,{$A,$B,$C,$D,$E,$F,$G,$H} + sub $Ktbl,r3,#256+32 @ K256 + sub sp,sp,#16*4 @ alloca(X[16]) +.Loop: +# if __ARM_ARCH__>=7 + ldr $t1,[$inp],#4 +# else + ldrb $t1,[$inp,#3] +# endif + eor $t3,$B,$C @ magic + eor $t2,$t2,$t2 +___ +for($i=0;$i<16;$i++) { &BODY_00_15($i,@V); unshift(@V,pop(@V)); } +$code.=".Lrounds_16_xx:\n"; +for (;$i<32;$i++) { &BODY_16_XX($i,@V); unshift(@V,pop(@V)); } +$code.=<<___; +#if __ARM_ARCH__>=7 + ite eq @ Thumb2 thing, sanity check in ARM +#endif + ldreq $t3,[sp,#16*4] @ pull ctx + bne .Lrounds_16_xx + + add $A,$A,$t2 @ h+=Maj(a,b,c) from the past + ldr $t0,[$t3,#0] + ldr $t1,[$t3,#4] + ldr $t2,[$t3,#8] + add $A,$A,$t0 + ldr $t0,[$t3,#12] + add $B,$B,$t1 + ldr $t1,[$t3,#16] + add $C,$C,$t2 + ldr $t2,[$t3,#20] + add $D,$D,$t0 + ldr $t0,[$t3,#24] + add $E,$E,$t1 + ldr $t1,[$t3,#28] + add $F,$F,$t2 + ldr $inp,[sp,#17*4] @ pull inp + ldr $t2,[sp,#18*4] @ pull inp+len + add $G,$G,$t0 + add $H,$H,$t1 + stmia $t3,{$A,$B,$C,$D,$E,$F,$G,$H} + cmp $inp,$t2 + sub $Ktbl,$Ktbl,#256 @ rewind Ktbl + bne .Loop + + add sp,sp,#`16+3`*4 @ destroy frame +#if __ARM_ARCH__>=5 + ldmia sp!,{r4-r11,pc} +#else + ldmia sp!,{r4-r11,lr} + tst lr,#1 + moveq pc,lr @ be binary compatible with V4, yet + bx lr @ interoperable with Thumb ISA:-) +#endif +.size sha256_block_data_order,.-sha256_block_data_order +___ +###################################################################### +# NEON stuff +# +{{{ +my @X=map("q$_",(0..3)); +my ($T0,$T1,$T2,$T3,$T4,$T5)=("q8","q9","q10","q11","d24","d25"); +my $Xfer=$t4; +my $j=0; + +sub Dlo() { shift=~m|q([1]?[0-9])|?"d".($1*2):""; } +sub Dhi() { shift=~m|q([1]?[0-9])|?"d".($1*2+1):""; } + +sub AUTOLOAD() # thunk [simplified] x86-style perlasm +{ my $opcode = $AUTOLOAD; $opcode =~ s/.*:://; $opcode =~ s/_/\./; + my $arg = pop; + $arg = "#$arg" if ($arg*1 eq $arg); + $code .= "\t$opcode\t".join(',',@_,$arg)."\n"; +} + +sub Xupdate() +{ use integer; + my $body = shift; + my @insns = (&$body,&$body,&$body,&$body); + my ($a,$b,$c,$d,$e,$f,$g,$h); + + &vext_8 ($T0,@X[0],@X[1],4); # X[1..4] + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + &vext_8 ($T1,@X[2],@X[3],4); # X[9..12] + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T2,$T0,$sigma0[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 (@X[0],@X[0],$T1); # X[0..3] += X[9..12] + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T1,$T0,$sigma0[2]); + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T2,$T0,32-$sigma0[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T3,$T0,$sigma0[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T1,$T1,$T2); + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T3,$T0,32-$sigma0[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T4,&Dhi(@X[3]),$sigma1[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T1,$T1,$T3); # sigma0(X[1..4]) + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T4,&Dhi(@X[3]),32-$sigma1[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T5,&Dhi(@X[3]),$sigma1[2]); + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 (@X[0],@X[0],$T1); # X[0..3] += sigma0(X[1..4]) + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T5,$T5,$T4); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T4,&Dhi(@X[3]),$sigma1[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T4,&Dhi(@X[3]),32-$sigma1[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T5,$T5,$T4); # sigma1(X[14..15]) + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 (&Dlo(@X[0]),&Dlo(@X[0]),$T5);# X[0..1] += sigma1(X[14..15]) + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T4,&Dlo(@X[0]),$sigma1[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T4,&Dlo(@X[0]),32-$sigma1[0]); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T5,&Dlo(@X[0]),$sigma1[2]); + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T5,$T5,$T4); + eval(shift(@insns)); + eval(shift(@insns)); + &vshr_u32 ($T4,&Dlo(@X[0]),$sigma1[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &vld1_32 ("{$T0}","[$Ktbl,:128]!"); + eval(shift(@insns)); + eval(shift(@insns)); + &vsli_32 ($T4,&Dlo(@X[0]),32-$sigma1[1]); + eval(shift(@insns)); + eval(shift(@insns)); + &veor ($T5,$T5,$T4); # sigma1(X[16..17]) + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 (&Dhi(@X[0]),&Dhi(@X[0]),$T5);# X[2..3] += sigma1(X[16..17]) + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 ($T0,$T0,@X[0]); + while($#insns>=2) { eval(shift(@insns)); } + &vst1_32 ("{$T0}","[$Xfer,:128]!"); + eval(shift(@insns)); + eval(shift(@insns)); + + push(@X,shift(@X)); # "rotate" X[] +} + +sub Xpreload() +{ use integer; + my $body = shift; + my @insns = (&$body,&$body,&$body,&$body); + my ($a,$b,$c,$d,$e,$f,$g,$h); + + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + &vld1_32 ("{$T0}","[$Ktbl,:128]!"); + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + &vrev32_8 (@X[0],@X[0]); + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + eval(shift(@insns)); + &vadd_i32 ($T0,$T0,@X[0]); + foreach (@insns) { eval; } # remaining instructions + &vst1_32 ("{$T0}","[$Xfer,:128]!"); + + push(@X,shift(@X)); # "rotate" X[] +} + +sub body_00_15 () { + ( + '($a,$b,$c,$d,$e,$f,$g,$h)=@V;'. + '&add ($h,$h,$t1)', # h+=X[i]+K[i] + '&eor ($t1,$f,$g)', + '&eor ($t0,$e,$e,"ror#".($Sigma1[1]-$Sigma1[0]))', + '&add ($a,$a,$t2)', # h+=Maj(a,b,c) from the past + '&and ($t1,$t1,$e)', + '&eor ($t2,$t0,$e,"ror#".($Sigma1[2]-$Sigma1[0]))', # Sigma1(e) + '&eor ($t0,$a,$a,"ror#".($Sigma0[1]-$Sigma0[0]))', + '&eor ($t1,$t1,$g)', # Ch(e,f,g) + '&add ($h,$h,$t2,"ror#$Sigma1[0]")', # h+=Sigma1(e) + '&eor ($t2,$a,$b)', # a^b, b^c in next round + '&eor ($t0,$t0,$a,"ror#".($Sigma0[2]-$Sigma0[0]))', # Sigma0(a) + '&add ($h,$h,$t1)', # h+=Ch(e,f,g) + '&ldr ($t1,sprintf "[sp,#%d]",4*(($j+1)&15)) if (($j&15)!=15);'. + '&ldr ($t1,"[$Ktbl]") if ($j==15);'. + '&ldr ($t1,"[sp,#64]") if ($j==31)', + '&and ($t3,$t3,$t2)', # (b^c)&=(a^b) + '&add ($d,$d,$h)', # d+=h + '&add ($h,$h,$t0,"ror#$Sigma0[0]");'. # h+=Sigma0(a) + '&eor ($t3,$t3,$b)', # Maj(a,b,c) + '$j++; unshift(@V,pop(@V)); ($t2,$t3)=($t3,$t2);' + ) +} + +$code.=<<___; +#if __ARM_MAX_ARCH__>=7 +.arch armv7-a +.fpu neon + +.global sha256_block_data_order_neon +.type sha256_block_data_order_neon,%function +.align 4 +sha256_block_data_order_neon: +.LNEON: + stmdb sp!,{r4-r12,lr} + + sub $H,sp,#16*4+16 + adrl $Ktbl,K256 + bic $H,$H,#15 @ align for 128-bit stores + mov $t2,sp + mov sp,$H @ alloca + add $len,$inp,$len,lsl#6 @ len to point at the end of inp + + vld1.8 {@X[0]},[$inp]! + vld1.8 {@X[1]},[$inp]! + vld1.8 {@X[2]},[$inp]! + vld1.8 {@X[3]},[$inp]! + vld1.32 {$T0},[$Ktbl,:128]! + vld1.32 {$T1},[$Ktbl,:128]! + vld1.32 {$T2},[$Ktbl,:128]! + vld1.32 {$T3},[$Ktbl,:128]! + vrev32.8 @X[0],@X[0] @ yes, even on + str $ctx,[sp,#64] + vrev32.8 @X[1],@X[1] @ big-endian + str $inp,[sp,#68] + mov $Xfer,sp + vrev32.8 @X[2],@X[2] + str $len,[sp,#72] + vrev32.8 @X[3],@X[3] + str $t2,[sp,#76] @ save original sp + vadd.i32 $T0,$T0,@X[0] + vadd.i32 $T1,$T1,@X[1] + vst1.32 {$T0},[$Xfer,:128]! + vadd.i32 $T2,$T2,@X[2] + vst1.32 {$T1},[$Xfer,:128]! + vadd.i32 $T3,$T3,@X[3] + vst1.32 {$T2},[$Xfer,:128]! + vst1.32 {$T3},[$Xfer,:128]! + + ldmia $ctx,{$A-$H} + sub $Xfer,$Xfer,#64 + ldr $t1,[sp,#0] + eor $t2,$t2,$t2 + eor $t3,$B,$C + b .L_00_48 + +.align 4 +.L_00_48: +___ + &Xupdate(\&body_00_15); + &Xupdate(\&body_00_15); + &Xupdate(\&body_00_15); + &Xupdate(\&body_00_15); +$code.=<<___; + teq $t1,#0 @ check for K256 terminator + ldr $t1,[sp,#0] + sub $Xfer,$Xfer,#64 + bne .L_00_48 + + ldr $inp,[sp,#68] + ldr $t0,[sp,#72] + sub $Ktbl,$Ktbl,#256 @ rewind $Ktbl + teq $inp,$t0 + it eq + subeq $inp,$inp,#64 @ avoid SEGV + vld1.8 {@X[0]},[$inp]! @ load next input block + vld1.8 {@X[1]},[$inp]! + vld1.8 {@X[2]},[$inp]! + vld1.8 {@X[3]},[$inp]! + it ne + strne $inp,[sp,#68] + mov $Xfer,sp +___ + &Xpreload(\&body_00_15); + &Xpreload(\&body_00_15); + &Xpreload(\&body_00_15); + &Xpreload(\&body_00_15); +$code.=<<___; + ldr $t0,[$t1,#0] + add $A,$A,$t2 @ h+=Maj(a,b,c) from the past + ldr $t2,[$t1,#4] + ldr $t3,[$t1,#8] + ldr $t4,[$t1,#12] + add $A,$A,$t0 @ accumulate + ldr $t0,[$t1,#16] + add $B,$B,$t2 + ldr $t2,[$t1,#20] + add $C,$C,$t3 + ldr $t3,[$t1,#24] + add $D,$D,$t4 + ldr $t4,[$t1,#28] + add $E,$E,$t0 + str $A,[$t1],#4 + add $F,$F,$t2 + str $B,[$t1],#4 + add $G,$G,$t3 + str $C,[$t1],#4 + add $H,$H,$t4 + str $D,[$t1],#4 + stmia $t1,{$E-$H} + + ittte ne + movne $Xfer,sp + ldrne $t1,[sp,#0] + eorne $t2,$t2,$t2 + ldreq sp,[sp,#76] @ restore original sp + itt ne + eorne $t3,$B,$C + bne .L_00_48 + + ldmia sp!,{r4-r12,pc} +.size sha256_block_data_order_neon,.-sha256_block_data_order_neon +#endif +___ +}}} +###################################################################### +# ARMv8 stuff +# +{{{ +my ($ABCD,$EFGH,$abcd)=map("q$_",(0..2)); +my @MSG=map("q$_",(8..11)); +my ($W0,$W1,$ABCD_SAVE,$EFGH_SAVE)=map("q$_",(12..15)); +my $Ktbl="r3"; + +$code.=<<___; +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) + +# ifdef __thumb2__ +# define INST(a,b,c,d) .byte c,d|0xc,a,b +# else +# define INST(a,b,c,d) .byte a,b,c,d +# endif + +.type sha256_block_data_order_armv8,%function +.align 5 +sha256_block_data_order_armv8: +.LARMv8: + vld1.32 {$ABCD,$EFGH},[$ctx] +# ifdef __thumb2__ + adr $Ktbl,.LARMv8 + sub $Ktbl,$Ktbl,#.LARMv8-K256 +# else + adrl $Ktbl,K256 +# endif + add $len,$inp,$len,lsl#6 @ len to point at the end of inp + +.Loop_v8: + vld1.8 {@MSG[0]-@MSG[1]},[$inp]! + vld1.8 {@MSG[2]-@MSG[3]},[$inp]! + vld1.32 {$W0},[$Ktbl]! + vrev32.8 @MSG[0],@MSG[0] + vrev32.8 @MSG[1],@MSG[1] + vrev32.8 @MSG[2],@MSG[2] + vrev32.8 @MSG[3],@MSG[3] + vmov $ABCD_SAVE,$ABCD @ offload + vmov $EFGH_SAVE,$EFGH + teq $inp,$len +___ +for($i=0;$i<12;$i++) { +$code.=<<___; + vld1.32 {$W1},[$Ktbl]! + vadd.i32 $W0,$W0,@MSG[0] + sha256su0 @MSG[0],@MSG[1] + vmov $abcd,$ABCD + sha256h $ABCD,$EFGH,$W0 + sha256h2 $EFGH,$abcd,$W0 + sha256su1 @MSG[0],@MSG[2],@MSG[3] +___ + ($W0,$W1)=($W1,$W0); push(@MSG,shift(@MSG)); +} +$code.=<<___; + vld1.32 {$W1},[$Ktbl]! + vadd.i32 $W0,$W0,@MSG[0] + vmov $abcd,$ABCD + sha256h $ABCD,$EFGH,$W0 + sha256h2 $EFGH,$abcd,$W0 + + vld1.32 {$W0},[$Ktbl]! + vadd.i32 $W1,$W1,@MSG[1] + vmov $abcd,$ABCD + sha256h $ABCD,$EFGH,$W1 + sha256h2 $EFGH,$abcd,$W1 + + vld1.32 {$W1},[$Ktbl] + vadd.i32 $W0,$W0,@MSG[2] + sub $Ktbl,$Ktbl,#256-16 @ rewind + vmov $abcd,$ABCD + sha256h $ABCD,$EFGH,$W0 + sha256h2 $EFGH,$abcd,$W0 + + vadd.i32 $W1,$W1,@MSG[3] + vmov $abcd,$ABCD + sha256h $ABCD,$EFGH,$W1 + sha256h2 $EFGH,$abcd,$W1 + + vadd.i32 $ABCD,$ABCD,$ABCD_SAVE + vadd.i32 $EFGH,$EFGH,$EFGH_SAVE + it ne + bne .Loop_v8 + + vst1.32 {$ABCD,$EFGH},[$ctx] + + ret @ bx lr +.size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 +#endif +___ +}}} +$code.=<<___; +.asciz "SHA256 block transform for ARMv4/NEON/ARMv8, CRYPTOGAMS by <appro\@openssl.org>" +.align 2 +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +.comm OPENSSL_armcap_P,4,4 +#endif +___ + +open SELF,$0; +while(<SELF>) { + next if (/^#!/); + last if (!s/^#/@/ and !/^$/); + print; +} +close SELF; + +{ my %opcode = ( + "sha256h" => 0xf3000c40, "sha256h2" => 0xf3100c40, + "sha256su0" => 0xf3ba03c0, "sha256su1" => 0xf3200c40 ); + + sub unsha256 { + my ($mnemonic,$arg)=@_; + + if ($arg =~ m/q([0-9]+)(?:,\s*q([0-9]+))?,\s*q([0-9]+)/o) { + my $word = $opcode{$mnemonic}|(($1&7)<<13)|(($1&8)<<19) + |(($2&7)<<17)|(($2&8)<<4) + |(($3&7)<<1) |(($3&8)<<2); + # since ARMv7 instructions are always encoded little-endian. + # correct solution is to use .inst directive, but older + # assemblers don't implement it:-( + sprintf "INST(0x%02x,0x%02x,0x%02x,0x%02x)\t@ %s %s", + $word&0xff,($word>>8)&0xff, + ($word>>16)&0xff,($word>>24)&0xff, + $mnemonic,$arg; + } + } +} + +foreach (split($/,$code)) { + + s/\`([^\`]*)\`/eval $1/geo; + + s/\b(sha256\w+)\s+(q.*)/unsha256($1,$2)/geo; + + s/\bret\b/bx lr/go or + s/\bbx\s+lr\b/.word\t0xe12fff1e/go; # make it possible to compile with -march=armv4 + + print $_,"\n"; +} + +close STDOUT; # enforce flush diff --git a/arch/arm/crypto/sha256-core.S_shipped b/arch/arm/crypto/sha256-core.S_shipped new file mode 100644 index 000000000000..555a1a8eec90 --- /dev/null +++ b/arch/arm/crypto/sha256-core.S_shipped @@ -0,0 +1,2808 @@ + +@ ==================================================================== +@ Written by Andy Polyakov <appro@openssl.org> for the OpenSSL +@ project. The module is, however, dual licensed under OpenSSL and +@ CRYPTOGAMS licenses depending on where you obtain it. For further +@ details see http://www.openssl.org/~appro/cryptogams/. +@ +@ Permission to use under GPL terms is granted. +@ ==================================================================== + +@ SHA256 block procedure for ARMv4. May 2007. + +@ Performance is ~2x better than gcc 3.4 generated code and in "abso- +@ lute" terms is ~2250 cycles per 64-byte block or ~35 cycles per +@ byte [on single-issue Xscale PXA250 core]. + +@ July 2010. +@ +@ Rescheduling for dual-issue pipeline resulted in 22% improvement on +@ Cortex A8 core and ~20 cycles per processed byte. + +@ February 2011. +@ +@ Profiler-assisted and platform-specific optimization resulted in 16% +@ improvement on Cortex A8 core and ~15.4 cycles per processed byte. + +@ September 2013. +@ +@ Add NEON implementation. On Cortex A8 it was measured to process one +@ byte in 12.5 cycles or 23% faster than integer-only code. Snapdragon +@ S4 does it in 12.5 cycles too, but it's 50% faster than integer-only +@ code (meaning that latter performs sub-optimally, nothing was done +@ about it). + +@ May 2014. +@ +@ Add ARMv8 code path performing at 2.0 cpb on Apple A7. + +#ifndef __KERNEL__ +# include "arm_arch.h" +#else +# define __ARM_ARCH__ __LINUX_ARM_ARCH__ +# define __ARM_MAX_ARCH__ 7 +#endif + +.text +#if __ARM_ARCH__<7 +.code 32 +#else +.syntax unified +# ifdef __thumb2__ +# define adrl adr +.thumb +# else +.code 32 +# endif +#endif + +.type K256,%object +.align 5 +K256: +.word 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 +.word 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 +.word 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 +.word 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 +.word 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc +.word 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da +.word 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 +.word 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 +.word 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 +.word 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 +.word 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 +.word 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 +.word 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 +.word 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 +.word 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 +.word 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +.size K256,.-K256 +.word 0 @ terminator +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +.LOPENSSL_armcap: +.word OPENSSL_armcap_P-sha256_block_data_order +#endif +.align 5 + +.global sha256_block_data_order +.type sha256_block_data_order,%function +sha256_block_data_order: +#if __ARM_ARCH__<7 + sub r3,pc,#8 @ sha256_block_data_order +#else + adr r3,sha256_block_data_order +#endif +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) + ldr r12,.LOPENSSL_armcap + ldr r12,[r3,r12] @ OPENSSL_armcap_P + tst r12,#ARMV8_SHA256 + bne .LARMv8 + tst r12,#ARMV7_NEON + bne .LNEON +#endif + add r2,r1,r2,lsl#6 @ len to point at the end of inp + stmdb sp!,{r0,r1,r2,r4-r11,lr} + ldmia r0,{r4,r5,r6,r7,r8,r9,r10,r11} + sub r14,r3,#256+32 @ K256 + sub sp,sp,#16*4 @ alloca(X[16]) +.Loop: +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 +# else + ldrb r2,[r1,#3] +# endif + eor r3,r5,r6 @ magic + eor r12,r12,r12 +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 0 +# if 0==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r8,r8,ror#5 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r8,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 0 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 0==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r8,r8,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r8,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r11,r11,r2 @ h+=X[i] + str r2,[sp,#0*4] + eor r2,r9,r10 + add r11,r11,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r8 + add r11,r11,r12 @ h+=K256[i] + eor r2,r2,r10 @ Ch(e,f,g) + eor r0,r4,r4,ror#11 + add r11,r11,r2 @ h+=Ch(e,f,g) +#if 0==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 0<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r4,r5 @ a^b, b^c in next round +#else + ldr r2,[sp,#2*4] @ from future BODY_16_xx + eor r12,r4,r5 @ a^b, b^c in next round + ldr r1,[sp,#15*4] @ from future BODY_16_xx +#endif + eor r0,r0,r4,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r7,r7,r11 @ d+=h + eor r3,r3,r5 @ Maj(a,b,c) + add r11,r11,r0,ror#2 @ h+=Sigma0(a) + @ add r11,r11,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 1 +# if 1==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r7,r7,ror#5 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r7,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 1 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 1==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r7,r7,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r7,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r10,r10,r2 @ h+=X[i] + str r2,[sp,#1*4] + eor r2,r8,r9 + add r10,r10,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r7 + add r10,r10,r3 @ h+=K256[i] + eor r2,r2,r9 @ Ch(e,f,g) + eor r0,r11,r11,ror#11 + add r10,r10,r2 @ h+=Ch(e,f,g) +#if 1==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 1<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r11,r4 @ a^b, b^c in next round +#else + ldr r2,[sp,#3*4] @ from future BODY_16_xx + eor r3,r11,r4 @ a^b, b^c in next round + ldr r1,[sp,#0*4] @ from future BODY_16_xx +#endif + eor r0,r0,r11,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r6,r6,r10 @ d+=h + eor r12,r12,r4 @ Maj(a,b,c) + add r10,r10,r0,ror#2 @ h+=Sigma0(a) + @ add r10,r10,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 2 +# if 2==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r6,r6,ror#5 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r6,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 2 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 2==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r6,r6,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r6,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r9,r9,r2 @ h+=X[i] + str r2,[sp,#2*4] + eor r2,r7,r8 + add r9,r9,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r6 + add r9,r9,r12 @ h+=K256[i] + eor r2,r2,r8 @ Ch(e,f,g) + eor r0,r10,r10,ror#11 + add r9,r9,r2 @ h+=Ch(e,f,g) +#if 2==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 2<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r10,r11 @ a^b, b^c in next round +#else + ldr r2,[sp,#4*4] @ from future BODY_16_xx + eor r12,r10,r11 @ a^b, b^c in next round + ldr r1,[sp,#1*4] @ from future BODY_16_xx +#endif + eor r0,r0,r10,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r5,r5,r9 @ d+=h + eor r3,r3,r11 @ Maj(a,b,c) + add r9,r9,r0,ror#2 @ h+=Sigma0(a) + @ add r9,r9,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 3 +# if 3==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r5,r5,ror#5 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r5,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 3 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 3==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r5,r5,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r5,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r8,r8,r2 @ h+=X[i] + str r2,[sp,#3*4] + eor r2,r6,r7 + add r8,r8,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r5 + add r8,r8,r3 @ h+=K256[i] + eor r2,r2,r7 @ Ch(e,f,g) + eor r0,r9,r9,ror#11 + add r8,r8,r2 @ h+=Ch(e,f,g) +#if 3==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 3<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r9,r10 @ a^b, b^c in next round +#else + ldr r2,[sp,#5*4] @ from future BODY_16_xx + eor r3,r9,r10 @ a^b, b^c in next round + ldr r1,[sp,#2*4] @ from future BODY_16_xx +#endif + eor r0,r0,r9,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r4,r4,r8 @ d+=h + eor r12,r12,r10 @ Maj(a,b,c) + add r8,r8,r0,ror#2 @ h+=Sigma0(a) + @ add r8,r8,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 4 +# if 4==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r4,r4,ror#5 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r4,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 4 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 4==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r4,r4,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r4,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r7,r7,r2 @ h+=X[i] + str r2,[sp,#4*4] + eor r2,r5,r6 + add r7,r7,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r4 + add r7,r7,r12 @ h+=K256[i] + eor r2,r2,r6 @ Ch(e,f,g) + eor r0,r8,r8,ror#11 + add r7,r7,r2 @ h+=Ch(e,f,g) +#if 4==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 4<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r8,r9 @ a^b, b^c in next round +#else + ldr r2,[sp,#6*4] @ from future BODY_16_xx + eor r12,r8,r9 @ a^b, b^c in next round + ldr r1,[sp,#3*4] @ from future BODY_16_xx +#endif + eor r0,r0,r8,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r11,r11,r7 @ d+=h + eor r3,r3,r9 @ Maj(a,b,c) + add r7,r7,r0,ror#2 @ h+=Sigma0(a) + @ add r7,r7,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 5 +# if 5==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r11,r11,ror#5 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r11,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 5 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 5==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r11,r11,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r11,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r6,r6,r2 @ h+=X[i] + str r2,[sp,#5*4] + eor r2,r4,r5 + add r6,r6,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r11 + add r6,r6,r3 @ h+=K256[i] + eor r2,r2,r5 @ Ch(e,f,g) + eor r0,r7,r7,ror#11 + add r6,r6,r2 @ h+=Ch(e,f,g) +#if 5==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 5<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r7,r8 @ a^b, b^c in next round +#else + ldr r2,[sp,#7*4] @ from future BODY_16_xx + eor r3,r7,r8 @ a^b, b^c in next round + ldr r1,[sp,#4*4] @ from future BODY_16_xx +#endif + eor r0,r0,r7,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r10,r10,r6 @ d+=h + eor r12,r12,r8 @ Maj(a,b,c) + add r6,r6,r0,ror#2 @ h+=Sigma0(a) + @ add r6,r6,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 6 +# if 6==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r10,r10,ror#5 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r10,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 6 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 6==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r10,r10,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r10,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r5,r5,r2 @ h+=X[i] + str r2,[sp,#6*4] + eor r2,r11,r4 + add r5,r5,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r10 + add r5,r5,r12 @ h+=K256[i] + eor r2,r2,r4 @ Ch(e,f,g) + eor r0,r6,r6,ror#11 + add r5,r5,r2 @ h+=Ch(e,f,g) +#if 6==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 6<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r6,r7 @ a^b, b^c in next round +#else + ldr r2,[sp,#8*4] @ from future BODY_16_xx + eor r12,r6,r7 @ a^b, b^c in next round + ldr r1,[sp,#5*4] @ from future BODY_16_xx +#endif + eor r0,r0,r6,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r9,r9,r5 @ d+=h + eor r3,r3,r7 @ Maj(a,b,c) + add r5,r5,r0,ror#2 @ h+=Sigma0(a) + @ add r5,r5,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 7 +# if 7==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r9,r9,ror#5 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r9,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 7 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 7==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r9,r9,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r9,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r4,r4,r2 @ h+=X[i] + str r2,[sp,#7*4] + eor r2,r10,r11 + add r4,r4,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r9 + add r4,r4,r3 @ h+=K256[i] + eor r2,r2,r11 @ Ch(e,f,g) + eor r0,r5,r5,ror#11 + add r4,r4,r2 @ h+=Ch(e,f,g) +#if 7==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 7<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r5,r6 @ a^b, b^c in next round +#else + ldr r2,[sp,#9*4] @ from future BODY_16_xx + eor r3,r5,r6 @ a^b, b^c in next round + ldr r1,[sp,#6*4] @ from future BODY_16_xx +#endif + eor r0,r0,r5,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r8,r8,r4 @ d+=h + eor r12,r12,r6 @ Maj(a,b,c) + add r4,r4,r0,ror#2 @ h+=Sigma0(a) + @ add r4,r4,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 8 +# if 8==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r8,r8,ror#5 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r8,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 8 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 8==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r8,r8,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r8,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r11,r11,r2 @ h+=X[i] + str r2,[sp,#8*4] + eor r2,r9,r10 + add r11,r11,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r8 + add r11,r11,r12 @ h+=K256[i] + eor r2,r2,r10 @ Ch(e,f,g) + eor r0,r4,r4,ror#11 + add r11,r11,r2 @ h+=Ch(e,f,g) +#if 8==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 8<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r4,r5 @ a^b, b^c in next round +#else + ldr r2,[sp,#10*4] @ from future BODY_16_xx + eor r12,r4,r5 @ a^b, b^c in next round + ldr r1,[sp,#7*4] @ from future BODY_16_xx +#endif + eor r0,r0,r4,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r7,r7,r11 @ d+=h + eor r3,r3,r5 @ Maj(a,b,c) + add r11,r11,r0,ror#2 @ h+=Sigma0(a) + @ add r11,r11,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 9 +# if 9==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r7,r7,ror#5 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r7,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 9 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 9==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r7,r7,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r7,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r10,r10,r2 @ h+=X[i] + str r2,[sp,#9*4] + eor r2,r8,r9 + add r10,r10,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r7 + add r10,r10,r3 @ h+=K256[i] + eor r2,r2,r9 @ Ch(e,f,g) + eor r0,r11,r11,ror#11 + add r10,r10,r2 @ h+=Ch(e,f,g) +#if 9==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 9<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r11,r4 @ a^b, b^c in next round +#else + ldr r2,[sp,#11*4] @ from future BODY_16_xx + eor r3,r11,r4 @ a^b, b^c in next round + ldr r1,[sp,#8*4] @ from future BODY_16_xx +#endif + eor r0,r0,r11,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r6,r6,r10 @ d+=h + eor r12,r12,r4 @ Maj(a,b,c) + add r10,r10,r0,ror#2 @ h+=Sigma0(a) + @ add r10,r10,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 10 +# if 10==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r6,r6,ror#5 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r6,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 10 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 10==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r6,r6,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r6,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r9,r9,r2 @ h+=X[i] + str r2,[sp,#10*4] + eor r2,r7,r8 + add r9,r9,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r6 + add r9,r9,r12 @ h+=K256[i] + eor r2,r2,r8 @ Ch(e,f,g) + eor r0,r10,r10,ror#11 + add r9,r9,r2 @ h+=Ch(e,f,g) +#if 10==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 10<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r10,r11 @ a^b, b^c in next round +#else + ldr r2,[sp,#12*4] @ from future BODY_16_xx + eor r12,r10,r11 @ a^b, b^c in next round + ldr r1,[sp,#9*4] @ from future BODY_16_xx +#endif + eor r0,r0,r10,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r5,r5,r9 @ d+=h + eor r3,r3,r11 @ Maj(a,b,c) + add r9,r9,r0,ror#2 @ h+=Sigma0(a) + @ add r9,r9,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 11 +# if 11==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r5,r5,ror#5 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r5,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 11 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 11==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r5,r5,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r5,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r8,r8,r2 @ h+=X[i] + str r2,[sp,#11*4] + eor r2,r6,r7 + add r8,r8,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r5 + add r8,r8,r3 @ h+=K256[i] + eor r2,r2,r7 @ Ch(e,f,g) + eor r0,r9,r9,ror#11 + add r8,r8,r2 @ h+=Ch(e,f,g) +#if 11==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 11<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r9,r10 @ a^b, b^c in next round +#else + ldr r2,[sp,#13*4] @ from future BODY_16_xx + eor r3,r9,r10 @ a^b, b^c in next round + ldr r1,[sp,#10*4] @ from future BODY_16_xx +#endif + eor r0,r0,r9,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r4,r4,r8 @ d+=h + eor r12,r12,r10 @ Maj(a,b,c) + add r8,r8,r0,ror#2 @ h+=Sigma0(a) + @ add r8,r8,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 12 +# if 12==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r4,r4,ror#5 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r4,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 12 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 12==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r4,r4,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r4,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r7,r7,r2 @ h+=X[i] + str r2,[sp,#12*4] + eor r2,r5,r6 + add r7,r7,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r4 + add r7,r7,r12 @ h+=K256[i] + eor r2,r2,r6 @ Ch(e,f,g) + eor r0,r8,r8,ror#11 + add r7,r7,r2 @ h+=Ch(e,f,g) +#if 12==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 12<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r8,r9 @ a^b, b^c in next round +#else + ldr r2,[sp,#14*4] @ from future BODY_16_xx + eor r12,r8,r9 @ a^b, b^c in next round + ldr r1,[sp,#11*4] @ from future BODY_16_xx +#endif + eor r0,r0,r8,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r11,r11,r7 @ d+=h + eor r3,r3,r9 @ Maj(a,b,c) + add r7,r7,r0,ror#2 @ h+=Sigma0(a) + @ add r7,r7,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 13 +# if 13==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r11,r11,ror#5 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r11,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 13 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 13==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r11,r11,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r11,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r6,r6,r2 @ h+=X[i] + str r2,[sp,#13*4] + eor r2,r4,r5 + add r6,r6,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r11 + add r6,r6,r3 @ h+=K256[i] + eor r2,r2,r5 @ Ch(e,f,g) + eor r0,r7,r7,ror#11 + add r6,r6,r2 @ h+=Ch(e,f,g) +#if 13==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 13<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r7,r8 @ a^b, b^c in next round +#else + ldr r2,[sp,#15*4] @ from future BODY_16_xx + eor r3,r7,r8 @ a^b, b^c in next round + ldr r1,[sp,#12*4] @ from future BODY_16_xx +#endif + eor r0,r0,r7,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r10,r10,r6 @ d+=h + eor r12,r12,r8 @ Maj(a,b,c) + add r6,r6,r0,ror#2 @ h+=Sigma0(a) + @ add r6,r6,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 14 +# if 14==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r10,r10,ror#5 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + eor r0,r0,r10,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 14 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + ldrb r12,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r12,lsl#8 + ldrb r12,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 14==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r10,r10,ror#5 + orr r2,r2,r12,lsl#24 + eor r0,r0,r10,ror#19 @ Sigma1(e) +#endif + ldr r12,[r14],#4 @ *K256++ + add r5,r5,r2 @ h+=X[i] + str r2,[sp,#14*4] + eor r2,r11,r4 + add r5,r5,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r10 + add r5,r5,r12 @ h+=K256[i] + eor r2,r2,r4 @ Ch(e,f,g) + eor r0,r6,r6,ror#11 + add r5,r5,r2 @ h+=Ch(e,f,g) +#if 14==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 14<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r6,r7 @ a^b, b^c in next round +#else + ldr r2,[sp,#0*4] @ from future BODY_16_xx + eor r12,r6,r7 @ a^b, b^c in next round + ldr r1,[sp,#13*4] @ from future BODY_16_xx +#endif + eor r0,r0,r6,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r9,r9,r5 @ d+=h + eor r3,r3,r7 @ Maj(a,b,c) + add r5,r5,r0,ror#2 @ h+=Sigma0(a) + @ add r5,r5,r3 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + @ ldr r2,[r1],#4 @ 15 +# if 15==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r9,r9,ror#5 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + eor r0,r0,r9,ror#19 @ Sigma1(e) +# ifndef __ARMEB__ + rev r2,r2 +# endif +#else + @ ldrb r2,[r1,#3] @ 15 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + ldrb r3,[r1,#2] + ldrb r0,[r1,#1] + orr r2,r2,r3,lsl#8 + ldrb r3,[r1],#4 + orr r2,r2,r0,lsl#16 +# if 15==15 + str r1,[sp,#17*4] @ make room for r1 +# endif + eor r0,r9,r9,ror#5 + orr r2,r2,r3,lsl#24 + eor r0,r0,r9,ror#19 @ Sigma1(e) +#endif + ldr r3,[r14],#4 @ *K256++ + add r4,r4,r2 @ h+=X[i] + str r2,[sp,#15*4] + eor r2,r10,r11 + add r4,r4,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r9 + add r4,r4,r3 @ h+=K256[i] + eor r2,r2,r11 @ Ch(e,f,g) + eor r0,r5,r5,ror#11 + add r4,r4,r2 @ h+=Ch(e,f,g) +#if 15==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 15<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r5,r6 @ a^b, b^c in next round +#else + ldr r2,[sp,#1*4] @ from future BODY_16_xx + eor r3,r5,r6 @ a^b, b^c in next round + ldr r1,[sp,#14*4] @ from future BODY_16_xx +#endif + eor r0,r0,r5,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r8,r8,r4 @ d+=h + eor r12,r12,r6 @ Maj(a,b,c) + add r4,r4,r0,ror#2 @ h+=Sigma0(a) + @ add r4,r4,r12 @ h+=Maj(a,b,c) +.Lrounds_16_xx: + @ ldr r2,[sp,#1*4] @ 16 + @ ldr r1,[sp,#14*4] + mov r0,r2,ror#7 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#0*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#9*4] + + add r12,r12,r0 + eor r0,r8,r8,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r8,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r11,r11,r2 @ h+=X[i] + str r2,[sp,#0*4] + eor r2,r9,r10 + add r11,r11,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r8 + add r11,r11,r12 @ h+=K256[i] + eor r2,r2,r10 @ Ch(e,f,g) + eor r0,r4,r4,ror#11 + add r11,r11,r2 @ h+=Ch(e,f,g) +#if 16==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 16<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r4,r5 @ a^b, b^c in next round +#else + ldr r2,[sp,#2*4] @ from future BODY_16_xx + eor r12,r4,r5 @ a^b, b^c in next round + ldr r1,[sp,#15*4] @ from future BODY_16_xx +#endif + eor r0,r0,r4,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r7,r7,r11 @ d+=h + eor r3,r3,r5 @ Maj(a,b,c) + add r11,r11,r0,ror#2 @ h+=Sigma0(a) + @ add r11,r11,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#2*4] @ 17 + @ ldr r1,[sp,#15*4] + mov r0,r2,ror#7 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#1*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#10*4] + + add r3,r3,r0 + eor r0,r7,r7,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r7,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r10,r10,r2 @ h+=X[i] + str r2,[sp,#1*4] + eor r2,r8,r9 + add r10,r10,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r7 + add r10,r10,r3 @ h+=K256[i] + eor r2,r2,r9 @ Ch(e,f,g) + eor r0,r11,r11,ror#11 + add r10,r10,r2 @ h+=Ch(e,f,g) +#if 17==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 17<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r11,r4 @ a^b, b^c in next round +#else + ldr r2,[sp,#3*4] @ from future BODY_16_xx + eor r3,r11,r4 @ a^b, b^c in next round + ldr r1,[sp,#0*4] @ from future BODY_16_xx +#endif + eor r0,r0,r11,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r6,r6,r10 @ d+=h + eor r12,r12,r4 @ Maj(a,b,c) + add r10,r10,r0,ror#2 @ h+=Sigma0(a) + @ add r10,r10,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#3*4] @ 18 + @ ldr r1,[sp,#0*4] + mov r0,r2,ror#7 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#2*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#11*4] + + add r12,r12,r0 + eor r0,r6,r6,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r6,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r9,r9,r2 @ h+=X[i] + str r2,[sp,#2*4] + eor r2,r7,r8 + add r9,r9,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r6 + add r9,r9,r12 @ h+=K256[i] + eor r2,r2,r8 @ Ch(e,f,g) + eor r0,r10,r10,ror#11 + add r9,r9,r2 @ h+=Ch(e,f,g) +#if 18==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 18<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r10,r11 @ a^b, b^c in next round +#else + ldr r2,[sp,#4*4] @ from future BODY_16_xx + eor r12,r10,r11 @ a^b, b^c in next round + ldr r1,[sp,#1*4] @ from future BODY_16_xx +#endif + eor r0,r0,r10,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r5,r5,r9 @ d+=h + eor r3,r3,r11 @ Maj(a,b,c) + add r9,r9,r0,ror#2 @ h+=Sigma0(a) + @ add r9,r9,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#4*4] @ 19 + @ ldr r1,[sp,#1*4] + mov r0,r2,ror#7 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#3*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#12*4] + + add r3,r3,r0 + eor r0,r5,r5,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r5,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r8,r8,r2 @ h+=X[i] + str r2,[sp,#3*4] + eor r2,r6,r7 + add r8,r8,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r5 + add r8,r8,r3 @ h+=K256[i] + eor r2,r2,r7 @ Ch(e,f,g) + eor r0,r9,r9,ror#11 + add r8,r8,r2 @ h+=Ch(e,f,g) +#if 19==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 19<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r9,r10 @ a^b, b^c in next round +#else + ldr r2,[sp,#5*4] @ from future BODY_16_xx + eor r3,r9,r10 @ a^b, b^c in next round + ldr r1,[sp,#2*4] @ from future BODY_16_xx +#endif + eor r0,r0,r9,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r4,r4,r8 @ d+=h + eor r12,r12,r10 @ Maj(a,b,c) + add r8,r8,r0,ror#2 @ h+=Sigma0(a) + @ add r8,r8,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#5*4] @ 20 + @ ldr r1,[sp,#2*4] + mov r0,r2,ror#7 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#4*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#13*4] + + add r12,r12,r0 + eor r0,r4,r4,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r4,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r7,r7,r2 @ h+=X[i] + str r2,[sp,#4*4] + eor r2,r5,r6 + add r7,r7,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r4 + add r7,r7,r12 @ h+=K256[i] + eor r2,r2,r6 @ Ch(e,f,g) + eor r0,r8,r8,ror#11 + add r7,r7,r2 @ h+=Ch(e,f,g) +#if 20==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 20<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r8,r9 @ a^b, b^c in next round +#else + ldr r2,[sp,#6*4] @ from future BODY_16_xx + eor r12,r8,r9 @ a^b, b^c in next round + ldr r1,[sp,#3*4] @ from future BODY_16_xx +#endif + eor r0,r0,r8,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r11,r11,r7 @ d+=h + eor r3,r3,r9 @ Maj(a,b,c) + add r7,r7,r0,ror#2 @ h+=Sigma0(a) + @ add r7,r7,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#6*4] @ 21 + @ ldr r1,[sp,#3*4] + mov r0,r2,ror#7 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#5*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#14*4] + + add r3,r3,r0 + eor r0,r11,r11,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r11,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r6,r6,r2 @ h+=X[i] + str r2,[sp,#5*4] + eor r2,r4,r5 + add r6,r6,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r11 + add r6,r6,r3 @ h+=K256[i] + eor r2,r2,r5 @ Ch(e,f,g) + eor r0,r7,r7,ror#11 + add r6,r6,r2 @ h+=Ch(e,f,g) +#if 21==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 21<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r7,r8 @ a^b, b^c in next round +#else + ldr r2,[sp,#7*4] @ from future BODY_16_xx + eor r3,r7,r8 @ a^b, b^c in next round + ldr r1,[sp,#4*4] @ from future BODY_16_xx +#endif + eor r0,r0,r7,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r10,r10,r6 @ d+=h + eor r12,r12,r8 @ Maj(a,b,c) + add r6,r6,r0,ror#2 @ h+=Sigma0(a) + @ add r6,r6,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#7*4] @ 22 + @ ldr r1,[sp,#4*4] + mov r0,r2,ror#7 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#6*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#15*4] + + add r12,r12,r0 + eor r0,r10,r10,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r10,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r5,r5,r2 @ h+=X[i] + str r2,[sp,#6*4] + eor r2,r11,r4 + add r5,r5,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r10 + add r5,r5,r12 @ h+=K256[i] + eor r2,r2,r4 @ Ch(e,f,g) + eor r0,r6,r6,ror#11 + add r5,r5,r2 @ h+=Ch(e,f,g) +#if 22==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 22<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r6,r7 @ a^b, b^c in next round +#else + ldr r2,[sp,#8*4] @ from future BODY_16_xx + eor r12,r6,r7 @ a^b, b^c in next round + ldr r1,[sp,#5*4] @ from future BODY_16_xx +#endif + eor r0,r0,r6,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r9,r9,r5 @ d+=h + eor r3,r3,r7 @ Maj(a,b,c) + add r5,r5,r0,ror#2 @ h+=Sigma0(a) + @ add r5,r5,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#8*4] @ 23 + @ ldr r1,[sp,#5*4] + mov r0,r2,ror#7 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#7*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#0*4] + + add r3,r3,r0 + eor r0,r9,r9,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r9,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r4,r4,r2 @ h+=X[i] + str r2,[sp,#7*4] + eor r2,r10,r11 + add r4,r4,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r9 + add r4,r4,r3 @ h+=K256[i] + eor r2,r2,r11 @ Ch(e,f,g) + eor r0,r5,r5,ror#11 + add r4,r4,r2 @ h+=Ch(e,f,g) +#if 23==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 23<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r5,r6 @ a^b, b^c in next round +#else + ldr r2,[sp,#9*4] @ from future BODY_16_xx + eor r3,r5,r6 @ a^b, b^c in next round + ldr r1,[sp,#6*4] @ from future BODY_16_xx +#endif + eor r0,r0,r5,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r8,r8,r4 @ d+=h + eor r12,r12,r6 @ Maj(a,b,c) + add r4,r4,r0,ror#2 @ h+=Sigma0(a) + @ add r4,r4,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#9*4] @ 24 + @ ldr r1,[sp,#6*4] + mov r0,r2,ror#7 + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#8*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#1*4] + + add r12,r12,r0 + eor r0,r8,r8,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r8,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r11,r11,r2 @ h+=X[i] + str r2,[sp,#8*4] + eor r2,r9,r10 + add r11,r11,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r8 + add r11,r11,r12 @ h+=K256[i] + eor r2,r2,r10 @ Ch(e,f,g) + eor r0,r4,r4,ror#11 + add r11,r11,r2 @ h+=Ch(e,f,g) +#if 24==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 24<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r4,r5 @ a^b, b^c in next round +#else + ldr r2,[sp,#10*4] @ from future BODY_16_xx + eor r12,r4,r5 @ a^b, b^c in next round + ldr r1,[sp,#7*4] @ from future BODY_16_xx +#endif + eor r0,r0,r4,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r7,r7,r11 @ d+=h + eor r3,r3,r5 @ Maj(a,b,c) + add r11,r11,r0,ror#2 @ h+=Sigma0(a) + @ add r11,r11,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#10*4] @ 25 + @ ldr r1,[sp,#7*4] + mov r0,r2,ror#7 + add r11,r11,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#9*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#2*4] + + add r3,r3,r0 + eor r0,r7,r7,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r7,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r10,r10,r2 @ h+=X[i] + str r2,[sp,#9*4] + eor r2,r8,r9 + add r10,r10,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r7 + add r10,r10,r3 @ h+=K256[i] + eor r2,r2,r9 @ Ch(e,f,g) + eor r0,r11,r11,ror#11 + add r10,r10,r2 @ h+=Ch(e,f,g) +#if 25==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 25<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r11,r4 @ a^b, b^c in next round +#else + ldr r2,[sp,#11*4] @ from future BODY_16_xx + eor r3,r11,r4 @ a^b, b^c in next round + ldr r1,[sp,#8*4] @ from future BODY_16_xx +#endif + eor r0,r0,r11,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r6,r6,r10 @ d+=h + eor r12,r12,r4 @ Maj(a,b,c) + add r10,r10,r0,ror#2 @ h+=Sigma0(a) + @ add r10,r10,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#11*4] @ 26 + @ ldr r1,[sp,#8*4] + mov r0,r2,ror#7 + add r10,r10,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#10*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#3*4] + + add r12,r12,r0 + eor r0,r6,r6,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r6,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r9,r9,r2 @ h+=X[i] + str r2,[sp,#10*4] + eor r2,r7,r8 + add r9,r9,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r6 + add r9,r9,r12 @ h+=K256[i] + eor r2,r2,r8 @ Ch(e,f,g) + eor r0,r10,r10,ror#11 + add r9,r9,r2 @ h+=Ch(e,f,g) +#if 26==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 26<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r10,r11 @ a^b, b^c in next round +#else + ldr r2,[sp,#12*4] @ from future BODY_16_xx + eor r12,r10,r11 @ a^b, b^c in next round + ldr r1,[sp,#9*4] @ from future BODY_16_xx +#endif + eor r0,r0,r10,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r5,r5,r9 @ d+=h + eor r3,r3,r11 @ Maj(a,b,c) + add r9,r9,r0,ror#2 @ h+=Sigma0(a) + @ add r9,r9,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#12*4] @ 27 + @ ldr r1,[sp,#9*4] + mov r0,r2,ror#7 + add r9,r9,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#11*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#4*4] + + add r3,r3,r0 + eor r0,r5,r5,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r5,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r8,r8,r2 @ h+=X[i] + str r2,[sp,#11*4] + eor r2,r6,r7 + add r8,r8,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r5 + add r8,r8,r3 @ h+=K256[i] + eor r2,r2,r7 @ Ch(e,f,g) + eor r0,r9,r9,ror#11 + add r8,r8,r2 @ h+=Ch(e,f,g) +#if 27==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 27<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r9,r10 @ a^b, b^c in next round +#else + ldr r2,[sp,#13*4] @ from future BODY_16_xx + eor r3,r9,r10 @ a^b, b^c in next round + ldr r1,[sp,#10*4] @ from future BODY_16_xx +#endif + eor r0,r0,r9,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r4,r4,r8 @ d+=h + eor r12,r12,r10 @ Maj(a,b,c) + add r8,r8,r0,ror#2 @ h+=Sigma0(a) + @ add r8,r8,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#13*4] @ 28 + @ ldr r1,[sp,#10*4] + mov r0,r2,ror#7 + add r8,r8,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#12*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#5*4] + + add r12,r12,r0 + eor r0,r4,r4,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r4,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r7,r7,r2 @ h+=X[i] + str r2,[sp,#12*4] + eor r2,r5,r6 + add r7,r7,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r4 + add r7,r7,r12 @ h+=K256[i] + eor r2,r2,r6 @ Ch(e,f,g) + eor r0,r8,r8,ror#11 + add r7,r7,r2 @ h+=Ch(e,f,g) +#if 28==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 28<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r8,r9 @ a^b, b^c in next round +#else + ldr r2,[sp,#14*4] @ from future BODY_16_xx + eor r12,r8,r9 @ a^b, b^c in next round + ldr r1,[sp,#11*4] @ from future BODY_16_xx +#endif + eor r0,r0,r8,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r11,r11,r7 @ d+=h + eor r3,r3,r9 @ Maj(a,b,c) + add r7,r7,r0,ror#2 @ h+=Sigma0(a) + @ add r7,r7,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#14*4] @ 29 + @ ldr r1,[sp,#11*4] + mov r0,r2,ror#7 + add r7,r7,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#13*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#6*4] + + add r3,r3,r0 + eor r0,r11,r11,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r11,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r6,r6,r2 @ h+=X[i] + str r2,[sp,#13*4] + eor r2,r4,r5 + add r6,r6,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r11 + add r6,r6,r3 @ h+=K256[i] + eor r2,r2,r5 @ Ch(e,f,g) + eor r0,r7,r7,ror#11 + add r6,r6,r2 @ h+=Ch(e,f,g) +#if 29==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 29<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r7,r8 @ a^b, b^c in next round +#else + ldr r2,[sp,#15*4] @ from future BODY_16_xx + eor r3,r7,r8 @ a^b, b^c in next round + ldr r1,[sp,#12*4] @ from future BODY_16_xx +#endif + eor r0,r0,r7,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r10,r10,r6 @ d+=h + eor r12,r12,r8 @ Maj(a,b,c) + add r6,r6,r0,ror#2 @ h+=Sigma0(a) + @ add r6,r6,r12 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#15*4] @ 30 + @ ldr r1,[sp,#12*4] + mov r0,r2,ror#7 + add r6,r6,r12 @ h+=Maj(a,b,c) from the past + mov r12,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r12,r12,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#14*4] + eor r12,r12,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#7*4] + + add r12,r12,r0 + eor r0,r10,r10,ror#5 @ from BODY_00_15 + add r2,r2,r12 + eor r0,r0,r10,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r12,[r14],#4 @ *K256++ + add r5,r5,r2 @ h+=X[i] + str r2,[sp,#14*4] + eor r2,r11,r4 + add r5,r5,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r10 + add r5,r5,r12 @ h+=K256[i] + eor r2,r2,r4 @ Ch(e,f,g) + eor r0,r6,r6,ror#11 + add r5,r5,r2 @ h+=Ch(e,f,g) +#if 30==31 + and r12,r12,#0xff + cmp r12,#0xf2 @ done? +#endif +#if 30<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r12,r6,r7 @ a^b, b^c in next round +#else + ldr r2,[sp,#0*4] @ from future BODY_16_xx + eor r12,r6,r7 @ a^b, b^c in next round + ldr r1,[sp,#13*4] @ from future BODY_16_xx +#endif + eor r0,r0,r6,ror#20 @ Sigma0(a) + and r3,r3,r12 @ (b^c)&=(a^b) + add r9,r9,r5 @ d+=h + eor r3,r3,r7 @ Maj(a,b,c) + add r5,r5,r0,ror#2 @ h+=Sigma0(a) + @ add r5,r5,r3 @ h+=Maj(a,b,c) + @ ldr r2,[sp,#0*4] @ 31 + @ ldr r1,[sp,#13*4] + mov r0,r2,ror#7 + add r5,r5,r3 @ h+=Maj(a,b,c) from the past + mov r3,r1,ror#17 + eor r0,r0,r2,ror#18 + eor r3,r3,r1,ror#19 + eor r0,r0,r2,lsr#3 @ sigma0(X[i+1]) + ldr r2,[sp,#15*4] + eor r3,r3,r1,lsr#10 @ sigma1(X[i+14]) + ldr r1,[sp,#8*4] + + add r3,r3,r0 + eor r0,r9,r9,ror#5 @ from BODY_00_15 + add r2,r2,r3 + eor r0,r0,r9,ror#19 @ Sigma1(e) + add r2,r2,r1 @ X[i] + ldr r3,[r14],#4 @ *K256++ + add r4,r4,r2 @ h+=X[i] + str r2,[sp,#15*4] + eor r2,r10,r11 + add r4,r4,r0,ror#6 @ h+=Sigma1(e) + and r2,r2,r9 + add r4,r4,r3 @ h+=K256[i] + eor r2,r2,r11 @ Ch(e,f,g) + eor r0,r5,r5,ror#11 + add r4,r4,r2 @ h+=Ch(e,f,g) +#if 31==31 + and r3,r3,#0xff + cmp r3,#0xf2 @ done? +#endif +#if 31<15 +# if __ARM_ARCH__>=7 + ldr r2,[r1],#4 @ prefetch +# else + ldrb r2,[r1,#3] +# endif + eor r3,r5,r6 @ a^b, b^c in next round +#else + ldr r2,[sp,#1*4] @ from future BODY_16_xx + eor r3,r5,r6 @ a^b, b^c in next round + ldr r1,[sp,#14*4] @ from future BODY_16_xx +#endif + eor r0,r0,r5,ror#20 @ Sigma0(a) + and r12,r12,r3 @ (b^c)&=(a^b) + add r8,r8,r4 @ d+=h + eor r12,r12,r6 @ Maj(a,b,c) + add r4,r4,r0,ror#2 @ h+=Sigma0(a) + @ add r4,r4,r12 @ h+=Maj(a,b,c) +#if __ARM_ARCH__>=7 + ite eq @ Thumb2 thing, sanity check in ARM +#endif + ldreq r3,[sp,#16*4] @ pull ctx + bne .Lrounds_16_xx + + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + ldr r0,[r3,#0] + ldr r2,[r3,#4] + ldr r12,[r3,#8] + add r4,r4,r0 + ldr r0,[r3,#12] + add r5,r5,r2 + ldr r2,[r3,#16] + add r6,r6,r12 + ldr r12,[r3,#20] + add r7,r7,r0 + ldr r0,[r3,#24] + add r8,r8,r2 + ldr r2,[r3,#28] + add r9,r9,r12 + ldr r1,[sp,#17*4] @ pull inp + ldr r12,[sp,#18*4] @ pull inp+len + add r10,r10,r0 + add r11,r11,r2 + stmia r3,{r4,r5,r6,r7,r8,r9,r10,r11} + cmp r1,r12 + sub r14,r14,#256 @ rewind Ktbl + bne .Loop + + add sp,sp,#19*4 @ destroy frame +#if __ARM_ARCH__>=5 + ldmia sp!,{r4-r11,pc} +#else + ldmia sp!,{r4-r11,lr} + tst lr,#1 + moveq pc,lr @ be binary compatible with V4, yet + .word 0xe12fff1e @ interoperable with Thumb ISA:-) +#endif +.size sha256_block_data_order,.-sha256_block_data_order +#if __ARM_MAX_ARCH__>=7 +.arch armv7-a +.fpu neon + +.global sha256_block_data_order_neon +.type sha256_block_data_order_neon,%function +.align 4 +sha256_block_data_order_neon: +.LNEON: + stmdb sp!,{r4-r12,lr} + + sub r11,sp,#16*4+16 + adrl r14,K256 + bic r11,r11,#15 @ align for 128-bit stores + mov r12,sp + mov sp,r11 @ alloca + add r2,r1,r2,lsl#6 @ len to point at the end of inp + + vld1.8 {q0},[r1]! + vld1.8 {q1},[r1]! + vld1.8 {q2},[r1]! + vld1.8 {q3},[r1]! + vld1.32 {q8},[r14,:128]! + vld1.32 {q9},[r14,:128]! + vld1.32 {q10},[r14,:128]! + vld1.32 {q11},[r14,:128]! + vrev32.8 q0,q0 @ yes, even on + str r0,[sp,#64] + vrev32.8 q1,q1 @ big-endian + str r1,[sp,#68] + mov r1,sp + vrev32.8 q2,q2 + str r2,[sp,#72] + vrev32.8 q3,q3 + str r12,[sp,#76] @ save original sp + vadd.i32 q8,q8,q0 + vadd.i32 q9,q9,q1 + vst1.32 {q8},[r1,:128]! + vadd.i32 q10,q10,q2 + vst1.32 {q9},[r1,:128]! + vadd.i32 q11,q11,q3 + vst1.32 {q10},[r1,:128]! + vst1.32 {q11},[r1,:128]! + + ldmia r0,{r4-r11} + sub r1,r1,#64 + ldr r2,[sp,#0] + eor r12,r12,r12 + eor r3,r5,r6 + b .L_00_48 + +.align 4 +.L_00_48: + vext.8 q8,q0,q1,#4 + add r11,r11,r2 + eor r2,r9,r10 + eor r0,r8,r8,ror#5 + vext.8 q9,q2,q3,#4 + add r4,r4,r12 + and r2,r2,r8 + eor r12,r0,r8,ror#19 + vshr.u32 q10,q8,#7 + eor r0,r4,r4,ror#11 + eor r2,r2,r10 + vadd.i32 q0,q0,q9 + add r11,r11,r12,ror#6 + eor r12,r4,r5 + vshr.u32 q9,q8,#3 + eor r0,r0,r4,ror#20 + add r11,r11,r2 + vsli.32 q10,q8,#25 + ldr r2,[sp,#4] + and r3,r3,r12 + vshr.u32 q11,q8,#18 + add r7,r7,r11 + add r11,r11,r0,ror#2 + eor r3,r3,r5 + veor q9,q9,q10 + add r10,r10,r2 + vsli.32 q11,q8,#14 + eor r2,r8,r9 + eor r0,r7,r7,ror#5 + vshr.u32 d24,d7,#17 + add r11,r11,r3 + and r2,r2,r7 + veor q9,q9,q11 + eor r3,r0,r7,ror#19 + eor r0,r11,r11,ror#11 + vsli.32 d24,d7,#15 + eor r2,r2,r9 + add r10,r10,r3,ror#6 + vshr.u32 d25,d7,#10 + eor r3,r11,r4 + eor r0,r0,r11,ror#20 + vadd.i32 q0,q0,q9 + add r10,r10,r2 + ldr r2,[sp,#8] + veor d25,d25,d24 + and r12,r12,r3 + add r6,r6,r10 + vshr.u32 d24,d7,#19 + add r10,r10,r0,ror#2 + eor r12,r12,r4 + vsli.32 d24,d7,#13 + add r9,r9,r2 + eor r2,r7,r8 + veor d25,d25,d24 + eor r0,r6,r6,ror#5 + add r10,r10,r12 + vadd.i32 d0,d0,d25 + and r2,r2,r6 + eor r12,r0,r6,ror#19 + vshr.u32 d24,d0,#17 + eor r0,r10,r10,ror#11 + eor r2,r2,r8 + vsli.32 d24,d0,#15 + add r9,r9,r12,ror#6 + eor r12,r10,r11 + vshr.u32 d25,d0,#10 + eor r0,r0,r10,ror#20 + add r9,r9,r2 + veor d25,d25,d24 + ldr r2,[sp,#12] + and r3,r3,r12 + vshr.u32 d24,d0,#19 + add r5,r5,r9 + add r9,r9,r0,ror#2 + eor r3,r3,r11 + vld1.32 {q8},[r14,:128]! + add r8,r8,r2 + vsli.32 d24,d0,#13 + eor r2,r6,r7 + eor r0,r5,r5,ror#5 + veor d25,d25,d24 + add r9,r9,r3 + and r2,r2,r5 + vadd.i32 d1,d1,d25 + eor r3,r0,r5,ror#19 + eor r0,r9,r9,ror#11 + vadd.i32 q8,q8,q0 + eor r2,r2,r7 + add r8,r8,r3,ror#6 + eor r3,r9,r10 + eor r0,r0,r9,ror#20 + add r8,r8,r2 + ldr r2,[sp,#16] + and r12,r12,r3 + add r4,r4,r8 + vst1.32 {q8},[r1,:128]! + add r8,r8,r0,ror#2 + eor r12,r12,r10 + vext.8 q8,q1,q2,#4 + add r7,r7,r2 + eor r2,r5,r6 + eor r0,r4,r4,ror#5 + vext.8 q9,q3,q0,#4 + add r8,r8,r12 + and r2,r2,r4 + eor r12,r0,r4,ror#19 + vshr.u32 q10,q8,#7 + eor r0,r8,r8,ror#11 + eor r2,r2,r6 + vadd.i32 q1,q1,q9 + add r7,r7,r12,ror#6 + eor r12,r8,r9 + vshr.u32 q9,q8,#3 + eor r0,r0,r8,ror#20 + add r7,r7,r2 + vsli.32 q10,q8,#25 + ldr r2,[sp,#20] + and r3,r3,r12 + vshr.u32 q11,q8,#18 + add r11,r11,r7 + add r7,r7,r0,ror#2 + eor r3,r3,r9 + veor q9,q9,q10 + add r6,r6,r2 + vsli.32 q11,q8,#14 + eor r2,r4,r5 + eor r0,r11,r11,ror#5 + vshr.u32 d24,d1,#17 + add r7,r7,r3 + and r2,r2,r11 + veor q9,q9,q11 + eor r3,r0,r11,ror#19 + eor r0,r7,r7,ror#11 + vsli.32 d24,d1,#15 + eor r2,r2,r5 + add r6,r6,r3,ror#6 + vshr.u32 d25,d1,#10 + eor r3,r7,r8 + eor r0,r0,r7,ror#20 + vadd.i32 q1,q1,q9 + add r6,r6,r2 + ldr r2,[sp,#24] + veor d25,d25,d24 + and r12,r12,r3 + add r10,r10,r6 + vshr.u32 d24,d1,#19 + add r6,r6,r0,ror#2 + eor r12,r12,r8 + vsli.32 d24,d1,#13 + add r5,r5,r2 + eor r2,r11,r4 + veor d25,d25,d24 + eor r0,r10,r10,ror#5 + add r6,r6,r12 + vadd.i32 d2,d2,d25 + and r2,r2,r10 + eor r12,r0,r10,ror#19 + vshr.u32 d24,d2,#17 + eor r0,r6,r6,ror#11 + eor r2,r2,r4 + vsli.32 d24,d2,#15 + add r5,r5,r12,ror#6 + eor r12,r6,r7 + vshr.u32 d25,d2,#10 + eor r0,r0,r6,ror#20 + add r5,r5,r2 + veor d25,d25,d24 + ldr r2,[sp,#28] + and r3,r3,r12 + vshr.u32 d24,d2,#19 + add r9,r9,r5 + add r5,r5,r0,ror#2 + eor r3,r3,r7 + vld1.32 {q8},[r14,:128]! + add r4,r4,r2 + vsli.32 d24,d2,#13 + eor r2,r10,r11 + eor r0,r9,r9,ror#5 + veor d25,d25,d24 + add r5,r5,r3 + and r2,r2,r9 + vadd.i32 d3,d3,d25 + eor r3,r0,r9,ror#19 + eor r0,r5,r5,ror#11 + vadd.i32 q8,q8,q1 + eor r2,r2,r11 + add r4,r4,r3,ror#6 + eor r3,r5,r6 + eor r0,r0,r5,ror#20 + add r4,r4,r2 + ldr r2,[sp,#32] + and r12,r12,r3 + add r8,r8,r4 + vst1.32 {q8},[r1,:128]! + add r4,r4,r0,ror#2 + eor r12,r12,r6 + vext.8 q8,q2,q3,#4 + add r11,r11,r2 + eor r2,r9,r10 + eor r0,r8,r8,ror#5 + vext.8 q9,q0,q1,#4 + add r4,r4,r12 + and r2,r2,r8 + eor r12,r0,r8,ror#19 + vshr.u32 q10,q8,#7 + eor r0,r4,r4,ror#11 + eor r2,r2,r10 + vadd.i32 q2,q2,q9 + add r11,r11,r12,ror#6 + eor r12,r4,r5 + vshr.u32 q9,q8,#3 + eor r0,r0,r4,ror#20 + add r11,r11,r2 + vsli.32 q10,q8,#25 + ldr r2,[sp,#36] + and r3,r3,r12 + vshr.u32 q11,q8,#18 + add r7,r7,r11 + add r11,r11,r0,ror#2 + eor r3,r3,r5 + veor q9,q9,q10 + add r10,r10,r2 + vsli.32 q11,q8,#14 + eor r2,r8,r9 + eor r0,r7,r7,ror#5 + vshr.u32 d24,d3,#17 + add r11,r11,r3 + and r2,r2,r7 + veor q9,q9,q11 + eor r3,r0,r7,ror#19 + eor r0,r11,r11,ror#11 + vsli.32 d24,d3,#15 + eor r2,r2,r9 + add r10,r10,r3,ror#6 + vshr.u32 d25,d3,#10 + eor r3,r11,r4 + eor r0,r0,r11,ror#20 + vadd.i32 q2,q2,q9 + add r10,r10,r2 + ldr r2,[sp,#40] + veor d25,d25,d24 + and r12,r12,r3 + add r6,r6,r10 + vshr.u32 d24,d3,#19 + add r10,r10,r0,ror#2 + eor r12,r12,r4 + vsli.32 d24,d3,#13 + add r9,r9,r2 + eor r2,r7,r8 + veor d25,d25,d24 + eor r0,r6,r6,ror#5 + add r10,r10,r12 + vadd.i32 d4,d4,d25 + and r2,r2,r6 + eor r12,r0,r6,ror#19 + vshr.u32 d24,d4,#17 + eor r0,r10,r10,ror#11 + eor r2,r2,r8 + vsli.32 d24,d4,#15 + add r9,r9,r12,ror#6 + eor r12,r10,r11 + vshr.u32 d25,d4,#10 + eor r0,r0,r10,ror#20 + add r9,r9,r2 + veor d25,d25,d24 + ldr r2,[sp,#44] + and r3,r3,r12 + vshr.u32 d24,d4,#19 + add r5,r5,r9 + add r9,r9,r0,ror#2 + eor r3,r3,r11 + vld1.32 {q8},[r14,:128]! + add r8,r8,r2 + vsli.32 d24,d4,#13 + eor r2,r6,r7 + eor r0,r5,r5,ror#5 + veor d25,d25,d24 + add r9,r9,r3 + and r2,r2,r5 + vadd.i32 d5,d5,d25 + eor r3,r0,r5,ror#19 + eor r0,r9,r9,ror#11 + vadd.i32 q8,q8,q2 + eor r2,r2,r7 + add r8,r8,r3,ror#6 + eor r3,r9,r10 + eor r0,r0,r9,ror#20 + add r8,r8,r2 + ldr r2,[sp,#48] + and r12,r12,r3 + add r4,r4,r8 + vst1.32 {q8},[r1,:128]! + add r8,r8,r0,ror#2 + eor r12,r12,r10 + vext.8 q8,q3,q0,#4 + add r7,r7,r2 + eor r2,r5,r6 + eor r0,r4,r4,ror#5 + vext.8 q9,q1,q2,#4 + add r8,r8,r12 + and r2,r2,r4 + eor r12,r0,r4,ror#19 + vshr.u32 q10,q8,#7 + eor r0,r8,r8,ror#11 + eor r2,r2,r6 + vadd.i32 q3,q3,q9 + add r7,r7,r12,ror#6 + eor r12,r8,r9 + vshr.u32 q9,q8,#3 + eor r0,r0,r8,ror#20 + add r7,r7,r2 + vsli.32 q10,q8,#25 + ldr r2,[sp,#52] + and r3,r3,r12 + vshr.u32 q11,q8,#18 + add r11,r11,r7 + add r7,r7,r0,ror#2 + eor r3,r3,r9 + veor q9,q9,q10 + add r6,r6,r2 + vsli.32 q11,q8,#14 + eor r2,r4,r5 + eor r0,r11,r11,ror#5 + vshr.u32 d24,d5,#17 + add r7,r7,r3 + and r2,r2,r11 + veor q9,q9,q11 + eor r3,r0,r11,ror#19 + eor r0,r7,r7,ror#11 + vsli.32 d24,d5,#15 + eor r2,r2,r5 + add r6,r6,r3,ror#6 + vshr.u32 d25,d5,#10 + eor r3,r7,r8 + eor r0,r0,r7,ror#20 + vadd.i32 q3,q3,q9 + add r6,r6,r2 + ldr r2,[sp,#56] + veor d25,d25,d24 + and r12,r12,r3 + add r10,r10,r6 + vshr.u32 d24,d5,#19 + add r6,r6,r0,ror#2 + eor r12,r12,r8 + vsli.32 d24,d5,#13 + add r5,r5,r2 + eor r2,r11,r4 + veor d25,d25,d24 + eor r0,r10,r10,ror#5 + add r6,r6,r12 + vadd.i32 d6,d6,d25 + and r2,r2,r10 + eor r12,r0,r10,ror#19 + vshr.u32 d24,d6,#17 + eor r0,r6,r6,ror#11 + eor r2,r2,r4 + vsli.32 d24,d6,#15 + add r5,r5,r12,ror#6 + eor r12,r6,r7 + vshr.u32 d25,d6,#10 + eor r0,r0,r6,ror#20 + add r5,r5,r2 + veor d25,d25,d24 + ldr r2,[sp,#60] + and r3,r3,r12 + vshr.u32 d24,d6,#19 + add r9,r9,r5 + add r5,r5,r0,ror#2 + eor r3,r3,r7 + vld1.32 {q8},[r14,:128]! + add r4,r4,r2 + vsli.32 d24,d6,#13 + eor r2,r10,r11 + eor r0,r9,r9,ror#5 + veor d25,d25,d24 + add r5,r5,r3 + and r2,r2,r9 + vadd.i32 d7,d7,d25 + eor r3,r0,r9,ror#19 + eor r0,r5,r5,ror#11 + vadd.i32 q8,q8,q3 + eor r2,r2,r11 + add r4,r4,r3,ror#6 + eor r3,r5,r6 + eor r0,r0,r5,ror#20 + add r4,r4,r2 + ldr r2,[r14] + and r12,r12,r3 + add r8,r8,r4 + vst1.32 {q8},[r1,:128]! + add r4,r4,r0,ror#2 + eor r12,r12,r6 + teq r2,#0 @ check for K256 terminator + ldr r2,[sp,#0] + sub r1,r1,#64 + bne .L_00_48 + + ldr r1,[sp,#68] + ldr r0,[sp,#72] + sub r14,r14,#256 @ rewind r14 + teq r1,r0 + it eq + subeq r1,r1,#64 @ avoid SEGV + vld1.8 {q0},[r1]! @ load next input block + vld1.8 {q1},[r1]! + vld1.8 {q2},[r1]! + vld1.8 {q3},[r1]! + it ne + strne r1,[sp,#68] + mov r1,sp + add r11,r11,r2 + eor r2,r9,r10 + eor r0,r8,r8,ror#5 + add r4,r4,r12 + vld1.32 {q8},[r14,:128]! + and r2,r2,r8 + eor r12,r0,r8,ror#19 + eor r0,r4,r4,ror#11 + eor r2,r2,r10 + vrev32.8 q0,q0 + add r11,r11,r12,ror#6 + eor r12,r4,r5 + eor r0,r0,r4,ror#20 + add r11,r11,r2 + vadd.i32 q8,q8,q0 + ldr r2,[sp,#4] + and r3,r3,r12 + add r7,r7,r11 + add r11,r11,r0,ror#2 + eor r3,r3,r5 + add r10,r10,r2 + eor r2,r8,r9 + eor r0,r7,r7,ror#5 + add r11,r11,r3 + and r2,r2,r7 + eor r3,r0,r7,ror#19 + eor r0,r11,r11,ror#11 + eor r2,r2,r9 + add r10,r10,r3,ror#6 + eor r3,r11,r4 + eor r0,r0,r11,ror#20 + add r10,r10,r2 + ldr r2,[sp,#8] + and r12,r12,r3 + add r6,r6,r10 + add r10,r10,r0,ror#2 + eor r12,r12,r4 + add r9,r9,r2 + eor r2,r7,r8 + eor r0,r6,r6,ror#5 + add r10,r10,r12 + and r2,r2,r6 + eor r12,r0,r6,ror#19 + eor r0,r10,r10,ror#11 + eor r2,r2,r8 + add r9,r9,r12,ror#6 + eor r12,r10,r11 + eor r0,r0,r10,ror#20 + add r9,r9,r2 + ldr r2,[sp,#12] + and r3,r3,r12 + add r5,r5,r9 + add r9,r9,r0,ror#2 + eor r3,r3,r11 + add r8,r8,r2 + eor r2,r6,r7 + eor r0,r5,r5,ror#5 + add r9,r9,r3 + and r2,r2,r5 + eor r3,r0,r5,ror#19 + eor r0,r9,r9,ror#11 + eor r2,r2,r7 + add r8,r8,r3,ror#6 + eor r3,r9,r10 + eor r0,r0,r9,ror#20 + add r8,r8,r2 + ldr r2,[sp,#16] + and r12,r12,r3 + add r4,r4,r8 + add r8,r8,r0,ror#2 + eor r12,r12,r10 + vst1.32 {q8},[r1,:128]! + add r7,r7,r2 + eor r2,r5,r6 + eor r0,r4,r4,ror#5 + add r8,r8,r12 + vld1.32 {q8},[r14,:128]! + and r2,r2,r4 + eor r12,r0,r4,ror#19 + eor r0,r8,r8,ror#11 + eor r2,r2,r6 + vrev32.8 q1,q1 + add r7,r7,r12,ror#6 + eor r12,r8,r9 + eor r0,r0,r8,ror#20 + add r7,r7,r2 + vadd.i32 q8,q8,q1 + ldr r2,[sp,#20] + and r3,r3,r12 + add r11,r11,r7 + add r7,r7,r0,ror#2 + eor r3,r3,r9 + add r6,r6,r2 + eor r2,r4,r5 + eor r0,r11,r11,ror#5 + add r7,r7,r3 + and r2,r2,r11 + eor r3,r0,r11,ror#19 + eor r0,r7,r7,ror#11 + eor r2,r2,r5 + add r6,r6,r3,ror#6 + eor r3,r7,r8 + eor r0,r0,r7,ror#20 + add r6,r6,r2 + ldr r2,[sp,#24] + and r12,r12,r3 + add r10,r10,r6 + add r6,r6,r0,ror#2 + eor r12,r12,r8 + add r5,r5,r2 + eor r2,r11,r4 + eor r0,r10,r10,ror#5 + add r6,r6,r12 + and r2,r2,r10 + eor r12,r0,r10,ror#19 + eor r0,r6,r6,ror#11 + eor r2,r2,r4 + add r5,r5,r12,ror#6 + eor r12,r6,r7 + eor r0,r0,r6,ror#20 + add r5,r5,r2 + ldr r2,[sp,#28] + and r3,r3,r12 + add r9,r9,r5 + add r5,r5,r0,ror#2 + eor r3,r3,r7 + add r4,r4,r2 + eor r2,r10,r11 + eor r0,r9,r9,ror#5 + add r5,r5,r3 + and r2,r2,r9 + eor r3,r0,r9,ror#19 + eor r0,r5,r5,ror#11 + eor r2,r2,r11 + add r4,r4,r3,ror#6 + eor r3,r5,r6 + eor r0,r0,r5,ror#20 + add r4,r4,r2 + ldr r2,[sp,#32] + and r12,r12,r3 + add r8,r8,r4 + add r4,r4,r0,ror#2 + eor r12,r12,r6 + vst1.32 {q8},[r1,:128]! + add r11,r11,r2 + eor r2,r9,r10 + eor r0,r8,r8,ror#5 + add r4,r4,r12 + vld1.32 {q8},[r14,:128]! + and r2,r2,r8 + eor r12,r0,r8,ror#19 + eor r0,r4,r4,ror#11 + eor r2,r2,r10 + vrev32.8 q2,q2 + add r11,r11,r12,ror#6 + eor r12,r4,r5 + eor r0,r0,r4,ror#20 + add r11,r11,r2 + vadd.i32 q8,q8,q2 + ldr r2,[sp,#36] + and r3,r3,r12 + add r7,r7,r11 + add r11,r11,r0,ror#2 + eor r3,r3,r5 + add r10,r10,r2 + eor r2,r8,r9 + eor r0,r7,r7,ror#5 + add r11,r11,r3 + and r2,r2,r7 + eor r3,r0,r7,ror#19 + eor r0,r11,r11,ror#11 + eor r2,r2,r9 + add r10,r10,r3,ror#6 + eor r3,r11,r4 + eor r0,r0,r11,ror#20 + add r10,r10,r2 + ldr r2,[sp,#40] + and r12,r12,r3 + add r6,r6,r10 + add r10,r10,r0,ror#2 + eor r12,r12,r4 + add r9,r9,r2 + eor r2,r7,r8 + eor r0,r6,r6,ror#5 + add r10,r10,r12 + and r2,r2,r6 + eor r12,r0,r6,ror#19 + eor r0,r10,r10,ror#11 + eor r2,r2,r8 + add r9,r9,r12,ror#6 + eor r12,r10,r11 + eor r0,r0,r10,ror#20 + add r9,r9,r2 + ldr r2,[sp,#44] + and r3,r3,r12 + add r5,r5,r9 + add r9,r9,r0,ror#2 + eor r3,r3,r11 + add r8,r8,r2 + eor r2,r6,r7 + eor r0,r5,r5,ror#5 + add r9,r9,r3 + and r2,r2,r5 + eor r3,r0,r5,ror#19 + eor r0,r9,r9,ror#11 + eor r2,r2,r7 + add r8,r8,r3,ror#6 + eor r3,r9,r10 + eor r0,r0,r9,ror#20 + add r8,r8,r2 + ldr r2,[sp,#48] + and r12,r12,r3 + add r4,r4,r8 + add r8,r8,r0,ror#2 + eor r12,r12,r10 + vst1.32 {q8},[r1,:128]! + add r7,r7,r2 + eor r2,r5,r6 + eor r0,r4,r4,ror#5 + add r8,r8,r12 + vld1.32 {q8},[r14,:128]! + and r2,r2,r4 + eor r12,r0,r4,ror#19 + eor r0,r8,r8,ror#11 + eor r2,r2,r6 + vrev32.8 q3,q3 + add r7,r7,r12,ror#6 + eor r12,r8,r9 + eor r0,r0,r8,ror#20 + add r7,r7,r2 + vadd.i32 q8,q8,q3 + ldr r2,[sp,#52] + and r3,r3,r12 + add r11,r11,r7 + add r7,r7,r0,ror#2 + eor r3,r3,r9 + add r6,r6,r2 + eor r2,r4,r5 + eor r0,r11,r11,ror#5 + add r7,r7,r3 + and r2,r2,r11 + eor r3,r0,r11,ror#19 + eor r0,r7,r7,ror#11 + eor r2,r2,r5 + add r6,r6,r3,ror#6 + eor r3,r7,r8 + eor r0,r0,r7,ror#20 + add r6,r6,r2 + ldr r2,[sp,#56] + and r12,r12,r3 + add r10,r10,r6 + add r6,r6,r0,ror#2 + eor r12,r12,r8 + add r5,r5,r2 + eor r2,r11,r4 + eor r0,r10,r10,ror#5 + add r6,r6,r12 + and r2,r2,r10 + eor r12,r0,r10,ror#19 + eor r0,r6,r6,ror#11 + eor r2,r2,r4 + add r5,r5,r12,ror#6 + eor r12,r6,r7 + eor r0,r0,r6,ror#20 + add r5,r5,r2 + ldr r2,[sp,#60] + and r3,r3,r12 + add r9,r9,r5 + add r5,r5,r0,ror#2 + eor r3,r3,r7 + add r4,r4,r2 + eor r2,r10,r11 + eor r0,r9,r9,ror#5 + add r5,r5,r3 + and r2,r2,r9 + eor r3,r0,r9,ror#19 + eor r0,r5,r5,ror#11 + eor r2,r2,r11 + add r4,r4,r3,ror#6 + eor r3,r5,r6 + eor r0,r0,r5,ror#20 + add r4,r4,r2 + ldr r2,[sp,#64] + and r12,r12,r3 + add r8,r8,r4 + add r4,r4,r0,ror#2 + eor r12,r12,r6 + vst1.32 {q8},[r1,:128]! + ldr r0,[r2,#0] + add r4,r4,r12 @ h+=Maj(a,b,c) from the past + ldr r12,[r2,#4] + ldr r3,[r2,#8] + ldr r1,[r2,#12] + add r4,r4,r0 @ accumulate + ldr r0,[r2,#16] + add r5,r5,r12 + ldr r12,[r2,#20] + add r6,r6,r3 + ldr r3,[r2,#24] + add r7,r7,r1 + ldr r1,[r2,#28] + add r8,r8,r0 + str r4,[r2],#4 + add r9,r9,r12 + str r5,[r2],#4 + add r10,r10,r3 + str r6,[r2],#4 + add r11,r11,r1 + str r7,[r2],#4 + stmia r2,{r8-r11} + + ittte ne + movne r1,sp + ldrne r2,[sp,#0] + eorne r12,r12,r12 + ldreq sp,[sp,#76] @ restore original sp + itt ne + eorne r3,r5,r6 + bne .L_00_48 + + ldmia sp!,{r4-r12,pc} +.size sha256_block_data_order_neon,.-sha256_block_data_order_neon +#endif +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) + +# ifdef __thumb2__ +# define INST(a,b,c,d) .byte c,d|0xc,a,b +# else +# define INST(a,b,c,d) .byte a,b,c,d +# endif + +.type sha256_block_data_order_armv8,%function +.align 5 +sha256_block_data_order_armv8: +.LARMv8: + vld1.32 {q0,q1},[r0] +# ifdef __thumb2__ + adr r3,.LARMv8 + sub r3,r3,#.LARMv8-K256 +# else + adrl r3,K256 +# endif + add r2,r1,r2,lsl#6 @ len to point at the end of inp + +.Loop_v8: + vld1.8 {q8-q9},[r1]! + vld1.8 {q10-q11},[r1]! + vld1.32 {q12},[r3]! + vrev32.8 q8,q8 + vrev32.8 q9,q9 + vrev32.8 q10,q10 + vrev32.8 q11,q11 + vmov q14,q0 @ offload + vmov q15,q1 + teq r1,r2 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q8 + INST(0xe2,0x03,0xfa,0xf3) @ sha256su0 q8,q9 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe6,0x0c,0x64,0xf3) @ sha256su1 q8,q10,q11 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q9 + INST(0xe4,0x23,0xfa,0xf3) @ sha256su0 q9,q10 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe0,0x2c,0x66,0xf3) @ sha256su1 q9,q11,q8 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q10 + INST(0xe6,0x43,0xfa,0xf3) @ sha256su0 q10,q11 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe2,0x4c,0x60,0xf3) @ sha256su1 q10,q8,q9 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q11 + INST(0xe0,0x63,0xfa,0xf3) @ sha256su0 q11,q8 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe4,0x6c,0x62,0xf3) @ sha256su1 q11,q9,q10 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q8 + INST(0xe2,0x03,0xfa,0xf3) @ sha256su0 q8,q9 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe6,0x0c,0x64,0xf3) @ sha256su1 q8,q10,q11 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q9 + INST(0xe4,0x23,0xfa,0xf3) @ sha256su0 q9,q10 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe0,0x2c,0x66,0xf3) @ sha256su1 q9,q11,q8 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q10 + INST(0xe6,0x43,0xfa,0xf3) @ sha256su0 q10,q11 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe2,0x4c,0x60,0xf3) @ sha256su1 q10,q8,q9 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q11 + INST(0xe0,0x63,0xfa,0xf3) @ sha256su0 q11,q8 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe4,0x6c,0x62,0xf3) @ sha256su1 q11,q9,q10 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q8 + INST(0xe2,0x03,0xfa,0xf3) @ sha256su0 q8,q9 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe6,0x0c,0x64,0xf3) @ sha256su1 q8,q10,q11 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q9 + INST(0xe4,0x23,0xfa,0xf3) @ sha256su0 q9,q10 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe0,0x2c,0x66,0xf3) @ sha256su1 q9,q11,q8 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q10 + INST(0xe6,0x43,0xfa,0xf3) @ sha256su0 q10,q11 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + INST(0xe2,0x4c,0x60,0xf3) @ sha256su1 q10,q8,q9 + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q11 + INST(0xe0,0x63,0xfa,0xf3) @ sha256su0 q11,q8 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + INST(0xe4,0x6c,0x62,0xf3) @ sha256su1 q11,q9,q10 + vld1.32 {q13},[r3]! + vadd.i32 q12,q12,q8 + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + + vld1.32 {q12},[r3]! + vadd.i32 q13,q13,q9 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + + vld1.32 {q13},[r3] + vadd.i32 q12,q12,q10 + sub r3,r3,#256-16 @ rewind + vmov q2,q0 + INST(0x68,0x0c,0x02,0xf3) @ sha256h q0,q1,q12 + INST(0x68,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q12 + + vadd.i32 q13,q13,q11 + vmov q2,q0 + INST(0x6a,0x0c,0x02,0xf3) @ sha256h q0,q1,q13 + INST(0x6a,0x2c,0x14,0xf3) @ sha256h2 q1,q2,q13 + + vadd.i32 q0,q0,q14 + vadd.i32 q1,q1,q15 + it ne + bne .Loop_v8 + + vst1.32 {q0,q1},[r0] + + bx lr @ bx lr +.size sha256_block_data_order_armv8,.-sha256_block_data_order_armv8 +#endif +.asciz "SHA256 block transform for ARMv4/NEON/ARMv8, CRYPTOGAMS by <appro@openssl.org>" +.align 2 +#if __ARM_MAX_ARCH__>=7 && !defined(__KERNEL__) +.comm OPENSSL_armcap_P,4,4 +#endif diff --git a/arch/arm/crypto/sha256_glue.c b/arch/arm/crypto/sha256_glue.c new file mode 100644 index 000000000000..a84e869ef900 --- /dev/null +++ b/arch/arm/crypto/sha256_glue.c @@ -0,0 +1,128 @@ +/* + * Glue code for the SHA256 Secure Hash Algorithm assembly implementation + * using optimized ARM assembler and NEON instructions. + * + * Copyright © 2015 Google Inc. + * + * This file is based on sha256_ssse3_glue.c: + * Copyright (C) 2013 Intel Corporation + * Author: Tim Chen <tim.c.chen@linux.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 <crypto/internal/hash.h> +#include <linux/crypto.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/cryptohash.h> +#include <linux/types.h> +#include <linux/string.h> +#include <crypto/sha.h> +#include <crypto/sha256_base.h> +#include <asm/simd.h> +#include <asm/neon.h> + +#include "sha256_glue.h" + +asmlinkage void sha256_block_data_order(u32 *digest, const void *data, + unsigned int num_blks); + +int crypto_sha256_arm_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + /* make sure casting to sha256_block_fn() is safe */ + BUILD_BUG_ON(offsetof(struct sha256_state, state) != 0); + + return sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_block_data_order); +} +EXPORT_SYMBOL(crypto_sha256_arm_update); + +static int sha256_final(struct shash_desc *desc, u8 *out) +{ + sha256_base_do_finalize(desc, + (sha256_block_fn *)sha256_block_data_order); + return sha256_base_finish(desc, out); +} + +int crypto_sha256_arm_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_block_data_order); + return sha256_final(desc, out); +} +EXPORT_SYMBOL(crypto_sha256_arm_finup); + +static struct shash_alg algs[] = { { + .digestsize = SHA256_DIGEST_SIZE, + .init = sha256_base_init, + .update = crypto_sha256_arm_update, + .final = sha256_final, + .finup = crypto_sha256_arm_finup, + .descsize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-asm", + .cra_priority = 150, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .digestsize = SHA224_DIGEST_SIZE, + .init = sha224_base_init, + .update = crypto_sha256_arm_update, + .final = sha256_final, + .finup = crypto_sha256_arm_finup, + .descsize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-asm", + .cra_priority = 150, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init sha256_mod_init(void) +{ + int res = crypto_register_shashes(algs, ARRAY_SIZE(algs)); + + if (res < 0) + return res; + + if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) { + res = crypto_register_shashes(sha256_neon_algs, + ARRAY_SIZE(sha256_neon_algs)); + + if (res < 0) + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); + } + + return res; +} + +static void __exit sha256_mod_fini(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); + + if (IS_ENABLED(CONFIG_KERNEL_MODE_NEON) && cpu_has_neon()) + crypto_unregister_shashes(sha256_neon_algs, + ARRAY_SIZE(sha256_neon_algs)); +} + +module_init(sha256_mod_init); +module_exit(sha256_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA256 Secure Hash Algorithm (ARM), including NEON"); + +MODULE_ALIAS_CRYPTO("sha256"); diff --git a/arch/arm/crypto/sha256_glue.h b/arch/arm/crypto/sha256_glue.h new file mode 100644 index 000000000000..7cf0bf786ada --- /dev/null +++ b/arch/arm/crypto/sha256_glue.h @@ -0,0 +1,14 @@ +#ifndef _CRYPTO_SHA256_GLUE_H +#define _CRYPTO_SHA256_GLUE_H + +#include <linux/crypto.h> + +extern struct shash_alg sha256_neon_algs[2]; + +int crypto_sha256_arm_update(struct shash_desc *desc, const u8 *data, + unsigned int len); + +int crypto_sha256_arm_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); + +#endif /* _CRYPTO_SHA256_GLUE_H */ diff --git a/arch/arm/crypto/sha256_neon_glue.c b/arch/arm/crypto/sha256_neon_glue.c new file mode 100644 index 000000000000..39ccd658817e --- /dev/null +++ b/arch/arm/crypto/sha256_neon_glue.c @@ -0,0 +1,101 @@ +/* + * Glue code for the SHA256 Secure Hash Algorithm assembly implementation + * using NEON instructions. + * + * Copyright © 2015 Google Inc. + * + * This file is based on sha512_neon_glue.c: + * Copyright © 2014 Jussi Kivilinna <jussi.kivilinna@iki.fi> + * + * 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 <crypto/internal/hash.h> +#include <linux/cryptohash.h> +#include <linux/types.h> +#include <linux/string.h> +#include <crypto/sha.h> +#include <crypto/sha256_base.h> +#include <asm/byteorder.h> +#include <asm/simd.h> +#include <asm/neon.h> + +#include "sha256_glue.h" + +asmlinkage void sha256_block_data_order_neon(u32 *digest, const void *data, + unsigned int num_blks); + +static int sha256_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + if (!may_use_simd() || + (sctx->count % SHA256_BLOCK_SIZE) + len < SHA256_BLOCK_SIZE) + return crypto_sha256_arm_update(desc, data, len); + + kernel_neon_begin(); + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_block_data_order_neon); + kernel_neon_end(); + + return 0; +} + +static int sha256_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) +{ + if (!may_use_simd()) + return crypto_sha256_arm_finup(desc, data, len, out); + + kernel_neon_begin(); + if (len) + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_block_data_order_neon); + sha256_base_do_finalize(desc, + (sha256_block_fn *)sha256_block_data_order_neon); + kernel_neon_end(); + + return sha256_base_finish(desc, out); +} + +static int sha256_final(struct shash_desc *desc, u8 *out) +{ + return sha256_finup(desc, NULL, 0, out); +} + +struct shash_alg sha256_neon_algs[] = { { + .digestsize = SHA256_DIGEST_SIZE, + .init = sha256_base_init, + .update = sha256_update, + .final = sha256_final, + .finup = sha256_finup, + .descsize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name = "sha256-neon", + .cra_priority = 250, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .digestsize = SHA224_DIGEST_SIZE, + .init = sha224_base_init, + .update = sha256_update, + .final = sha256_final, + .finup = sha256_finup, + .descsize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name = "sha224-neon", + .cra_priority = 250, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; diff --git a/arch/arm/include/asm/thread_info.h b/arch/arm/include/asm/thread_info.h index 72812a1f3d1c..bd32eded3e50 100644 --- a/arch/arm/include/asm/thread_info.h +++ b/arch/arm/include/asm/thread_info.h @@ -23,7 +23,6 @@ #ifndef __ASSEMBLY__ struct task_struct; -struct exec_domain; #include <asm/types.h> #include <asm/domain.h> @@ -53,7 +52,6 @@ struct thread_info { int preempt_count; /* 0 => preemptable, <0 => bug */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ __u32 cpu; /* cpu */ __u32 cpu_domain; /* cpu domain */ struct cpu_context_save cpu_context; /* cpu context */ @@ -73,7 +71,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index ba5f83226011..752725dcbf42 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -34,7 +34,6 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ISA_DMA_API) += dma.o obj-$(CONFIG_FIQ) += fiq.o fiqasm.o obj-$(CONFIG_MODULES) += armksyms.o module.o -obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o obj-$(CONFIG_PCI) += bios32.o isa.o obj-$(CONFIG_ARM_CPU_SUSPEND) += sleep.o suspend.o diff --git a/arch/arm/kernel/arthur.c b/arch/arm/kernel/arthur.c deleted file mode 100644 index 321c5291d05f..000000000000 --- a/arch/arm/kernel/arthur.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * linux/arch/arm/kernel/arthur.c - * - * Copyright (C) 1998, 1999, 2000, 2001 Philip Blundell - * - * Arthur personality - */ - -/* - * 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/personality.h> -#include <linux/stddef.h> -#include <linux/signal.h> -#include <linux/init.h> -#include <linux/sched.h> - -#include <asm/ptrace.h> - -/* Arthur doesn't have many signals, and a lot of those that it does - have don't map easily to any Linux equivalent. Never mind. */ - -#define ARTHUR_SIGABRT 1 -#define ARTHUR_SIGFPE 2 -#define ARTHUR_SIGILL 3 -#define ARTHUR_SIGINT 4 -#define ARTHUR_SIGSEGV 5 -#define ARTHUR_SIGTERM 6 -#define ARTHUR_SIGSTAK 7 -#define ARTHUR_SIGUSR1 8 -#define ARTHUR_SIGUSR2 9 -#define ARTHUR_SIGOSERROR 10 - -static unsigned long arthur_to_linux_signals[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31 -}; - -static unsigned long linux_to_arthur_signals[32] = { - 0, -1, ARTHUR_SIGINT, -1, - ARTHUR_SIGILL, 5, ARTHUR_SIGABRT, 7, - ARTHUR_SIGFPE, 9, ARTHUR_SIGUSR1, ARTHUR_SIGSEGV, - ARTHUR_SIGUSR2, 13, 14, ARTHUR_SIGTERM, - 16, 17, 18, 19, - 20, 21, 22, 23, - 24, 25, 26, 27, - 28, 29, 30, 31 -}; - -static void arthur_lcall7(int nr, struct pt_regs *regs) -{ - struct siginfo info; - info.si_signo = SIGSWI; - info.si_errno = nr; - /* Bounce it to the emulator */ - send_sig_info(SIGSWI, &info, current); -} - -static struct exec_domain arthur_exec_domain = { - .name = "Arthur", - .handler = arthur_lcall7, - .pers_low = PER_RISCOS, - .pers_high = PER_RISCOS, - .signal_map = arthur_to_linux_signals, - .signal_invmap = linux_to_arthur_signals, - .module = THIS_MODULE, -}; - -/* - * We could do with some locking to stop Arthur being removed while - * processes are using it. - */ - -static int __init arthur_init(void) -{ - return register_exec_domain(&arthur_exec_domain); -} - -static void __exit arthur_exit(void) -{ - unregister_exec_domain(&arthur_exec_domain); -} - -module_init(arthur_init); -module_exit(arthur_exit); - -MODULE_LICENSE("GPL"); diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 61bb5a65eb37..871b8267d211 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -67,7 +67,6 @@ int main(void) DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain)); DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context)); diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 023ac905e4c3..423663e23791 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -318,17 +318,6 @@ get_sigframe(struct ksignal *ksig, struct pt_regs *regs, int framesize) return frame; } -/* - * translate the signal - */ -static inline int map_sig(int sig) -{ - struct thread_info *thread = current_thread_info(); - if (sig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) - sig = thread->exec_domain->signal_invmap[sig]; - return sig; -} - static int setup_return(struct pt_regs *regs, struct ksignal *ksig, unsigned long __user *rc, void __user *frame) @@ -412,7 +401,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig, } } - regs->ARM_r0 = map_sig(ksig->sig); + regs->ARM_r0 = ksig->sig; regs->ARM_sp = (unsigned long)frame; regs->ARM_lr = retcode; regs->ARM_pc = handler; diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 788e23fe64d8..3dce1a342030 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -505,12 +505,10 @@ asmlinkage void bad_mode(struct pt_regs *regs, int reason) static int bad_syscall(int n, struct pt_regs *regs) { - struct thread_info *thread = current_thread_info(); siginfo_t info; - if ((current->personality & PER_MASK) != PER_LINUX && - thread->exec_domain->handler) { - thread->exec_domain->handler(n, regs); + if ((current->personality & PER_MASK) != PER_LINUX) { + send_sig(SIGSEGV, current, 1); return regs->ARM_r0; } diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 6d949f1c850b..36aaeb12e1a5 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -1015,7 +1015,6 @@ static struct asoc_simple_card_info fsi_wm8978_info = { .platform = "sh_fsi2", .daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM, .cpu_dai = { - .fmt = SND_SOC_DAIFMT_IB_NF, .name = "fsia-dai", }, .codec_dai = { @@ -1040,9 +1039,9 @@ static struct asoc_simple_card_info fsi2_hdmi_info = { .card = "FSI2B-HDMI", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", + .daifmt = SND_SOC_DAIFMT_CBS_CFS, .cpu_dai = { .name = "fsib-dai", - .fmt = SND_SOC_DAIFMT_CBS_CFS, }, .codec_dai = { .name = "sh_mobile_hdmi-hifi", diff --git a/arch/arm64/crypto/aes-glue.c b/arch/arm64/crypto/aes-glue.c index b1b5b893eb20..05d9e16c0dfd 100644 --- a/arch/arm64/crypto/aes-glue.c +++ b/arch/arm64/crypto/aes-glue.c @@ -284,7 +284,8 @@ static struct crypto_alg aes_algs[] = { { .cra_name = "__ecb-aes-" MODE, .cra_driver_name = "__driver-ecb-aes-" MODE, .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx), .cra_alignmask = 7, @@ -302,7 +303,8 @@ static struct crypto_alg aes_algs[] = { { .cra_name = "__cbc-aes-" MODE, .cra_driver_name = "__driver-cbc-aes-" MODE, .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx), .cra_alignmask = 7, @@ -320,7 +322,8 @@ static struct crypto_alg aes_algs[] = { { .cra_name = "__ctr-aes-" MODE, .cra_driver_name = "__driver-ctr-aes-" MODE, .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct crypto_aes_ctx), .cra_alignmask = 7, @@ -338,7 +341,8 @@ static struct crypto_alg aes_algs[] = { { .cra_name = "__xts-aes-" MODE, .cra_driver_name = "__driver-xts-aes-" MODE, .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_xts_ctx), .cra_alignmask = 7, diff --git a/arch/arm64/crypto/sha1-ce-core.S b/arch/arm64/crypto/sha1-ce-core.S index 09d57d98609c..033aae6d732a 100644 --- a/arch/arm64/crypto/sha1-ce-core.S +++ b/arch/arm64/crypto/sha1-ce-core.S @@ -66,8 +66,8 @@ .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6 /* - * void sha1_ce_transform(int blocks, u8 const *src, u32 *state, - * u8 *head, long bytes) + * void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, + * int blocks) */ ENTRY(sha1_ce_transform) /* load round constants */ @@ -78,25 +78,22 @@ ENTRY(sha1_ce_transform) ld1r {k3.4s}, [x6] /* load state */ - ldr dga, [x2] - ldr dgb, [x2, #16] + ldr dga, [x0] + ldr dgb, [x0, #16] - /* load partial state (if supplied) */ - cbz x3, 0f - ld1 {v8.4s-v11.4s}, [x3] - b 1f + /* load sha1_ce_state::finalize */ + ldr w4, [x0, #:lo12:sha1_ce_offsetof_finalize] /* load input */ 0: ld1 {v8.4s-v11.4s}, [x1], #64 - sub w0, w0, #1 + sub w2, w2, #1 -1: CPU_LE( rev32 v8.16b, v8.16b ) CPU_LE( rev32 v9.16b, v9.16b ) CPU_LE( rev32 v10.16b, v10.16b ) CPU_LE( rev32 v11.16b, v11.16b ) -2: add t0.4s, v8.4s, k0.4s +1: add t0.4s, v8.4s, k0.4s mov dg0v.16b, dgav.16b add_update c, ev, k0, 8, 9, 10, 11, dgb @@ -127,15 +124,15 @@ CPU_LE( rev32 v11.16b, v11.16b ) add dgbv.2s, dgbv.2s, dg1v.2s add dgav.4s, dgav.4s, dg0v.4s - cbnz w0, 0b + cbnz w2, 0b /* * Final block: add padding and total bit count. - * Skip if we have no total byte count in x4. In that case, the input - * size was not a round multiple of the block size, and the padding is - * handled by the C code. + * Skip if the input size was not a round multiple of the block size, + * the padding is handled by the C code in that case. */ cbz x4, 3f + ldr x4, [x0, #:lo12:sha1_ce_offsetof_count] movi v9.2d, #0 mov x8, #0x80000000 movi v10.2d, #0 @@ -144,10 +141,10 @@ CPU_LE( rev32 v11.16b, v11.16b ) mov x4, #0 mov v11.d[0], xzr mov v11.d[1], x7 - b 2b + b 1b /* store new state */ -3: str dga, [x2] - str dgb, [x2, #16] +3: str dga, [x0] + str dgb, [x0, #16] ret ENDPROC(sha1_ce_transform) diff --git a/arch/arm64/crypto/sha1-ce-glue.c b/arch/arm64/crypto/sha1-ce-glue.c index 6fe83f37a750..114e7cc5de8c 100644 --- a/arch/arm64/crypto/sha1-ce-glue.c +++ b/arch/arm64/crypto/sha1-ce-glue.c @@ -12,144 +12,81 @@ #include <asm/unaligned.h> #include <crypto/internal/hash.h> #include <crypto/sha.h> +#include <crypto/sha1_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> #include <linux/module.h> +#define ASM_EXPORT(sym, val) \ + asm(".globl " #sym "; .set " #sym ", %0" :: "I"(val)); + MODULE_DESCRIPTION("SHA1 secure hash using ARMv8 Crypto Extensions"); MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); MODULE_LICENSE("GPL v2"); -asmlinkage void sha1_ce_transform(int blocks, u8 const *src, u32 *state, - u8 *head, long bytes); +struct sha1_ce_state { + struct sha1_state sst; + u32 finalize; +}; -static int sha1_init(struct shash_desc *desc) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); +asmlinkage void sha1_ce_transform(struct sha1_ce_state *sst, u8 const *src, + int blocks); - *sctx = (struct sha1_state){ - .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, - }; - return 0; -} - -static int sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) +static int sha1_ce_update(struct shash_desc *desc, const u8 *data, + unsigned int len) { - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; - - sctx->count += len; - - if ((partial + len) >= SHA1_BLOCK_SIZE) { - int blocks; - - if (partial) { - int p = SHA1_BLOCK_SIZE - partial; + struct sha1_ce_state *sctx = shash_desc_ctx(desc); - memcpy(sctx->buffer + partial, data, p); - data += p; - len -= p; - } - - blocks = len / SHA1_BLOCK_SIZE; - len %= SHA1_BLOCK_SIZE; - - kernel_neon_begin_partial(16); - sha1_ce_transform(blocks, data, sctx->state, - partial ? sctx->buffer : NULL, 0); - kernel_neon_end(); + sctx->finalize = 0; + kernel_neon_begin_partial(16); + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_ce_transform); + kernel_neon_end(); - data += blocks * SHA1_BLOCK_SIZE; - partial = 0; - } - if (len) - memcpy(sctx->buffer + partial, data, len); return 0; } -static int sha1_final(struct shash_desc *desc, u8 *out) +static int sha1_ce_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; + struct sha1_ce_state *sctx = shash_desc_ctx(desc); + bool finalize = !sctx->sst.count && !(len % SHA1_BLOCK_SIZE); - struct sha1_state *sctx = shash_desc_ctx(desc); - __be64 bits = cpu_to_be64(sctx->count << 3); - __be32 *dst = (__be32 *)out; - int i; - - u32 padlen = SHA1_BLOCK_SIZE - - ((sctx->count + sizeof(bits)) % SHA1_BLOCK_SIZE); - - sha1_update(desc, padding, padlen); - sha1_update(desc, (const u8 *)&bits, sizeof(bits)); - - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); - - *sctx = (struct sha1_state){}; - return 0; -} - -static int sha1_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - int blocks; - int i; - - if (sctx->count || !len || (len % SHA1_BLOCK_SIZE)) { - sha1_update(desc, data, len); - return sha1_final(desc, out); - } + ASM_EXPORT(sha1_ce_offsetof_count, + offsetof(struct sha1_ce_state, sst.count)); + ASM_EXPORT(sha1_ce_offsetof_finalize, + offsetof(struct sha1_ce_state, finalize)); /* - * Use a fast path if the input is a multiple of 64 bytes. In - * this case, there is no need to copy data around, and we can - * perform the entire digest calculation in a single invocation - * of sha1_ce_transform() + * Allow the asm code to perform the finalization if there is no + * partial data and the input is a round multiple of the block size. */ - blocks = len / SHA1_BLOCK_SIZE; + sctx->finalize = finalize; kernel_neon_begin_partial(16); - sha1_ce_transform(blocks, data, sctx->state, NULL, len); + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_ce_transform); + if (!finalize) + sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_ce_transform); kernel_neon_end(); - - for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); - - *sctx = (struct sha1_state){}; - return 0; + return sha1_base_finish(desc, out); } -static int sha1_export(struct shash_desc *desc, void *out) +static int sha1_ce_final(struct shash_desc *desc, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - struct sha1_state *dst = out; - - *dst = *sctx; - return 0; -} - -static int sha1_import(struct shash_desc *desc, const void *in) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - struct sha1_state const *src = in; - - *sctx = *src; - return 0; + kernel_neon_begin_partial(16); + sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_ce_transform); + kernel_neon_end(); + return sha1_base_finish(desc, out); } static struct shash_alg alg = { - .init = sha1_init, - .update = sha1_update, - .final = sha1_final, - .finup = sha1_finup, - .export = sha1_export, - .import = sha1_import, - .descsize = sizeof(struct sha1_state), + .init = sha1_base_init, + .update = sha1_ce_update, + .final = sha1_ce_final, + .finup = sha1_ce_finup, + .descsize = sizeof(struct sha1_ce_state), .digestsize = SHA1_DIGEST_SIZE, - .statesize = sizeof(struct sha1_state), .base = { .cra_name = "sha1", .cra_driver_name = "sha1-ce", diff --git a/arch/arm64/crypto/sha2-ce-core.S b/arch/arm64/crypto/sha2-ce-core.S index 7f29fc031ea8..5df9d9d470ad 100644 --- a/arch/arm64/crypto/sha2-ce-core.S +++ b/arch/arm64/crypto/sha2-ce-core.S @@ -73,8 +73,8 @@ .word 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 /* - * void sha2_ce_transform(int blocks, u8 const *src, u32 *state, - * u8 *head, long bytes) + * void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src, + * int blocks) */ ENTRY(sha2_ce_transform) /* load round constants */ @@ -85,24 +85,21 @@ ENTRY(sha2_ce_transform) ld1 {v12.4s-v15.4s}, [x8] /* load state */ - ldp dga, dgb, [x2] + ldp dga, dgb, [x0] - /* load partial input (if supplied) */ - cbz x3, 0f - ld1 {v16.4s-v19.4s}, [x3] - b 1f + /* load sha256_ce_state::finalize */ + ldr w4, [x0, #:lo12:sha256_ce_offsetof_finalize] /* load input */ 0: ld1 {v16.4s-v19.4s}, [x1], #64 - sub w0, w0, #1 + sub w2, w2, #1 -1: CPU_LE( rev32 v16.16b, v16.16b ) CPU_LE( rev32 v17.16b, v17.16b ) CPU_LE( rev32 v18.16b, v18.16b ) CPU_LE( rev32 v19.16b, v19.16b ) -2: add t0.4s, v16.4s, v0.4s +1: add t0.4s, v16.4s, v0.4s mov dg0v.16b, dgav.16b mov dg1v.16b, dgbv.16b @@ -131,15 +128,15 @@ CPU_LE( rev32 v19.16b, v19.16b ) add dgbv.4s, dgbv.4s, dg1v.4s /* handled all input blocks? */ - cbnz w0, 0b + cbnz w2, 0b /* * Final block: add padding and total bit count. - * Skip if we have no total byte count in x4. In that case, the input - * size was not a round multiple of the block size, and the padding is - * handled by the C code. + * Skip if the input size was not a round multiple of the block size, + * the padding is handled by the C code in that case. */ cbz x4, 3f + ldr x4, [x0, #:lo12:sha256_ce_offsetof_count] movi v17.2d, #0 mov x8, #0x80000000 movi v18.2d, #0 @@ -148,9 +145,9 @@ CPU_LE( rev32 v19.16b, v19.16b ) mov x4, #0 mov v19.d[0], xzr mov v19.d[1], x7 - b 2b + b 1b /* store new state */ -3: stp dga, dgb, [x2] +3: stp dga, dgb, [x0] ret ENDPROC(sha2_ce_transform) diff --git a/arch/arm64/crypto/sha2-ce-glue.c b/arch/arm64/crypto/sha2-ce-glue.c index ae67e88c28b9..1340e44c048b 100644 --- a/arch/arm64/crypto/sha2-ce-glue.c +++ b/arch/arm64/crypto/sha2-ce-glue.c @@ -12,206 +12,82 @@ #include <asm/unaligned.h> #include <crypto/internal/hash.h> #include <crypto/sha.h> +#include <crypto/sha256_base.h> #include <linux/cpufeature.h> #include <linux/crypto.h> #include <linux/module.h> +#define ASM_EXPORT(sym, val) \ + asm(".globl " #sym "; .set " #sym ", %0" :: "I"(val)); + MODULE_DESCRIPTION("SHA-224/SHA-256 secure hash using ARMv8 Crypto Extensions"); MODULE_AUTHOR("Ard Biesheuvel <ard.biesheuvel@linaro.org>"); MODULE_LICENSE("GPL v2"); -asmlinkage int sha2_ce_transform(int blocks, u8 const *src, u32 *state, - u8 *head, long bytes); - -static int sha224_init(struct shash_desc *desc) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - *sctx = (struct sha256_state){ - .state = { - SHA224_H0, SHA224_H1, SHA224_H2, SHA224_H3, - SHA224_H4, SHA224_H5, SHA224_H6, SHA224_H7, - } - }; - return 0; -} - -static int sha256_init(struct shash_desc *desc) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - *sctx = (struct sha256_state){ - .state = { - SHA256_H0, SHA256_H1, SHA256_H2, SHA256_H3, - SHA256_H4, SHA256_H5, SHA256_H6, SHA256_H7, - } - }; - return 0; -} - -static int sha2_update(struct shash_desc *desc, const u8 *data, - unsigned int len) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; - - sctx->count += len; - - if ((partial + len) >= SHA256_BLOCK_SIZE) { - int blocks; - - if (partial) { - int p = SHA256_BLOCK_SIZE - partial; - - memcpy(sctx->buf + partial, data, p); - data += p; - len -= p; - } +struct sha256_ce_state { + struct sha256_state sst; + u32 finalize; +}; - blocks = len / SHA256_BLOCK_SIZE; - len %= SHA256_BLOCK_SIZE; +asmlinkage void sha2_ce_transform(struct sha256_ce_state *sst, u8 const *src, + int blocks); - kernel_neon_begin_partial(28); - sha2_ce_transform(blocks, data, sctx->state, - partial ? sctx->buf : NULL, 0); - kernel_neon_end(); - - data += blocks * SHA256_BLOCK_SIZE; - partial = 0; - } - if (len) - memcpy(sctx->buf + partial, data, len); - return 0; -} - -static void sha2_final(struct shash_desc *desc) +static int sha256_ce_update(struct shash_desc *desc, const u8 *data, + unsigned int len) { - static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, }; - - struct sha256_state *sctx = shash_desc_ctx(desc); - __be64 bits = cpu_to_be64(sctx->count << 3); - u32 padlen = SHA256_BLOCK_SIZE - - ((sctx->count + sizeof(bits)) % SHA256_BLOCK_SIZE); - - sha2_update(desc, padding, padlen); - sha2_update(desc, (const u8 *)&bits, sizeof(bits)); -} - -static int sha224_final(struct shash_desc *desc, u8 *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - int i; - - sha2_final(desc); - - for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); - - *sctx = (struct sha256_state){}; - return 0; -} + struct sha256_ce_state *sctx = shash_desc_ctx(desc); -static int sha256_final(struct shash_desc *desc, u8 *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - int i; - - sha2_final(desc); - - for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); + sctx->finalize = 0; + kernel_neon_begin_partial(28); + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha2_ce_transform); + kernel_neon_end(); - *sctx = (struct sha256_state){}; return 0; } -static void sha2_finup(struct shash_desc *desc, const u8 *data, - unsigned int len) +static int sha256_ce_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha256_state *sctx = shash_desc_ctx(desc); - int blocks; + struct sha256_ce_state *sctx = shash_desc_ctx(desc); + bool finalize = !sctx->sst.count && !(len % SHA256_BLOCK_SIZE); - if (sctx->count || !len || (len % SHA256_BLOCK_SIZE)) { - sha2_update(desc, data, len); - sha2_final(desc); - return; - } + ASM_EXPORT(sha256_ce_offsetof_count, + offsetof(struct sha256_ce_state, sst.count)); + ASM_EXPORT(sha256_ce_offsetof_finalize, + offsetof(struct sha256_ce_state, finalize)); /* - * Use a fast path if the input is a multiple of 64 bytes. In - * this case, there is no need to copy data around, and we can - * perform the entire digest calculation in a single invocation - * of sha2_ce_transform() + * Allow the asm code to perform the finalization if there is no + * partial data and the input is a round multiple of the block size. */ - blocks = len / SHA256_BLOCK_SIZE; + sctx->finalize = finalize; kernel_neon_begin_partial(28); - sha2_ce_transform(blocks, data, sctx->state, NULL, len); + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha2_ce_transform); + if (!finalize) + sha256_base_do_finalize(desc, + (sha256_block_fn *)sha2_ce_transform); kernel_neon_end(); + return sha256_base_finish(desc, out); } -static int sha224_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) +static int sha256_ce_final(struct shash_desc *desc, u8 *out) { - struct sha256_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - int i; - - sha2_finup(desc, data, len); - - for (i = 0; i < SHA224_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); - - *sctx = (struct sha256_state){}; - return 0; -} - -static int sha256_finup(struct shash_desc *desc, const u8 *data, - unsigned int len, u8 *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - int i; - - sha2_finup(desc, data, len); - - for (i = 0; i < SHA256_DIGEST_SIZE / sizeof(__be32); i++) - put_unaligned_be32(sctx->state[i], dst++); - - *sctx = (struct sha256_state){}; - return 0; -} - -static int sha2_export(struct shash_desc *desc, void *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - struct sha256_state *dst = out; - - *dst = *sctx; - return 0; -} - -static int sha2_import(struct shash_desc *desc, const void *in) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - struct sha256_state const *src = in; - - *sctx = *src; - return 0; + kernel_neon_begin_partial(28); + sha256_base_do_finalize(desc, (sha256_block_fn *)sha2_ce_transform); + kernel_neon_end(); + return sha256_base_finish(desc, out); } static struct shash_alg algs[] = { { - .init = sha224_init, - .update = sha2_update, - .final = sha224_final, - .finup = sha224_finup, - .export = sha2_export, - .import = sha2_import, - .descsize = sizeof(struct sha256_state), + .init = sha224_base_init, + .update = sha256_ce_update, + .final = sha256_ce_final, + .finup = sha256_ce_finup, + .descsize = sizeof(struct sha256_ce_state), .digestsize = SHA224_DIGEST_SIZE, - .statesize = sizeof(struct sha256_state), .base = { .cra_name = "sha224", .cra_driver_name = "sha224-ce", @@ -221,15 +97,12 @@ static struct shash_alg algs[] = { { .cra_module = THIS_MODULE, } }, { - .init = sha256_init, - .update = sha2_update, - .final = sha256_final, - .finup = sha256_finup, - .export = sha2_export, - .import = sha2_import, - .descsize = sizeof(struct sha256_state), + .init = sha256_base_init, + .update = sha256_ce_update, + .final = sha256_ce_final, + .finup = sha256_ce_finup, + .descsize = sizeof(struct sha256_ce_state), .digestsize = SHA256_DIGEST_SIZE, - .statesize = sizeof(struct sha256_state), .base = { .cra_name = "sha256", .cra_driver_name = "sha256-ce", diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h index 702e1e6a0d80..dcd06d18a42a 100644 --- a/arch/arm64/include/asm/thread_info.h +++ b/arch/arm64/include/asm/thread_info.h @@ -33,7 +33,6 @@ #ifndef __ASSEMBLY__ struct task_struct; -struct exec_domain; #include <asm/types.h> @@ -47,7 +46,6 @@ struct thread_info { unsigned long flags; /* low level flags */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ int preempt_count; /* 0 => preemptable, <0 => bug */ int cpu; /* cpu */ }; @@ -55,7 +53,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index f7fa65d4c352..56cadd3606bf 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -38,7 +38,6 @@ int main(void) DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); BLANK(); DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context)); diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c index 660ccf9f7524..e18c48cb6db1 100644 --- a/arch/arm64/kernel/signal.c +++ b/arch/arm64/kernel/signal.c @@ -287,19 +287,12 @@ static void setup_restart_syscall(struct pt_regs *regs) */ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) { - struct thread_info *thread = current_thread_info(); struct task_struct *tsk = current; sigset_t *oldset = sigmask_to_save(); int usig = ksig->sig; int ret; /* - * translate the signal - */ - if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap) - usig = thread->exec_domain->signal_invmap[usig]; - - /* * Set up the stack frame */ if (is_compat_task()) { diff --git a/arch/avr32/include/asm/thread_info.h b/arch/avr32/include/asm/thread_info.h index d56afa99a514..d4d3079541ea 100644 --- a/arch/avr32/include/asm/thread_info.h +++ b/arch/avr32/include/asm/thread_info.h @@ -17,11 +17,9 @@ #include <asm/types.h> struct task_struct; -struct exec_domain; struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -36,7 +34,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/avr32/kernel/asm-offsets.c b/arch/avr32/kernel/asm-offsets.c index e41c84516e5d..2c9764fe3532 100644 --- a/arch/avr32/kernel/asm-offsets.c +++ b/arch/avr32/kernel/asm-offsets.c @@ -12,7 +12,6 @@ void foo(void) { OFFSET(TI_task, thread_info, task); - OFFSET(TI_exec_domain, thread_info, exec_domain); OFFSET(TI_flags, thread_info, flags); OFFSET(TI_cpu, thread_info, cpu); OFFSET(TI_preempt_count, thread_info, preempt_count); diff --git a/arch/blackfin/include/asm/thread_info.h b/arch/blackfin/include/asm/thread_info.h index 57c3a8bd583d..2966b93850a1 100644 --- a/arch/blackfin/include/asm/thread_info.h +++ b/arch/blackfin/include/asm/thread_info.h @@ -37,7 +37,6 @@ typedef unsigned long mm_segment_t; struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ int preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -53,7 +52,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ @@ -76,15 +74,6 @@ static inline struct thread_info *current_thread_info(void) #endif /* __ASSEMBLY__ */ /* - * Offsets in thread_info structure, used in assembly code - */ -#define TI_TASK 0 -#define TI_EXECDOMAIN 4 -#define TI_FLAGS 8 -#define TI_CPU 12 -#define TI_PREEMPT 16 - -/* * thread information flag bit numbers */ #define TIF_SYSCALL_TRACE 0 /* syscall trace active */ diff --git a/arch/blackfin/kernel/asm-offsets.c b/arch/blackfin/kernel/asm-offsets.c index 37fcae950216..486560aea050 100644 --- a/arch/blackfin/kernel/asm-offsets.c +++ b/arch/blackfin/kernel/asm-offsets.c @@ -42,6 +42,12 @@ int main(void) DEFINE(THREAD_PC, offsetof(struct thread_struct, pc)); DEFINE(KERNEL_STACK_SIZE, THREAD_SIZE); + /* offsets in thread_info struct */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PREEMPT, thread_info, preempt_count); + /* offsets into the pt_regs */ DEFINE(PT_ORIG_R0, offsetof(struct pt_regs, orig_r0)); DEFINE(PT_ORIG_P0, offsetof(struct pt_regs, orig_p0)); diff --git a/arch/blackfin/kernel/signal.c b/arch/blackfin/kernel/signal.c index f2a8b5493bd3..ea570db598e5 100644 --- a/arch/blackfin/kernel/signal.c +++ b/arch/blackfin/kernel/signal.c @@ -151,11 +151,7 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) frame = get_sigframe(ksig, sizeof(*frame)); - err |= __put_user((current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && ksig->sig < 32 - ? current_thread_info()->exec_domain-> - signal_invmap[ksig->sig] : ksig->sig), &frame->sig); + err |= __put_user(ksig->sig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); diff --git a/arch/blackfin/kernel/traps.c b/arch/blackfin/kernel/traps.c index de5c2c3ebd9b..1ed85ddadc0d 100644 --- a/arch/blackfin/kernel/traps.c +++ b/arch/blackfin/kernel/traps.c @@ -18,6 +18,7 @@ #include <asm/fixed_code.h> #include <asm/pseudo_instructions.h> #include <asm/pda.h> +#include <asm/asm-offsets.h> #ifdef CONFIG_KGDB # include <linux/kgdb.h> diff --git a/arch/c6x/include/asm/thread_info.h b/arch/c6x/include/asm/thread_info.h index 584e253f3217..acc70c135ab8 100644 --- a/arch/c6x/include/asm/thread_info.h +++ b/arch/c6x/include/asm/thread_info.h @@ -40,7 +40,6 @@ typedef struct { */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ int preempt_count; /* 0 = preemptable, <0 = BUG */ @@ -55,7 +54,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/cris/arch-v10/kernel/signal.c b/arch/cris/arch-v10/kernel/signal.c index 74d7ba35120d..7122d9773b13 100644 --- a/arch/cris/arch-v10/kernel/signal.c +++ b/arch/cris/arch-v10/kernel/signal.c @@ -321,8 +321,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, if (err) return -EFAULT; - /* TODO what is the current->exec_domain stuff and invmap ? */ - /* Set up registers for signal handler */ /* What we enter NOW */ diff --git a/arch/cris/arch-v32/kernel/signal.c b/arch/cris/arch-v32/kernel/signal.c index 870e3e069318..0c9ce9eac614 100644 --- a/arch/cris/arch-v32/kernel/signal.c +++ b/arch/cris/arch-v32/kernel/signal.c @@ -287,8 +287,6 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - /* TODO: what is the current->exec_domain stuff and invmap ? */ - err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, &ksig->info); diff --git a/arch/cris/include/asm/thread_info.h b/arch/cris/include/asm/thread_info.h index 7286db5ed90e..4ead1b40d2d7 100644 --- a/arch/cris/include/asm/thread_info.h +++ b/arch/cris/include/asm/thread_info.h @@ -28,7 +28,6 @@ #ifndef __ASSEMBLY__ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -50,7 +49,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/frv/include/asm/thread_info.h b/arch/frv/include/asm/thread_info.h index 6b917f1c2955..ccba3b6ce918 100644 --- a/arch/frv/include/asm/thread_info.h +++ b/arch/frv/include/asm/thread_info.h @@ -31,7 +31,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ @@ -59,7 +58,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/frv/kernel/asm-offsets.c b/arch/frv/kernel/asm-offsets.c index 446e89d500cc..8414293f213a 100644 --- a/arch/frv/kernel/asm-offsets.c +++ b/arch/frv/kernel/asm-offsets.c @@ -34,7 +34,6 @@ void foo(void) { /* offsets into the thread_info structure */ OFFSET(TI_TASK, thread_info, task); - OFFSET(TI_EXEC_DOMAIN, thread_info, exec_domain); OFFSET(TI_FLAGS, thread_info, flags); OFFSET(TI_STATUS, thread_info, status); OFFSET(TI_CPU, thread_info, cpu); diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c index 85ca6727ca07..82d5e914dc15 100644 --- a/arch/frv/kernel/signal.c +++ b/arch/frv/kernel/signal.c @@ -174,20 +174,14 @@ static inline void __user *get_sigframe(struct ksignal *ksig, static int setup_frame(struct ksignal *ksig, sigset_t *set) { struct sigframe __user *frame; - int rsig, sig = ksig->sig; + int sig = ksig->sig; frame = get_sigframe(ksig, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - rsig = sig; - if (sig < 32 && - __current_thread_info->exec_domain && - __current_thread_info->exec_domain->signal_invmap) - rsig = __current_thread_info->exec_domain->signal_invmap[sig]; - - if (__put_user(rsig, &frame->sig) < 0) + if (__put_user(sig, &frame->sig) < 0) return -EFAULT; if (setup_sigcontext(&frame->sc, set->sig[0])) @@ -253,20 +247,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set) static int setup_rt_frame(struct ksignal *ksig, sigset_t *set) { struct rt_sigframe __user *frame; - int rsig, sig = ksig->sig; + int sig = ksig->sig; frame = get_sigframe(ksig, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - rsig = sig; - if (sig < 32 && - __current_thread_info->exec_domain && - __current_thread_info->exec_domain->signal_invmap) - rsig = __current_thread_info->exec_domain->signal_invmap[sig]; - - if (__put_user(rsig, &frame->sig) || + if (__put_user(sig, &frame->sig) || __put_user(&frame->info, &frame->pinfo) || __put_user(&frame->uc, &frame->puc)) return -EFAULT; diff --git a/arch/hexagon/include/asm/thread_info.h b/arch/hexagon/include/asm/thread_info.h index bacd3d6030c5..b80fe1db7b64 100644 --- a/arch/hexagon/include/asm/thread_info.h +++ b/arch/hexagon/include/asm/thread_info.h @@ -47,7 +47,6 @@ typedef struct { struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current cpu */ int preempt_count; /* 0=>preemptible,<0=>BUG */ @@ -77,7 +76,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = 1, \ diff --git a/arch/ia64/include/asm/thread_info.h b/arch/ia64/include/asm/thread_info.h index c16f21a068ff..aa995b67c3f5 100644 --- a/arch/ia64/include/asm/thread_info.h +++ b/arch/ia64/include/asm/thread_info.h @@ -20,7 +20,6 @@ */ struct thread_info { struct task_struct *task; /* XXX not really needed, except for dup_task_struct() */ - struct exec_domain *exec_domain;/* execution domain */ __u32 flags; /* thread_info flags (see TIF_*) */ __u32 cpu; /* current CPU */ __u32 last_cpu; /* Last CPU thread ran on */ @@ -40,7 +39,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile index 20678a9ed11a..d68b5cf81e31 100644 --- a/arch/ia64/kernel/Makefile +++ b/arch/ia64/kernel/Makefile @@ -51,7 +51,7 @@ obj-$(CONFIG_BINFMT_ELF) += elfcore.o CFLAGS_traps.o += -mfixed-range=f2-f5,f16-f31 # The gate DSO image is built using a special linker script. -include $(srctree)/arch/ia64/kernel/Makefile.gate +include $(src)/Makefile.gate # tell compiled for native CPPFLAGS_gate.lds += -D__IA64_GATE_PARAVIRTUALIZED_NATIVE diff --git a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c index 6b3345758d3e..a9b65cf7b34a 100644 --- a/arch/ia64/mm/init.c +++ b/arch/ia64/mm/init.c @@ -692,31 +692,6 @@ int arch_remove_memory(u64 start, u64 size) #endif #endif -/* - * Even when CONFIG_IA32_SUPPORT is not enabled it is - * useful to have the Linux/x86 domain registered to - * avoid an attempted module load when emulators call - * personality(PER_LINUX32). This saves several milliseconds - * on each such call. - */ -static struct exec_domain ia32_exec_domain; - -static int __init -per_linux32_init(void) -{ - ia32_exec_domain.name = "Linux/x86"; - ia32_exec_domain.handler = NULL; - ia32_exec_domain.pers_low = PER_LINUX32; - ia32_exec_domain.pers_high = PER_LINUX32; - ia32_exec_domain.signal_map = default_exec_domain.signal_map; - ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap; - register_exec_domain(&ia32_exec_domain); - - return 0; -} - -__initcall(per_linux32_init); - /** * show_mem - give short summary of memory stats * diff --git a/arch/m32r/include/asm/asm-offsets.h b/arch/m32r/include/asm/asm-offsets.h new file mode 100644 index 000000000000..d370ee36a182 --- /dev/null +++ b/arch/m32r/include/asm/asm-offsets.h @@ -0,0 +1 @@ +#include <generated/asm-offsets.h> diff --git a/arch/m32r/include/asm/thread_info.h b/arch/m32r/include/asm/thread_info.h index 32422d0211c3..f630d9c30b28 100644 --- a/arch/m32r/include/asm/thread_info.h +++ b/arch/m32r/include/asm/thread_info.h @@ -24,7 +24,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ @@ -38,18 +37,7 @@ struct thread_info { __u8 supervisor_stack[0]; }; -#else /* !__ASSEMBLY__ */ - -/* offsets into the thread_info struct for assembly code access */ -#define TI_TASK 0x00000000 -#define TI_EXEC_DOMAIN 0x00000004 -#define TI_FLAGS 0x00000008 -#define TI_STATUS 0x0000000C -#define TI_CPU 0x00000010 -#define TI_PRE_COUNT 0x00000014 -#define TI_ADDR_LIMIT 0x00000018 - -#endif +#endif /* !__ASSEMBLY__ */ #define THREAD_SIZE (PAGE_SIZE << 1) #define THREAD_SIZE_ORDER 1 @@ -61,7 +49,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/m32r/kernel/asm-offsets.c b/arch/m32r/kernel/asm-offsets.c index 9e263112a6e2..cd3d2fc9c8df 100644 --- a/arch/m32r/kernel/asm-offsets.c +++ b/arch/m32r/kernel/asm-offsets.c @@ -1 +1,14 @@ -/* Dummy asm-offsets.c file. Required by kbuild and ready to be used - hint! */ +#include <linux/thread_info.h> +#include <linux/kbuild.h> + +int foo(void) +{ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_STATUS, thread_info, status); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PRE_COUNT, thread_info, preempt_count); + OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit); + + return 0; +} diff --git a/arch/m32r/kernel/entry.S b/arch/m32r/kernel/entry.S index 7c3db9940ce1..c639bfa32232 100644 --- a/arch/m32r/kernel/entry.S +++ b/arch/m32r/kernel/entry.S @@ -65,6 +65,7 @@ #include <asm/page.h> #include <asm/m32r.h> #include <asm/mmu_context.h> +#include <asm/asm-offsets.h> #if !defined(CONFIG_MMU) #define sys_madvise sys_ni_syscall diff --git a/arch/m32r/kernel/signal.c b/arch/m32r/kernel/signal.c index 8c25e0c8f6a5..1c81e24fd006 100644 --- a/arch/m32r/kernel/signal.c +++ b/arch/m32r/kernel/signal.c @@ -172,20 +172,14 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, { struct rt_sigframe __user *frame; int err = 0; - int signal, sig = ksig->sig; + int sig = ksig->sig; frame = get_sigframe(ksig, regs->spu, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - - err |= __put_user(signal, &frame->sig); + err |= __put_user(sig, &frame->sig); if (err) return -EFAULT; @@ -209,7 +203,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, /* Set up registers for signal handler */ regs->spu = (unsigned long)frame; - regs->r0 = signal; /* Arg for signal handler */ + regs->r0 = sig; /* Arg for signal handler */ regs->r1 = (unsigned long)&frame->info; regs->r2 = (unsigned long)&frame->uc; regs->bpc = (unsigned long)ksig->ka.sa.sa_handler; diff --git a/arch/m68k/include/asm/thread_info.h b/arch/m68k/include/asm/thread_info.h index c54256e69e64..cee13c2e5161 100644 --- a/arch/m68k/include/asm/thread_info.h +++ b/arch/m68k/include/asm/thread_info.h @@ -26,7 +26,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ unsigned long flags; - struct exec_domain *exec_domain; /* execution domain */ mm_segment_t addr_limit; /* thread address space */ int preempt_count; /* 0 => preemptable, <0 => BUG */ __u32 cpu; /* should always be 0 on m68k */ @@ -37,7 +36,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .addr_limit = KERNEL_DS, \ .preempt_count = INIT_PREEMPT_COUNT, \ } diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index d7179281e74a..af1c4f330aef 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -863,12 +863,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, if (fsize) err |= copy_to_user (frame + 1, regs + 1, fsize); - err |= __put_user((current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); + err |= __put_user(sig, &frame->sig); err |= __put_user(regs->vector, &frame->code); err |= __put_user(&frame->sc, &frame->psc); @@ -948,12 +943,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, if (fsize) err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); - err |= __put_user((current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig), - &frame->sig); + err |= __put_user(sig, &frame->sig); err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, &ksig->info); diff --git a/arch/metag/include/asm/thread_info.h b/arch/metag/include/asm/thread_info.h index afb3ca4776d1..32677cc278aa 100644 --- a/arch/metag/include/asm/thread_info.h +++ b/arch/metag/include/asm/thread_info.h @@ -28,7 +28,6 @@ /* This must be 8 byte aligned so we can ensure stack alignment. */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ u32 cpu; /* current CPU */ @@ -68,7 +67,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/metag/kernel/irq.c b/arch/metag/kernel/irq.c index 5385dd1216b7..4f8f1f87ef11 100644 --- a/arch/metag/kernel/irq.c +++ b/arch/metag/kernel/irq.c @@ -132,7 +132,6 @@ void irq_ctx_init(int cpu) irqctx = (union irq_ctx *) &hardirq_stack[cpu * THREAD_SIZE]; irqctx->tinfo.task = NULL; - irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.cpu = cpu; irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); @@ -141,7 +140,6 @@ void irq_ctx_init(int cpu) irqctx = (union irq_ctx *) &softirq_stack[cpu * THREAD_SIZE]; irqctx->tinfo.task = NULL; - irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.cpu = cpu; irqctx->tinfo.preempt_count = 0; irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); diff --git a/arch/microblaze/include/asm/thread_info.h b/arch/microblaze/include/asm/thread_info.h index b699fbd7de4a..383f387b4eee 100644 --- a/arch/microblaze/include/asm/thread_info.h +++ b/arch/microblaze/include/asm/thread_info.h @@ -65,7 +65,6 @@ typedef struct { struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ @@ -81,7 +80,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/microblaze/kernel/signal.c b/arch/microblaze/kernel/signal.c index 20ccd4e2baa5..97001524ca2d 100644 --- a/arch/microblaze/kernel/signal.c +++ b/arch/microblaze/kernel/signal.c @@ -158,7 +158,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, { struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - unsigned long signal; unsigned long address = 0; #ifdef CONFIG_MMU pmd_t *pmdp; @@ -170,12 +169,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : (unsigned long)sig; - if (ksig->ka.sa.sa_flags & SA_SIGINFO) err |= copy_siginfo_to_user(&frame->info, &ksig->info); @@ -230,7 +223,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, regs->r1 = (unsigned long) frame; /* Signal handler args: */ - regs->r5 = signal; /* arg 0: signum */ + regs->r5 = sig; /* arg 0: signum */ regs->r6 = (unsigned long) &frame->info; /* arg 1: siginfo */ regs->r7 = (unsigned long) &frame->uc; /* arg2: ucontext */ /* Offset to handle microblaze rtid r14, 0 */ diff --git a/arch/mips/Makefile b/arch/mips/Makefile index 8f57fc72d62c..d152dfbc360d 100644 --- a/arch/mips/Makefile +++ b/arch/mips/Makefile @@ -225,7 +225,7 @@ endif # # Board-dependent options and extra files # -include $(srctree)/arch/mips/Kbuild.platforms +include arch/mips/Kbuild.platforms ifdef CONFIG_PHYSICAL_START load-y = $(CONFIG_PHYSICAL_START) diff --git a/arch/mips/cavium-octeon/crypto/Makefile b/arch/mips/cavium-octeon/crypto/Makefile index a74f76d85a2f..f7aa9d5d3b87 100644 --- a/arch/mips/cavium-octeon/crypto/Makefile +++ b/arch/mips/cavium-octeon/crypto/Makefile @@ -4,4 +4,7 @@ obj-y += octeon-crypto.o -obj-$(CONFIG_CRYPTO_MD5_OCTEON) += octeon-md5.o +obj-$(CONFIG_CRYPTO_MD5_OCTEON) += octeon-md5.o +obj-$(CONFIG_CRYPTO_SHA1_OCTEON) += octeon-sha1.o +obj-$(CONFIG_CRYPTO_SHA256_OCTEON) += octeon-sha256.o +obj-$(CONFIG_CRYPTO_SHA512_OCTEON) += octeon-sha512.o diff --git a/arch/mips/cavium-octeon/crypto/octeon-crypto.c b/arch/mips/cavium-octeon/crypto/octeon-crypto.c index 7c82ff463b65..f66bd1adc7ff 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-crypto.c +++ b/arch/mips/cavium-octeon/crypto/octeon-crypto.c @@ -17,7 +17,7 @@ * crypto operations in calls to octeon_crypto_enable/disable in order to make * sure the state of COP2 isn't corrupted if userspace is also performing * hardware crypto operations. Allocate the state parameter on the stack. - * Preemption must be disabled to prevent context switches. + * Returns with preemption disabled. * * @state: Pointer to state structure to store current COP2 state in. * @@ -28,6 +28,7 @@ unsigned long octeon_crypto_enable(struct octeon_cop2_state *state) int status; unsigned long flags; + preempt_disable(); local_irq_save(flags); status = read_c0_status(); write_c0_status(status | ST0_CU2); @@ -62,5 +63,6 @@ void octeon_crypto_disable(struct octeon_cop2_state *state, else write_c0_status(read_c0_status() & ~ST0_CU2); local_irq_restore(flags); + preempt_enable(); } EXPORT_SYMBOL_GPL(octeon_crypto_disable); diff --git a/arch/mips/cavium-octeon/crypto/octeon-crypto.h b/arch/mips/cavium-octeon/crypto/octeon-crypto.h index e2a4aece9c24..355072535110 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-crypto.h +++ b/arch/mips/cavium-octeon/crypto/octeon-crypto.h @@ -5,7 +5,8 @@ * * Copyright (C) 2012-2013 Cavium Inc., All Rights Reserved. * - * MD5 instruction definitions added by Aaro Koskinen <aaro.koskinen@iki.fi>. + * MD5/SHA1/SHA256/SHA512 instruction definitions added by + * Aaro Koskinen <aaro.koskinen@iki.fi>. * */ #ifndef __LINUX_OCTEON_CRYPTO_H @@ -21,11 +22,11 @@ extern void octeon_crypto_disable(struct octeon_cop2_state *state, unsigned long flags); /* - * Macros needed to implement MD5: + * Macros needed to implement MD5/SHA1/SHA256: */ /* - * The index can be 0-1. + * The index can be 0-1 (MD5) or 0-2 (SHA1), 0-3 (SHA256). */ #define write_octeon_64bit_hash_dword(value, index) \ do { \ @@ -36,7 +37,7 @@ do { \ } while (0) /* - * The index can be 0-1. + * The index can be 0-1 (MD5) or 0-2 (SHA1), 0-3 (SHA256). */ #define read_octeon_64bit_hash_dword(index) \ ({ \ @@ -72,4 +73,78 @@ do { \ : [rt] "d" (value)); \ } while (0) +/* + * The value is the final block dword (64-bit). + */ +#define octeon_sha1_start(value) \ +do { \ + __asm__ __volatile__ ( \ + "dmtc2 %[rt],0x4057" \ + : \ + : [rt] "d" (value)); \ +} while (0) + +/* + * The value is the final block dword (64-bit). + */ +#define octeon_sha256_start(value) \ +do { \ + __asm__ __volatile__ ( \ + "dmtc2 %[rt],0x404f" \ + : \ + : [rt] "d" (value)); \ +} while (0) + +/* + * Macros needed to implement SHA512: + */ + +/* + * The index can be 0-7. + */ +#define write_octeon_64bit_hash_sha512(value, index) \ +do { \ + __asm__ __volatile__ ( \ + "dmtc2 %[rt],0x0250+" STR(index) \ + : \ + : [rt] "d" (value)); \ +} while (0) + +/* + * The index can be 0-7. + */ +#define read_octeon_64bit_hash_sha512(index) \ +({ \ + u64 __value; \ + \ + __asm__ __volatile__ ( \ + "dmfc2 %[rt],0x0250+" STR(index) \ + : [rt] "=d" (__value) \ + : ); \ + \ + __value; \ +}) + +/* + * The index can be 0-14. + */ +#define write_octeon_64bit_block_sha512(value, index) \ +do { \ + __asm__ __volatile__ ( \ + "dmtc2 %[rt],0x0240+" STR(index) \ + : \ + : [rt] "d" (value)); \ +} while (0) + +/* + * The value is the final block word (64-bit). + */ +#define octeon_sha512_start(value) \ +do { \ + __asm__ __volatile__ ( \ + "dmtc2 %[rt],0x424f" \ + : \ + : [rt] "d" (value)); \ +} while (0) + #endif /* __LINUX_OCTEON_CRYPTO_H */ diff --git a/arch/mips/cavium-octeon/crypto/octeon-md5.c b/arch/mips/cavium-octeon/crypto/octeon-md5.c index b909881ba6c1..12dccdb38286 100644 --- a/arch/mips/cavium-octeon/crypto/octeon-md5.c +++ b/arch/mips/cavium-octeon/crypto/octeon-md5.c @@ -97,8 +97,6 @@ static int octeon_md5_update(struct shash_desc *desc, const u8 *data, memcpy((char *)mctx->block + (sizeof(mctx->block) - avail), data, avail); - local_bh_disable(); - preempt_disable(); flags = octeon_crypto_enable(&state); octeon_md5_store_hash(mctx); @@ -114,8 +112,6 @@ static int octeon_md5_update(struct shash_desc *desc, const u8 *data, octeon_md5_read_hash(mctx); octeon_crypto_disable(&state, flags); - preempt_enable(); - local_bh_enable(); memcpy(mctx->block, data, len); @@ -133,8 +129,6 @@ static int octeon_md5_final(struct shash_desc *desc, u8 *out) *p++ = 0x80; - local_bh_disable(); - preempt_disable(); flags = octeon_crypto_enable(&state); octeon_md5_store_hash(mctx); @@ -152,8 +146,6 @@ static int octeon_md5_final(struct shash_desc *desc, u8 *out) octeon_md5_read_hash(mctx); octeon_crypto_disable(&state, flags); - preempt_enable(); - local_bh_enable(); memcpy(out, mctx->hash, sizeof(mctx->hash)); memset(mctx, 0, sizeof(*mctx)); diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha1.c b/arch/mips/cavium-octeon/crypto/octeon-sha1.c new file mode 100644 index 000000000000..2b74b5b67cae --- /dev/null +++ b/arch/mips/cavium-octeon/crypto/octeon-sha1.c @@ -0,0 +1,241 @@ +/* + * Cryptographic API. + * + * SHA1 Secure Hash Algorithm. + * + * Adapted for OCTEON by Aaro Koskinen <aaro.koskinen@iki.fi>. + * + * Based on crypto/sha1_generic.c, which is: + * + * Copyright (c) Alan Smithee. + * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk> + * Copyright (c) Jean-Francois Dive <jef@linuxbe.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/mm.h> +#include <crypto/sha.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <asm/byteorder.h> +#include <asm/octeon/octeon.h> +#include <crypto/internal/hash.h> + +#include "octeon-crypto.h" + +/* + * We pass everything as 64-bit. OCTEON can handle misaligned data. + */ + +static void octeon_sha1_store_hash(struct sha1_state *sctx) +{ + u64 *hash = (u64 *)sctx->state; + union { + u32 word[2]; + u64 dword; + } hash_tail = { { sctx->state[4], } }; + + write_octeon_64bit_hash_dword(hash[0], 0); + write_octeon_64bit_hash_dword(hash[1], 1); + write_octeon_64bit_hash_dword(hash_tail.dword, 2); + memzero_explicit(&hash_tail.word[0], sizeof(hash_tail.word[0])); +} + +static void octeon_sha1_read_hash(struct sha1_state *sctx) +{ + u64 *hash = (u64 *)sctx->state; + union { + u32 word[2]; + u64 dword; + } hash_tail; + + hash[0] = read_octeon_64bit_hash_dword(0); + hash[1] = read_octeon_64bit_hash_dword(1); + hash_tail.dword = read_octeon_64bit_hash_dword(2); + sctx->state[4] = hash_tail.word[0]; + memzero_explicit(&hash_tail.dword, sizeof(hash_tail.dword)); +} + +static void octeon_sha1_transform(const void *_block) +{ + const u64 *block = _block; + + write_octeon_64bit_block_dword(block[0], 0); + write_octeon_64bit_block_dword(block[1], 1); + write_octeon_64bit_block_dword(block[2], 2); + write_octeon_64bit_block_dword(block[3], 3); + write_octeon_64bit_block_dword(block[4], 4); + write_octeon_64bit_block_dword(block[5], 5); + write_octeon_64bit_block_dword(block[6], 6); + octeon_sha1_start(block[7]); +} + +static int octeon_sha1_init(struct shash_desc *desc) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA1_H0; + sctx->state[1] = SHA1_H1; + sctx->state[2] = SHA1_H2; + sctx->state[3] = SHA1_H3; + sctx->state[4] = SHA1_H4; + sctx->count = 0; + + return 0; +} + +static void __octeon_sha1_update(struct sha1_state *sctx, const u8 *data, + unsigned int len) +{ + unsigned int partial; + unsigned int done; + const u8 *src; + + partial = sctx->count % SHA1_BLOCK_SIZE; + sctx->count += len; + done = 0; + src = data; + + if ((partial + len) >= SHA1_BLOCK_SIZE) { + if (partial) { + done = -partial; + memcpy(sctx->buffer + partial, data, + done + SHA1_BLOCK_SIZE); + src = sctx->buffer; + } + + do { + octeon_sha1_transform(src); + done += SHA1_BLOCK_SIZE; + src = data + done; + } while (done + SHA1_BLOCK_SIZE <= len); + + partial = 0; + } + memcpy(sctx->buffer + partial, src, len - done); +} + +static int octeon_sha1_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + struct octeon_cop2_state state; + unsigned long flags; + + /* + * Small updates never reach the crypto engine, so the generic sha1 is + * faster because of the heavyweight octeon_crypto_enable() / + * octeon_crypto_disable(). + */ + if ((sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE) + return crypto_sha1_update(desc, data, len); + + flags = octeon_crypto_enable(&state); + octeon_sha1_store_hash(sctx); + + __octeon_sha1_update(sctx, data, len); + + octeon_sha1_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + return 0; +} + +static int octeon_sha1_final(struct shash_desc *desc, u8 *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + static const u8 padding[64] = { 0x80, }; + struct octeon_cop2_state state; + __be32 *dst = (__be32 *)out; + unsigned int pad_len; + unsigned long flags; + unsigned int index; + __be64 bits; + int i; + + /* Save number of bits. */ + bits = cpu_to_be64(sctx->count << 3); + + /* Pad out to 56 mod 64. */ + index = sctx->count & 0x3f; + pad_len = (index < 56) ? (56 - index) : ((64+56) - index); + + flags = octeon_crypto_enable(&state); + octeon_sha1_store_hash(sctx); + + __octeon_sha1_update(sctx, padding, pad_len); + + /* Append length (before padding). */ + __octeon_sha1_update(sctx, (const u8 *)&bits, sizeof(bits)); + + octeon_sha1_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + /* Store state in digest */ + for (i = 0; i < 5; i++) + dst[i] = cpu_to_be32(sctx->state[i]); + + /* Zeroize sensitive information. */ + memset(sctx, 0, sizeof(*sctx)); + + return 0; +} + +static int octeon_sha1_export(struct shash_desc *desc, void *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int octeon_sha1_import(struct shash_desc *desc, const void *in) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg octeon_sha1_alg = { + .digestsize = SHA1_DIGEST_SIZE, + .init = octeon_sha1_init, + .update = octeon_sha1_update, + .final = octeon_sha1_final, + .export = octeon_sha1_export, + .import = octeon_sha1_import, + .descsize = sizeof(struct sha1_state), + .statesize = sizeof(struct sha1_state), + .base = { + .cra_name = "sha1", + .cra_driver_name= "octeon-sha1", + .cra_priority = OCTEON_CR_OPCODE_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init octeon_sha1_mod_init(void) +{ + if (!octeon_has_crypto()) + return -ENOTSUPP; + return crypto_register_shash(&octeon_sha1_alg); +} + +static void __exit octeon_sha1_mod_fini(void) +{ + crypto_unregister_shash(&octeon_sha1_alg); +} + +module_init(octeon_sha1_mod_init); +module_exit(octeon_sha1_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm (OCTEON)"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha256.c b/arch/mips/cavium-octeon/crypto/octeon-sha256.c new file mode 100644 index 000000000000..97e96fead08a --- /dev/null +++ b/arch/mips/cavium-octeon/crypto/octeon-sha256.c @@ -0,0 +1,280 @@ +/* + * Cryptographic API. + * + * SHA-224 and SHA-256 Secure Hash Algorithm. + * + * Adapted for OCTEON by Aaro Koskinen <aaro.koskinen@iki.fi>. + * + * Based on crypto/sha256_generic.c, which is: + * + * Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com> + * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk> + * Copyright (c) 2002 James Morris <jmorris@intercode.com.au> + * SHA224 Support Copyright 2007 Intel Corporation <jonathan.lynch@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/mm.h> +#include <crypto/sha.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <asm/byteorder.h> +#include <asm/octeon/octeon.h> +#include <crypto/internal/hash.h> + +#include "octeon-crypto.h" + +/* + * We pass everything as 64-bit. OCTEON can handle misaligned data. + */ + +static void octeon_sha256_store_hash(struct sha256_state *sctx) +{ + u64 *hash = (u64 *)sctx->state; + + write_octeon_64bit_hash_dword(hash[0], 0); + write_octeon_64bit_hash_dword(hash[1], 1); + write_octeon_64bit_hash_dword(hash[2], 2); + write_octeon_64bit_hash_dword(hash[3], 3); +} + +static void octeon_sha256_read_hash(struct sha256_state *sctx) +{ + u64 *hash = (u64 *)sctx->state; + + hash[0] = read_octeon_64bit_hash_dword(0); + hash[1] = read_octeon_64bit_hash_dword(1); + hash[2] = read_octeon_64bit_hash_dword(2); + hash[3] = read_octeon_64bit_hash_dword(3); +} + +static void octeon_sha256_transform(const void *_block) +{ + const u64 *block = _block; + + write_octeon_64bit_block_dword(block[0], 0); + write_octeon_64bit_block_dword(block[1], 1); + write_octeon_64bit_block_dword(block[2], 2); + write_octeon_64bit_block_dword(block[3], 3); + write_octeon_64bit_block_dword(block[4], 4); + write_octeon_64bit_block_dword(block[5], 5); + write_octeon_64bit_block_dword(block[6], 6); + octeon_sha256_start(block[7]); +} + +static int octeon_sha224_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA224_H0; + sctx->state[1] = SHA224_H1; + sctx->state[2] = SHA224_H2; + sctx->state[3] = SHA224_H3; + sctx->state[4] = SHA224_H4; + sctx->state[5] = SHA224_H5; + sctx->state[6] = SHA224_H6; + sctx->state[7] = SHA224_H7; + sctx->count = 0; + + return 0; +} + +static int octeon_sha256_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +static void __octeon_sha256_update(struct sha256_state *sctx, const u8 *data, + unsigned int len) +{ + unsigned int partial; + unsigned int done; + const u8 *src; + + partial = sctx->count % SHA256_BLOCK_SIZE; + sctx->count += len; + done = 0; + src = data; + + if ((partial + len) >= SHA256_BLOCK_SIZE) { + if (partial) { + done = -partial; + memcpy(sctx->buf + partial, data, + done + SHA256_BLOCK_SIZE); + src = sctx->buf; + } + + do { + octeon_sha256_transform(src); + done += SHA256_BLOCK_SIZE; + src = data + done; + } while (done + SHA256_BLOCK_SIZE <= len); + + partial = 0; + } + memcpy(sctx->buf + partial, src, len - done); +} + +static int octeon_sha256_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + struct octeon_cop2_state state; + unsigned long flags; + + /* + * Small updates never reach the crypto engine, so the generic sha256 is + * faster because of the heavyweight octeon_crypto_enable() / + * octeon_crypto_disable(). + */ + if ((sctx->count % SHA256_BLOCK_SIZE) + len < SHA256_BLOCK_SIZE) + return crypto_sha256_update(desc, data, len); + + flags = octeon_crypto_enable(&state); + octeon_sha256_store_hash(sctx); + + __octeon_sha256_update(sctx, data, len); + + octeon_sha256_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + return 0; +} + +static int octeon_sha256_final(struct shash_desc *desc, u8 *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + static const u8 padding[64] = { 0x80, }; + struct octeon_cop2_state state; + __be32 *dst = (__be32 *)out; + unsigned int pad_len; + unsigned long flags; + unsigned int index; + __be64 bits; + int i; + + /* Save number of bits. */ + bits = cpu_to_be64(sctx->count << 3); + + /* Pad out to 56 mod 64. */ + index = sctx->count & 0x3f; + pad_len = (index < 56) ? (56 - index) : ((64+56) - index); + + flags = octeon_crypto_enable(&state); + octeon_sha256_store_hash(sctx); + + __octeon_sha256_update(sctx, padding, pad_len); + + /* Append length (before padding). */ + __octeon_sha256_update(sctx, (const u8 *)&bits, sizeof(bits)); + + octeon_sha256_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + /* Store state in digest */ + for (i = 0; i < 8; i++) + dst[i] = cpu_to_be32(sctx->state[i]); + + /* Zeroize sensitive information. */ + memset(sctx, 0, sizeof(*sctx)); + + return 0; +} + +static int octeon_sha224_final(struct shash_desc *desc, u8 *hash) +{ + u8 D[SHA256_DIGEST_SIZE]; + + octeon_sha256_final(desc, D); + + memcpy(hash, D, SHA224_DIGEST_SIZE); + memzero_explicit(D, SHA256_DIGEST_SIZE); + + return 0; +} + +static int octeon_sha256_export(struct shash_desc *desc, void *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int octeon_sha256_import(struct shash_desc *desc, const void *in) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg octeon_sha256_algs[2] = { { + .digestsize = SHA256_DIGEST_SIZE, + .init = octeon_sha256_init, + .update = octeon_sha256_update, + .final = octeon_sha256_final, + .export = octeon_sha256_export, + .import = octeon_sha256_import, + .descsize = sizeof(struct sha256_state), + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name= "octeon-sha256", + .cra_priority = OCTEON_CR_OPCODE_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .digestsize = SHA224_DIGEST_SIZE, + .init = octeon_sha224_init, + .update = octeon_sha256_update, + .final = octeon_sha224_final, + .descsize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name= "octeon-sha224", + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init octeon_sha256_mod_init(void) +{ + if (!octeon_has_crypto()) + return -ENOTSUPP; + return crypto_register_shashes(octeon_sha256_algs, + ARRAY_SIZE(octeon_sha256_algs)); +} + +static void __exit octeon_sha256_mod_fini(void) +{ + crypto_unregister_shashes(octeon_sha256_algs, + ARRAY_SIZE(octeon_sha256_algs)); +} + +module_init(octeon_sha256_mod_init); +module_exit(octeon_sha256_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA-224 and SHA-256 Secure Hash Algorithm (OCTEON)"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); diff --git a/arch/mips/cavium-octeon/crypto/octeon-sha512.c b/arch/mips/cavium-octeon/crypto/octeon-sha512.c new file mode 100644 index 000000000000..d5fb3c6f22ae --- /dev/null +++ b/arch/mips/cavium-octeon/crypto/octeon-sha512.c @@ -0,0 +1,277 @@ +/* + * Cryptographic API. + * + * SHA-512 and SHA-384 Secure Hash Algorithm. + * + * Adapted for OCTEON by Aaro Koskinen <aaro.koskinen@iki.fi>. + * + * Based on crypto/sha512_generic.c, which is: + * + * Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com> + * Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk> + * Copyright (c) 2003 Kyle McMartin <kyle@debian.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + */ + +#include <linux/mm.h> +#include <crypto/sha.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/module.h> +#include <asm/byteorder.h> +#include <asm/octeon/octeon.h> +#include <crypto/internal/hash.h> + +#include "octeon-crypto.h" + +/* + * We pass everything as 64-bit. OCTEON can handle misaligned data. + */ + +static void octeon_sha512_store_hash(struct sha512_state *sctx) +{ + write_octeon_64bit_hash_sha512(sctx->state[0], 0); + write_octeon_64bit_hash_sha512(sctx->state[1], 1); + write_octeon_64bit_hash_sha512(sctx->state[2], 2); + write_octeon_64bit_hash_sha512(sctx->state[3], 3); + write_octeon_64bit_hash_sha512(sctx->state[4], 4); + write_octeon_64bit_hash_sha512(sctx->state[5], 5); + write_octeon_64bit_hash_sha512(sctx->state[6], 6); + write_octeon_64bit_hash_sha512(sctx->state[7], 7); +} + +static void octeon_sha512_read_hash(struct sha512_state *sctx) +{ + sctx->state[0] = read_octeon_64bit_hash_sha512(0); + sctx->state[1] = read_octeon_64bit_hash_sha512(1); + sctx->state[2] = read_octeon_64bit_hash_sha512(2); + sctx->state[3] = read_octeon_64bit_hash_sha512(3); + sctx->state[4] = read_octeon_64bit_hash_sha512(4); + sctx->state[5] = read_octeon_64bit_hash_sha512(5); + sctx->state[6] = read_octeon_64bit_hash_sha512(6); + sctx->state[7] = read_octeon_64bit_hash_sha512(7); +} + +static void octeon_sha512_transform(const void *_block) +{ + const u64 *block = _block; + + write_octeon_64bit_block_sha512(block[0], 0); + write_octeon_64bit_block_sha512(block[1], 1); + write_octeon_64bit_block_sha512(block[2], 2); + write_octeon_64bit_block_sha512(block[3], 3); + write_octeon_64bit_block_sha512(block[4], 4); + write_octeon_64bit_block_sha512(block[5], 5); + write_octeon_64bit_block_sha512(block[6], 6); + write_octeon_64bit_block_sha512(block[7], 7); + write_octeon_64bit_block_sha512(block[8], 8); + write_octeon_64bit_block_sha512(block[9], 9); + write_octeon_64bit_block_sha512(block[10], 10); + write_octeon_64bit_block_sha512(block[11], 11); + write_octeon_64bit_block_sha512(block[12], 12); + write_octeon_64bit_block_sha512(block[13], 13); + write_octeon_64bit_block_sha512(block[14], 14); + octeon_sha512_start(block[15]); +} + +static int octeon_sha512_init(struct shash_desc *desc) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA512_H0; + sctx->state[1] = SHA512_H1; + sctx->state[2] = SHA512_H2; + sctx->state[3] = SHA512_H3; + sctx->state[4] = SHA512_H4; + sctx->state[5] = SHA512_H5; + sctx->state[6] = SHA512_H6; + sctx->state[7] = SHA512_H7; + sctx->count[0] = sctx->count[1] = 0; + + return 0; +} + +static int octeon_sha384_init(struct shash_desc *desc) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA384_H0; + sctx->state[1] = SHA384_H1; + sctx->state[2] = SHA384_H2; + sctx->state[3] = SHA384_H3; + sctx->state[4] = SHA384_H4; + sctx->state[5] = SHA384_H5; + sctx->state[6] = SHA384_H6; + sctx->state[7] = SHA384_H7; + sctx->count[0] = sctx->count[1] = 0; + + return 0; +} + +static void __octeon_sha512_update(struct sha512_state *sctx, const u8 *data, + unsigned int len) +{ + unsigned int part_len; + unsigned int index; + unsigned int i; + + /* Compute number of bytes mod 128. */ + index = sctx->count[0] % SHA512_BLOCK_SIZE; + + /* Update number of bytes. */ + if ((sctx->count[0] += len) < len) + sctx->count[1]++; + + part_len = SHA512_BLOCK_SIZE - index; + + /* Transform as many times as possible. */ + if (len >= part_len) { + memcpy(&sctx->buf[index], data, part_len); + octeon_sha512_transform(sctx->buf); + + for (i = part_len; i + SHA512_BLOCK_SIZE <= len; + i += SHA512_BLOCK_SIZE) + octeon_sha512_transform(&data[i]); + + index = 0; + } else { + i = 0; + } + + /* Buffer remaining input. */ + memcpy(&sctx->buf[index], &data[i], len - i); +} + +static int octeon_sha512_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + struct octeon_cop2_state state; + unsigned long flags; + + /* + * Small updates never reach the crypto engine, so the generic sha512 is + * faster because of the heavyweight octeon_crypto_enable() / + * octeon_crypto_disable(). + */ + if ((sctx->count[0] % SHA512_BLOCK_SIZE) + len < SHA512_BLOCK_SIZE) + return crypto_sha512_update(desc, data, len); + + flags = octeon_crypto_enable(&state); + octeon_sha512_store_hash(sctx); + + __octeon_sha512_update(sctx, data, len); + + octeon_sha512_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + return 0; +} + +static int octeon_sha512_final(struct shash_desc *desc, u8 *hash) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + static u8 padding[128] = { 0x80, }; + struct octeon_cop2_state state; + __be64 *dst = (__be64 *)hash; + unsigned int pad_len; + unsigned long flags; + unsigned int index; + __be64 bits[2]; + int i; + + /* Save number of bits. */ + bits[1] = cpu_to_be64(sctx->count[0] << 3); + bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61); + + /* Pad out to 112 mod 128. */ + index = sctx->count[0] & 0x7f; + pad_len = (index < 112) ? (112 - index) : ((128+112) - index); + + flags = octeon_crypto_enable(&state); + octeon_sha512_store_hash(sctx); + + __octeon_sha512_update(sctx, padding, pad_len); + + /* Append length (before padding). */ + __octeon_sha512_update(sctx, (const u8 *)bits, sizeof(bits)); + + octeon_sha512_read_hash(sctx); + octeon_crypto_disable(&state, flags); + + /* Store state in digest. */ + for (i = 0; i < 8; i++) + dst[i] = cpu_to_be64(sctx->state[i]); + + /* Zeroize sensitive information. */ + memset(sctx, 0, sizeof(struct sha512_state)); + + return 0; +} + +static int octeon_sha384_final(struct shash_desc *desc, u8 *hash) +{ + u8 D[64]; + + octeon_sha512_final(desc, D); + + memcpy(hash, D, 48); + memzero_explicit(D, 64); + + return 0; +} + +static struct shash_alg octeon_sha512_algs[2] = { { + .digestsize = SHA512_DIGEST_SIZE, + .init = octeon_sha512_init, + .update = octeon_sha512_update, + .final = octeon_sha512_final, + .descsize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha512", + .cra_driver_name= "octeon-sha512", + .cra_priority = OCTEON_CR_OPCODE_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA512_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .digestsize = SHA384_DIGEST_SIZE, + .init = octeon_sha384_init, + .update = octeon_sha512_update, + .final = octeon_sha384_final, + .descsize = sizeof(struct sha512_state), + .base = { + .cra_name = "sha384", + .cra_driver_name= "octeon-sha384", + .cra_priority = OCTEON_CR_OPCODE_PRIORITY, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA384_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init octeon_sha512_mod_init(void) +{ + if (!octeon_has_crypto()) + return -ENOTSUPP; + return crypto_register_shashes(octeon_sha512_algs, + ARRAY_SIZE(octeon_sha512_algs)); +} + +static void __exit octeon_sha512_mod_fini(void) +{ + crypto_unregister_shashes(octeon_sha512_algs, + ARRAY_SIZE(octeon_sha512_algs)); +} + +module_init(octeon_sha512_mod_init); +module_exit(octeon_sha512_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA-512 and SHA-384 Secure Hash Algorithms (OCTEON)"); +MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>"); diff --git a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h index 4794067cb5a7..5035f09c5427 100644 --- a/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h +++ b/arch/mips/include/asm/mach-bcm63xx/bcm63xx_regs.h @@ -1259,20 +1259,6 @@ #define M2M_DSTID_REG(x) ((x) * 0x40 + 0x18) /************************************************************************* - * _REG relative to RSET_RNG - *************************************************************************/ - -#define RNG_CTRL 0x00 -#define RNG_EN (1 << 0) - -#define RNG_STAT 0x04 -#define RNG_AVAIL_MASK (0xff000000) - -#define RNG_DATA 0x08 -#define RNG_THRES 0x0c -#define RNG_MASK 0x10 - -/************************************************************************* * _REG relative to RSET_SPI *************************************************************************/ diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h index 55ed6602204c..8408a30c47f3 100644 --- a/arch/mips/include/asm/thread_info.h +++ b/arch/mips/include/asm/thread_info.h @@ -23,7 +23,6 @@ */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long tp_value; /* thread pointer */ __u32 cpu; /* current CPU */ @@ -44,7 +43,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = _TIF_FIXADE, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c index e59fd7cfac9e..beabe19ff8e5 100644 --- a/arch/mips/kernel/asm-offsets.c +++ b/arch/mips/kernel/asm-offsets.c @@ -92,7 +92,6 @@ void output_thread_info_defines(void) { COMMENT("MIPS thread_info offsets."); OFFSET(TI_TASK, thread_info, task); - OFFSET(TI_EXEC_DOMAIN, thread_info, exec_domain); OFFSET(TI_FLAGS, thread_info, flags); OFFSET(TI_TP_VALUE, thread_info, tp_value); OFFSET(TI_CPU, thread_info, cpu); diff --git a/arch/mn10300/include/asm/thread_info.h b/arch/mn10300/include/asm/thread_info.h index c1c374f0ec12..4861a78c7160 100644 --- a/arch/mn10300/include/asm/thread_info.h +++ b/arch/mn10300/include/asm/thread_info.h @@ -40,7 +40,6 @@ typedef struct { struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ struct pt_regs *frame; /* current exception frame */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ @@ -74,7 +73,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/mn10300/kernel/asm-offsets.c b/arch/mn10300/kernel/asm-offsets.c index d780670cbaf3..e5a61c659b5a 100644 --- a/arch/mn10300/kernel/asm-offsets.c +++ b/arch/mn10300/kernel/asm-offsets.c @@ -22,7 +22,6 @@ void foo(void) BLANK(); OFFSET(TI_task, thread_info, task); - OFFSET(TI_exec_domain, thread_info, exec_domain); OFFSET(TI_frame, thread_info, frame); OFFSET(TI_flags, thread_info, flags); OFFSET(TI_cpu, thread_info, cpu); @@ -85,7 +84,6 @@ void foo(void) DEFINE(SIGCHLD_asm, SIGCHLD); BLANK(); - OFFSET(EXEC_DOMAIN_handler, exec_domain, handler); OFFSET(RT_SIGFRAME_sigcontext, rt_sigframe, uc.uc_mcontext); DEFINE(PAGE_SIZE_asm, PAGE_SIZE); diff --git a/arch/mn10300/kernel/signal.c b/arch/mn10300/kernel/signal.c index 8609845f12c5..dfd0301cf200 100644 --- a/arch/mn10300/kernel/signal.c +++ b/arch/mn10300/kernel/signal.c @@ -202,20 +202,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct sigframe __user *frame; - int rsig, sig = ksig->sig; + int sig = ksig->sig; frame = get_sigframe(ksig, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - rsig = sig; - if (sig < 32 && - current_thread_info()->exec_domain && - current_thread_info()->exec_domain->signal_invmap) - rsig = current_thread_info()->exec_domain->signal_invmap[sig]; - - if (__put_user(rsig, &frame->sig) < 0 || + if (__put_user(sig, &frame->sig) < 0 || __put_user(&frame->sc, &frame->psc) < 0) return -EFAULT; @@ -270,20 +264,14 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { struct rt_sigframe __user *frame; - int rsig, sig = ksig->sig; + int sig = ksig->sig; frame = get_sigframe(ksig, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - rsig = sig; - if (sig < 32 && - current_thread_info()->exec_domain && - current_thread_info()->exec_domain->signal_invmap) - rsig = current_thread_info()->exec_domain->signal_invmap[sig]; - - if (__put_user(rsig, &frame->sig) || + if (__put_user(sig, &frame->sig) || __put_user(&frame->info, &frame->pinfo) || __put_user(&frame->uc, &frame->puc) || copy_siginfo_to_user(&frame->info, &ksig->info)) diff --git a/arch/nios2/include/asm/thread_info.h b/arch/nios2/include/asm/thread_info.h index a16e55cbd8ad..d69c338bd19c 100644 --- a/arch/nios2/include/asm/thread_info.h +++ b/arch/nios2/include/asm/thread_info.h @@ -39,7 +39,6 @@ typedef struct { */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable,<0 => BUG */ @@ -58,7 +57,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/openrisc/include/asm/thread_info.h b/arch/openrisc/include/asm/thread_info.h index 875f0845a707..6e619a79a401 100644 --- a/arch/openrisc/include/asm/thread_info.h +++ b/arch/openrisc/include/asm/thread_info.h @@ -48,7 +48,6 @@ typedef unsigned long mm_segment_t; struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ __s32 preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -73,7 +72,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = 1, \ diff --git a/arch/openrisc/kernel/signal.c b/arch/openrisc/kernel/signal.c index 4112175bf803..c82be69b43c6 100644 --- a/arch/openrisc/kernel/signal.c +++ b/arch/openrisc/kernel/signal.c @@ -193,8 +193,6 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, if (err) return -EFAULT; - /* TODO what is the current->exec_domain stuff and invmap ? */ - /* Set up registers for signal handler */ regs->pc = (unsigned long)ksig->ka.sa.sa_handler; /* what we enter NOW */ regs->gpr[9] = (unsigned long)return_ip; /* what we enter LATER */ diff --git a/arch/parisc/include/asm/thread_info.h b/arch/parisc/include/asm/thread_info.h index fb13e3865563..e96e693fd58c 100644 --- a/arch/parisc/include/asm/thread_info.h +++ b/arch/parisc/include/asm/thread_info.h @@ -9,7 +9,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain;/* execution domain */ unsigned long flags; /* thread_info flags (see TIF_*) */ mm_segment_t addr_limit; /* user-level address space limit */ __u32 cpu; /* current CPU */ @@ -19,7 +18,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c index dcd55103a4bb..59001cea13f9 100644 --- a/arch/parisc/kernel/asm-offsets.c +++ b/arch/parisc/kernel/asm-offsets.c @@ -242,7 +242,6 @@ int main(void) DEFINE(PT_SZ_ALGN, align_frame(sizeof(struct pt_regs), FRAME_ALIGN)); BLANK(); DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); DEFINE(TI_SEGMENT, offsetof(struct thread_info, addr_limit)); diff --git a/arch/powerpc/crypto/Makefile b/arch/powerpc/crypto/Makefile index 2926fb9c570a..9c221b69c181 100644 --- a/arch/powerpc/crypto/Makefile +++ b/arch/powerpc/crypto/Makefile @@ -4,6 +4,14 @@ # Arch-specific CryptoAPI modules. # +obj-$(CONFIG_CRYPTO_AES_PPC_SPE) += aes-ppc-spe.o +obj-$(CONFIG_CRYPTO_MD5_PPC) += md5-ppc.o obj-$(CONFIG_CRYPTO_SHA1_PPC) += sha1-powerpc.o +obj-$(CONFIG_CRYPTO_SHA1_PPC_SPE) += sha1-ppc-spe.o +obj-$(CONFIG_CRYPTO_SHA256_PPC_SPE) += sha256-ppc-spe.o +aes-ppc-spe-y := aes-spe-core.o aes-spe-keys.o aes-tab-4k.o aes-spe-modes.o aes-spe-glue.o +md5-ppc-y := md5-asm.o md5-glue.o sha1-powerpc-y := sha1-powerpc-asm.o sha1.o +sha1-ppc-spe-y := sha1-spe-asm.o sha1-spe-glue.o +sha256-ppc-spe-y := sha256-spe-asm.o sha256-spe-glue.o diff --git a/arch/powerpc/crypto/aes-spe-core.S b/arch/powerpc/crypto/aes-spe-core.S new file mode 100644 index 000000000000..5dc6bce90a77 --- /dev/null +++ b/arch/powerpc/crypto/aes-spe-core.S @@ -0,0 +1,351 @@ +/* + * Fast AES implementation for SPE instruction set (PPC) + * + * This code makes use of the SPE SIMD instruction set as defined in + * http://cache.freescale.com/files/32bit/doc/ref_manual/SPEPIM.pdf + * Implementation is based on optimization guide notes from + * http://cache.freescale.com/files/32bit/doc/app_note/AN2665.pdf + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> +#include "aes-spe-regs.h" + +#define EAD(in, bpos) \ + rlwimi rT0,in,28-((bpos+3)%4)*8,20,27; + +#define DAD(in, bpos) \ + rlwimi rT1,in,24-((bpos+3)%4)*8,24,31; + +#define LWH(out, off) \ + evlwwsplat out,off(rT0); /* load word high */ + +#define LWL(out, off) \ + lwz out,off(rT0); /* load word low */ + +#define LBZ(out, tab, off) \ + lbz out,off(tab); /* load byte */ + +#define LAH(out, in, bpos, off) \ + EAD(in, bpos) /* calc addr + load word high */ \ + LWH(out, off) + +#define LAL(out, in, bpos, off) \ + EAD(in, bpos) /* calc addr + load word low */ \ + LWL(out, off) + +#define LAE(out, in, bpos) \ + EAD(in, bpos) /* calc addr + load enc byte */ \ + LBZ(out, rT0, 8) + +#define LBE(out) \ + LBZ(out, rT0, 8) /* load enc byte */ + +#define LAD(out, in, bpos) \ + DAD(in, bpos) /* calc addr + load dec byte */ \ + LBZ(out, rT1, 0) + +#define LBD(out) \ + LBZ(out, rT1, 0) + +/* + * ppc_encrypt_block: The central encryption function for a single 16 bytes + * block. It does no stack handling or register saving to support fast calls + * via bl/blr. It expects that caller has pre-xored input data with first + * 4 words of encryption key into rD0-rD3. Pointer/counter registers must + * have also been set up before (rT0, rKP, CTR). Output is stored in rD0-rD3 + * and rW0-rW3 and caller must execute a final xor on the ouput registers. + * All working registers rD0-rD3 & rW0-rW7 are overwritten during processing. + * + */ +_GLOBAL(ppc_encrypt_block) + LAH(rW4, rD1, 2, 4) + LAH(rW6, rD0, 3, 0) + LAH(rW3, rD0, 1, 8) +ppc_encrypt_block_loop: + LAH(rW0, rD3, 0, 12) + LAL(rW0, rD0, 0, 12) + LAH(rW1, rD1, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAL(rW3, rD1, 1, 8) + LAL(rW4, rD2, 2, 4) + LAL(rW6, rD1, 3, 0) + LAH(rW5, rD3, 2, 4) + LAL(rW5, rD0, 2, 4) + LAH(rW7, rD2, 3, 0) + evldw rD1,16(rKP) + EAD(rD3, 3) + evxor rW2,rW2,rW4 + LWL(rW7, 0) + evxor rW2,rW2,rW6 + EAD(rD2, 0) + evxor rD1,rD1,rW2 + LWL(rW1, 12) + evxor rD1,rD1,rW0 + evldw rD3,24(rKP) + evmergehi rD0,rD0,rD1 + EAD(rD1, 2) + evxor rW3,rW3,rW5 + LWH(rW4, 4) + evxor rW3,rW3,rW7 + EAD(rD0, 3) + evxor rD3,rD3,rW3 + LWH(rW6, 0) + evxor rD3,rD3,rW1 + EAD(rD0, 1) + evmergehi rD2,rD2,rD3 + LWH(rW3, 8) + LAH(rW0, rD3, 0, 12) + LAL(rW0, rD0, 0, 12) + LAH(rW1, rD1, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAL(rW3, rD1, 1, 8) + LAL(rW4, rD2, 2, 4) + LAL(rW6, rD1, 3, 0) + LAH(rW5, rD3, 2, 4) + LAL(rW5, rD0, 2, 4) + LAH(rW7, rD2, 3, 0) + evldw rD1,32(rKP) + EAD(rD3, 3) + evxor rW2,rW2,rW4 + LWL(rW7, 0) + evxor rW2,rW2,rW6 + EAD(rD2, 0) + evxor rD1,rD1,rW2 + LWL(rW1, 12) + evxor rD1,rD1,rW0 + evldw rD3,40(rKP) + evmergehi rD0,rD0,rD1 + EAD(rD1, 2) + evxor rW3,rW3,rW5 + LWH(rW4, 4) + evxor rW3,rW3,rW7 + EAD(rD0, 3) + evxor rD3,rD3,rW3 + LWH(rW6, 0) + evxor rD3,rD3,rW1 + EAD(rD0, 1) + evmergehi rD2,rD2,rD3 + LWH(rW3, 8) + addi rKP,rKP,32 + bdnz ppc_encrypt_block_loop + LAH(rW0, rD3, 0, 12) + LAL(rW0, rD0, 0, 12) + LAH(rW1, rD1, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAL(rW3, rD1, 1, 8) + LAL(rW4, rD2, 2, 4) + LAH(rW5, rD3, 2, 4) + LAL(rW6, rD1, 3, 0) + LAL(rW5, rD0, 2, 4) + LAH(rW7, rD2, 3, 0) + evldw rD1,16(rKP) + EAD(rD3, 3) + evxor rW2,rW2,rW4 + LWL(rW7, 0) + evxor rW2,rW2,rW6 + EAD(rD2, 0) + evxor rD1,rD1,rW2 + LWL(rW1, 12) + evxor rD1,rD1,rW0 + evldw rD3,24(rKP) + evmergehi rD0,rD0,rD1 + EAD(rD1, 0) + evxor rW3,rW3,rW5 + LBE(rW2) + evxor rW3,rW3,rW7 + EAD(rD0, 1) + evxor rD3,rD3,rW3 + LBE(rW6) + evxor rD3,rD3,rW1 + EAD(rD0, 0) + evmergehi rD2,rD2,rD3 + LBE(rW1) + LAE(rW0, rD3, 0) + LAE(rW1, rD0, 0) + LAE(rW4, rD2, 1) + LAE(rW5, rD3, 1) + LAE(rW3, rD2, 0) + LAE(rW7, rD1, 1) + rlwimi rW0,rW4,8,16,23 + rlwimi rW1,rW5,8,16,23 + LAE(rW4, rD1, 2) + LAE(rW5, rD2, 2) + rlwimi rW2,rW6,8,16,23 + rlwimi rW3,rW7,8,16,23 + LAE(rW6, rD3, 2) + LAE(rW7, rD0, 2) + rlwimi rW0,rW4,16,8,15 + rlwimi rW1,rW5,16,8,15 + LAE(rW4, rD0, 3) + LAE(rW5, rD1, 3) + rlwimi rW2,rW6,16,8,15 + lwz rD0,32(rKP) + rlwimi rW3,rW7,16,8,15 + lwz rD1,36(rKP) + LAE(rW6, rD2, 3) + LAE(rW7, rD3, 3) + rlwimi rW0,rW4,24,0,7 + lwz rD2,40(rKP) + rlwimi rW1,rW5,24,0,7 + lwz rD3,44(rKP) + rlwimi rW2,rW6,24,0,7 + rlwimi rW3,rW7,24,0,7 + blr + +/* + * ppc_decrypt_block: The central decryption function for a single 16 bytes + * block. It does no stack handling or register saving to support fast calls + * via bl/blr. It expects that caller has pre-xored input data with first + * 4 words of encryption key into rD0-rD3. Pointer/counter registers must + * have also been set up before (rT0, rKP, CTR). Output is stored in rD0-rD3 + * and rW0-rW3 and caller must execute a final xor on the ouput registers. + * All working registers rD0-rD3 & rW0-rW7 are overwritten during processing. + * + */ +_GLOBAL(ppc_decrypt_block) + LAH(rW0, rD1, 0, 12) + LAH(rW6, rD0, 3, 0) + LAH(rW3, rD0, 1, 8) +ppc_decrypt_block_loop: + LAH(rW1, rD3, 0, 12) + LAL(rW0, rD2, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAH(rW4, rD3, 2, 4) + LAL(rW4, rD0, 2, 4) + LAL(rW6, rD1, 3, 0) + LAH(rW5, rD1, 2, 4) + LAH(rW7, rD2, 3, 0) + LAL(rW7, rD3, 3, 0) + LAL(rW3, rD1, 1, 8) + evldw rD1,16(rKP) + EAD(rD0, 0) + evxor rW4,rW4,rW6 + LWL(rW1, 12) + evxor rW0,rW0,rW4 + EAD(rD2, 2) + evxor rW0,rW0,rW2 + LWL(rW5, 4) + evxor rD1,rD1,rW0 + evldw rD3,24(rKP) + evmergehi rD0,rD0,rD1 + EAD(rD1, 0) + evxor rW3,rW3,rW7 + LWH(rW0, 12) + evxor rW3,rW3,rW1 + EAD(rD0, 3) + evxor rD3,rD3,rW3 + LWH(rW6, 0) + evxor rD3,rD3,rW5 + EAD(rD0, 1) + evmergehi rD2,rD2,rD3 + LWH(rW3, 8) + LAH(rW1, rD3, 0, 12) + LAL(rW0, rD2, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAH(rW4, rD3, 2, 4) + LAL(rW4, rD0, 2, 4) + LAL(rW6, rD1, 3, 0) + LAH(rW5, rD1, 2, 4) + LAH(rW7, rD2, 3, 0) + LAL(rW7, rD3, 3, 0) + LAL(rW3, rD1, 1, 8) + evldw rD1,32(rKP) + EAD(rD0, 0) + evxor rW4,rW4,rW6 + LWL(rW1, 12) + evxor rW0,rW0,rW4 + EAD(rD2, 2) + evxor rW0,rW0,rW2 + LWL(rW5, 4) + evxor rD1,rD1,rW0 + evldw rD3,40(rKP) + evmergehi rD0,rD0,rD1 + EAD(rD1, 0) + evxor rW3,rW3,rW7 + LWH(rW0, 12) + evxor rW3,rW3,rW1 + EAD(rD0, 3) + evxor rD3,rD3,rW3 + LWH(rW6, 0) + evxor rD3,rD3,rW5 + EAD(rD0, 1) + evmergehi rD2,rD2,rD3 + LWH(rW3, 8) + addi rKP,rKP,32 + bdnz ppc_decrypt_block_loop + LAH(rW1, rD3, 0, 12) + LAL(rW0, rD2, 0, 12) + LAH(rW2, rD2, 1, 8) + LAL(rW2, rD3, 1, 8) + LAH(rW4, rD3, 2, 4) + LAL(rW4, rD0, 2, 4) + LAL(rW6, rD1, 3, 0) + LAH(rW5, rD1, 2, 4) + LAH(rW7, rD2, 3, 0) + LAL(rW7, rD3, 3, 0) + LAL(rW3, rD1, 1, 8) + evldw rD1,16(rKP) + EAD(rD0, 0) + evxor rW4,rW4,rW6 + LWL(rW1, 12) + evxor rW0,rW0,rW4 + EAD(rD2, 2) + evxor rW0,rW0,rW2 + LWL(rW5, 4) + evxor rD1,rD1,rW0 + evldw rD3,24(rKP) + evmergehi rD0,rD0,rD1 + DAD(rD1, 0) + evxor rW3,rW3,rW7 + LBD(rW0) + evxor rW3,rW3,rW1 + DAD(rD0, 1) + evxor rD3,rD3,rW3 + LBD(rW6) + evxor rD3,rD3,rW5 + DAD(rD0, 0) + evmergehi rD2,rD2,rD3 + LBD(rW3) + LAD(rW2, rD3, 0) + LAD(rW1, rD2, 0) + LAD(rW4, rD2, 1) + LAD(rW5, rD3, 1) + LAD(rW7, rD1, 1) + rlwimi rW0,rW4,8,16,23 + rlwimi rW1,rW5,8,16,23 + LAD(rW4, rD3, 2) + LAD(rW5, rD0, 2) + rlwimi rW2,rW6,8,16,23 + rlwimi rW3,rW7,8,16,23 + LAD(rW6, rD1, 2) + LAD(rW7, rD2, 2) + rlwimi rW0,rW4,16,8,15 + rlwimi rW1,rW5,16,8,15 + LAD(rW4, rD0, 3) + LAD(rW5, rD1, 3) + rlwimi rW2,rW6,16,8,15 + lwz rD0,32(rKP) + rlwimi rW3,rW7,16,8,15 + lwz rD1,36(rKP) + LAD(rW6, rD2, 3) + LAD(rW7, rD3, 3) + rlwimi rW0,rW4,24,0,7 + lwz rD2,40(rKP) + rlwimi rW1,rW5,24,0,7 + lwz rD3,44(rKP) + rlwimi rW2,rW6,24,0,7 + rlwimi rW3,rW7,24,0,7 + blr diff --git a/arch/powerpc/crypto/aes-spe-glue.c b/arch/powerpc/crypto/aes-spe-glue.c new file mode 100644 index 000000000000..bd5e63f72ad4 --- /dev/null +++ b/arch/powerpc/crypto/aes-spe-glue.c @@ -0,0 +1,512 @@ +/* + * Glue code for AES implementation for SPE instructions (PPC) + * + * Based on generic implementation. The assembler module takes care + * about the SPE registers so it can run from interrupt context. + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <crypto/aes.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/crypto.h> +#include <asm/byteorder.h> +#include <asm/switch_to.h> +#include <crypto/algapi.h> + +/* + * MAX_BYTES defines the number of bytes that are allowed to be processed + * between preempt_disable() and preempt_enable(). e500 cores can issue two + * instructions per clock cycle using one 32/64 bit unit (SU1) and one 32 + * bit unit (SU2). One of these can be a memory access that is executed via + * a single load and store unit (LSU). XTS-AES-256 takes ~780 operations per + * 16 byte block block or 25 cycles per byte. Thus 768 bytes of input data + * will need an estimated maximum of 20,000 cycles. Headroom for cache misses + * included. Even with the low end model clocked at 667 MHz this equals to a + * critical time window of less than 30us. The value has been choosen to + * process a 512 byte disk block in one or a large 1400 bytes IPsec network + * packet in two runs. + * + */ +#define MAX_BYTES 768 + +struct ppc_aes_ctx { + u32 key_enc[AES_MAX_KEYLENGTH_U32]; + u32 key_dec[AES_MAX_KEYLENGTH_U32]; + u32 rounds; +}; + +struct ppc_xts_ctx { + u32 key_enc[AES_MAX_KEYLENGTH_U32]; + u32 key_dec[AES_MAX_KEYLENGTH_U32]; + u32 key_twk[AES_MAX_KEYLENGTH_U32]; + u32 rounds; +}; + +extern void ppc_encrypt_aes(u8 *out, const u8 *in, u32 *key_enc, u32 rounds); +extern void ppc_decrypt_aes(u8 *out, const u8 *in, u32 *key_dec, u32 rounds); +extern void ppc_encrypt_ecb(u8 *out, const u8 *in, u32 *key_enc, u32 rounds, + u32 bytes); +extern void ppc_decrypt_ecb(u8 *out, const u8 *in, u32 *key_dec, u32 rounds, + u32 bytes); +extern void ppc_encrypt_cbc(u8 *out, const u8 *in, u32 *key_enc, u32 rounds, + u32 bytes, u8 *iv); +extern void ppc_decrypt_cbc(u8 *out, const u8 *in, u32 *key_dec, u32 rounds, + u32 bytes, u8 *iv); +extern void ppc_crypt_ctr (u8 *out, const u8 *in, u32 *key_enc, u32 rounds, + u32 bytes, u8 *iv); +extern void ppc_encrypt_xts(u8 *out, const u8 *in, u32 *key_enc, u32 rounds, + u32 bytes, u8 *iv, u32 *key_twk); +extern void ppc_decrypt_xts(u8 *out, const u8 *in, u32 *key_dec, u32 rounds, + u32 bytes, u8 *iv, u32 *key_twk); + +extern void ppc_expand_key_128(u32 *key_enc, const u8 *key); +extern void ppc_expand_key_192(u32 *key_enc, const u8 *key); +extern void ppc_expand_key_256(u32 *key_enc, const u8 *key); + +extern void ppc_generate_decrypt_key(u32 *key_dec,u32 *key_enc, + unsigned int key_len); + +static void spe_begin(void) +{ + /* disable preemption and save users SPE registers if required */ + preempt_disable(); + enable_kernel_spe(); +} + +static void spe_end(void) +{ + /* reenable preemption */ + preempt_enable(); +} + +static int ppc_aes_setkey(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct ppc_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + if (key_len != AES_KEYSIZE_128 && + key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) { + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; + } + + switch (key_len) { + case AES_KEYSIZE_128: + ctx->rounds = 4; + ppc_expand_key_128(ctx->key_enc, in_key); + break; + case AES_KEYSIZE_192: + ctx->rounds = 5; + ppc_expand_key_192(ctx->key_enc, in_key); + break; + case AES_KEYSIZE_256: + ctx->rounds = 6; + ppc_expand_key_256(ctx->key_enc, in_key); + break; + } + + ppc_generate_decrypt_key(ctx->key_dec, ctx->key_enc, key_len); + + return 0; +} + +static int ppc_xts_setkey(struct crypto_tfm *tfm, const u8 *in_key, + unsigned int key_len) +{ + struct ppc_xts_ctx *ctx = crypto_tfm_ctx(tfm); + + key_len >>= 1; + + if (key_len != AES_KEYSIZE_128 && + key_len != AES_KEYSIZE_192 && + key_len != AES_KEYSIZE_256) { + tfm->crt_flags |= CRYPTO_TFM_RES_BAD_KEY_LEN; + return -EINVAL; + } + + switch (key_len) { + case AES_KEYSIZE_128: + ctx->rounds = 4; + ppc_expand_key_128(ctx->key_enc, in_key); + ppc_expand_key_128(ctx->key_twk, in_key + AES_KEYSIZE_128); + break; + case AES_KEYSIZE_192: + ctx->rounds = 5; + ppc_expand_key_192(ctx->key_enc, in_key); + ppc_expand_key_192(ctx->key_twk, in_key + AES_KEYSIZE_192); + break; + case AES_KEYSIZE_256: + ctx->rounds = 6; + ppc_expand_key_256(ctx->key_enc, in_key); + ppc_expand_key_256(ctx->key_twk, in_key + AES_KEYSIZE_256); + break; + } + + ppc_generate_decrypt_key(ctx->key_dec, ctx->key_enc, key_len); + + return 0; +} + +static void ppc_aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + struct ppc_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + spe_begin(); + ppc_encrypt_aes(out, in, ctx->key_enc, ctx->rounds); + spe_end(); +} + +static void ppc_aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in) +{ + struct ppc_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + spe_begin(); + ppc_decrypt_aes(out, in, ctx->key_dec, ctx->rounds); + spe_end(); +} + +static int ppc_ecb_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_encrypt_ecb(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_enc, ctx->rounds, nbytes); + spe_end(); + + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_ecb_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_decrypt_ecb(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_dec, ctx->rounds, nbytes); + spe_end(); + + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_cbc_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_encrypt_cbc(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_enc, ctx->rounds, nbytes, walk.iv); + spe_end(); + + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_cbc_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_decrypt_cbc(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_dec, ctx->rounds, nbytes, walk.iv); + spe_end(); + + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_ctr_crypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_aes_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int pbytes, ubytes; + int err; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + + while ((pbytes = walk.nbytes)) { + pbytes = pbytes > MAX_BYTES ? MAX_BYTES : pbytes; + pbytes = pbytes == nbytes ? + nbytes : pbytes & ~(AES_BLOCK_SIZE - 1); + ubytes = walk.nbytes - pbytes; + + spe_begin(); + ppc_crypt_ctr(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_enc, ctx->rounds, pbytes , walk.iv); + spe_end(); + + nbytes -= pbytes; + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_xts_encrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + u32 *twk; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + twk = ctx->key_twk; + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_encrypt_xts(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_enc, ctx->rounds, nbytes, walk.iv, twk); + spe_end(); + + twk = NULL; + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +static int ppc_xts_decrypt(struct blkcipher_desc *desc, struct scatterlist *dst, + struct scatterlist *src, unsigned int nbytes) +{ + struct ppc_xts_ctx *ctx = crypto_blkcipher_ctx(desc->tfm); + struct blkcipher_walk walk; + unsigned int ubytes; + int err; + u32 *twk; + + desc->flags &= ~CRYPTO_TFM_REQ_MAY_SLEEP; + blkcipher_walk_init(&walk, dst, src, nbytes); + err = blkcipher_walk_virt(desc, &walk); + twk = ctx->key_twk; + + while ((nbytes = walk.nbytes)) { + ubytes = nbytes > MAX_BYTES ? + nbytes - MAX_BYTES : nbytes & (AES_BLOCK_SIZE - 1); + nbytes -= ubytes; + + spe_begin(); + ppc_decrypt_xts(walk.dst.virt.addr, walk.src.virt.addr, + ctx->key_dec, ctx->rounds, nbytes, walk.iv, twk); + spe_end(); + + twk = NULL; + err = blkcipher_walk_done(desc, &walk, ubytes); + } + + return err; +} + +/* + * Algorithm definitions. Disabling alignment (cra_alignmask=0) was chosen + * because the e500 platform can handle unaligned reads/writes very efficently. + * This improves IPsec thoughput by another few percent. Additionally we assume + * that AES context is always aligned to at least 8 bytes because it is created + * with kmalloc() in the crypto infrastructure + * + */ +static struct crypto_alg aes_algs[] = { { + .cra_name = "aes", + .cra_driver_name = "aes-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ppc_aes_ctx), + .cra_alignmask = 0, + .cra_module = THIS_MODULE, + .cra_u = { + .cipher = { + .cia_min_keysize = AES_MIN_KEY_SIZE, + .cia_max_keysize = AES_MAX_KEY_SIZE, + .cia_setkey = ppc_aes_setkey, + .cia_encrypt = ppc_aes_encrypt, + .cia_decrypt = ppc_aes_decrypt + } + } +}, { + .cra_name = "ecb(aes)", + .cra_driver_name = "ecb-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ppc_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ppc_aes_setkey, + .encrypt = ppc_ecb_encrypt, + .decrypt = ppc_ecb_decrypt, + } + } +}, { + .cra_name = "cbc(aes)", + .cra_driver_name = "cbc-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ppc_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ppc_aes_setkey, + .encrypt = ppc_cbc_encrypt, + .decrypt = ppc_cbc_decrypt, + } + } +}, { + .cra_name = "ctr(aes)", + .cra_driver_name = "ctr-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct ppc_aes_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .ivsize = AES_BLOCK_SIZE, + .setkey = ppc_aes_setkey, + .encrypt = ppc_ctr_crypt, + .decrypt = ppc_ctr_crypt, + } + } +}, { + .cra_name = "xts(aes)", + .cra_driver_name = "xts-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct ppc_xts_ctx), + .cra_alignmask = 0, + .cra_type = &crypto_blkcipher_type, + .cra_module = THIS_MODULE, + .cra_u = { + .blkcipher = { + .min_keysize = AES_MIN_KEY_SIZE * 2, + .max_keysize = AES_MAX_KEY_SIZE * 2, + .ivsize = AES_BLOCK_SIZE, + .setkey = ppc_xts_setkey, + .encrypt = ppc_xts_encrypt, + .decrypt = ppc_xts_decrypt, + } + } +} }; + +static int __init ppc_aes_mod_init(void) +{ + return crypto_register_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +static void __exit ppc_aes_mod_fini(void) +{ + crypto_unregister_algs(aes_algs, ARRAY_SIZE(aes_algs)); +} + +module_init(ppc_aes_mod_init); +module_exit(ppc_aes_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("AES-ECB/CBC/CTR/XTS, SPE optimized"); + +MODULE_ALIAS_CRYPTO("aes"); +MODULE_ALIAS_CRYPTO("ecb(aes)"); +MODULE_ALIAS_CRYPTO("cbc(aes)"); +MODULE_ALIAS_CRYPTO("ctr(aes)"); +MODULE_ALIAS_CRYPTO("xts(aes)"); +MODULE_ALIAS_CRYPTO("aes-ppc-spe"); diff --git a/arch/powerpc/crypto/aes-spe-keys.S b/arch/powerpc/crypto/aes-spe-keys.S new file mode 100644 index 000000000000..be8090f3d700 --- /dev/null +++ b/arch/powerpc/crypto/aes-spe-keys.S @@ -0,0 +1,283 @@ +/* + * Key handling functions for PPC AES implementation + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> + +#ifdef __BIG_ENDIAN__ +#define LOAD_KEY(d, s, off) \ + lwz d,off(s); +#else +#define LOAD_KEY(d, s, off) \ + li r0,off; \ + lwbrx d,s,r0; +#endif + +#define INITIALIZE_KEY \ + stwu r1,-32(r1); /* create stack frame */ \ + stw r14,8(r1); /* save registers */ \ + stw r15,12(r1); \ + stw r16,16(r1); + +#define FINALIZE_KEY \ + lwz r14,8(r1); /* restore registers */ \ + lwz r15,12(r1); \ + lwz r16,16(r1); \ + xor r5,r5,r5; /* clear sensitive data */ \ + xor r6,r6,r6; \ + xor r7,r7,r7; \ + xor r8,r8,r8; \ + xor r9,r9,r9; \ + xor r10,r10,r10; \ + xor r11,r11,r11; \ + xor r12,r12,r12; \ + addi r1,r1,32; /* cleanup stack */ + +#define LS_BOX(r, t1, t2) \ + lis t2,PPC_AES_4K_ENCTAB@h; \ + ori t2,t2,PPC_AES_4K_ENCTAB@l; \ + rlwimi t2,r,4,20,27; \ + lbz t1,8(t2); \ + rlwimi r,t1,0,24,31; \ + rlwimi t2,r,28,20,27; \ + lbz t1,8(t2); \ + rlwimi r,t1,8,16,23; \ + rlwimi t2,r,20,20,27; \ + lbz t1,8(t2); \ + rlwimi r,t1,16,8,15; \ + rlwimi t2,r,12,20,27; \ + lbz t1,8(t2); \ + rlwimi r,t1,24,0,7; + +#define GF8_MUL(out, in, t1, t2) \ + lis t1,0x8080; /* multiplication in GF8 */ \ + ori t1,t1,0x8080; \ + and t1,t1,in; \ + srwi t1,t1,7; \ + mulli t1,t1,0x1b; \ + lis t2,0x7f7f; \ + ori t2,t2,0x7f7f; \ + and t2,t2,in; \ + slwi t2,t2,1; \ + xor out,t1,t2; + +/* + * ppc_expand_key_128(u32 *key_enc, const u8 *key) + * + * Expand 128 bit key into 176 bytes encryption key. It consists of + * key itself plus 10 rounds with 16 bytes each + * + */ +_GLOBAL(ppc_expand_key_128) + INITIALIZE_KEY + LOAD_KEY(r5,r4,0) + LOAD_KEY(r6,r4,4) + LOAD_KEY(r7,r4,8) + LOAD_KEY(r8,r4,12) + stw r5,0(r3) /* key[0..3] = input data */ + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + li r16,10 /* 10 expansion rounds */ + lis r0,0x0100 /* RCO(1) */ +ppc_expand_128_loop: + addi r3,r3,16 + mr r14,r8 /* apply LS_BOX to 4th temp */ + rotlwi r14,r14,8 + LS_BOX(r14, r15, r4) + xor r14,r14,r0 + xor r5,r5,r14 /* xor next 4 keys */ + xor r6,r6,r5 + xor r7,r7,r6 + xor r8,r8,r7 + stw r5,0(r3) /* store next 4 keys */ + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + GF8_MUL(r0, r0, r4, r14) /* multiply RCO by 2 in GF */ + subi r16,r16,1 + cmpwi r16,0 + bt eq,ppc_expand_128_end + b ppc_expand_128_loop +ppc_expand_128_end: + FINALIZE_KEY + blr + +/* + * ppc_expand_key_192(u32 *key_enc, const u8 *key) + * + * Expand 192 bit key into 208 bytes encryption key. It consists of key + * itself plus 12 rounds with 16 bytes each + * + */ +_GLOBAL(ppc_expand_key_192) + INITIALIZE_KEY + LOAD_KEY(r5,r4,0) + LOAD_KEY(r6,r4,4) + LOAD_KEY(r7,r4,8) + LOAD_KEY(r8,r4,12) + LOAD_KEY(r9,r4,16) + LOAD_KEY(r10,r4,20) + stw r5,0(r3) + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + stw r9,16(r3) + stw r10,20(r3) + li r16,8 /* 8 expansion rounds */ + lis r0,0x0100 /* RCO(1) */ +ppc_expand_192_loop: + addi r3,r3,24 + mr r14,r10 /* apply LS_BOX to 6th temp */ + rotlwi r14,r14,8 + LS_BOX(r14, r15, r4) + xor r14,r14,r0 + xor r5,r5,r14 /* xor next 6 keys */ + xor r6,r6,r5 + xor r7,r7,r6 + xor r8,r8,r7 + xor r9,r9,r8 + xor r10,r10,r9 + stw r5,0(r3) + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + subi r16,r16,1 + cmpwi r16,0 /* last round early kick out */ + bt eq,ppc_expand_192_end + stw r9,16(r3) + stw r10,20(r3) + GF8_MUL(r0, r0, r4, r14) /* multiply RCO GF8 */ + b ppc_expand_192_loop +ppc_expand_192_end: + FINALIZE_KEY + blr + +/* + * ppc_expand_key_256(u32 *key_enc, const u8 *key) + * + * Expand 256 bit key into 240 bytes encryption key. It consists of key + * itself plus 14 rounds with 16 bytes each + * + */ +_GLOBAL(ppc_expand_key_256) + INITIALIZE_KEY + LOAD_KEY(r5,r4,0) + LOAD_KEY(r6,r4,4) + LOAD_KEY(r7,r4,8) + LOAD_KEY(r8,r4,12) + LOAD_KEY(r9,r4,16) + LOAD_KEY(r10,r4,20) + LOAD_KEY(r11,r4,24) + LOAD_KEY(r12,r4,28) + stw r5,0(r3) + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + stw r9,16(r3) + stw r10,20(r3) + stw r11,24(r3) + stw r12,28(r3) + li r16,7 /* 7 expansion rounds */ + lis r0,0x0100 /* RCO(1) */ +ppc_expand_256_loop: + addi r3,r3,32 + mr r14,r12 /* apply LS_BOX to 8th temp */ + rotlwi r14,r14,8 + LS_BOX(r14, r15, r4) + xor r14,r14,r0 + xor r5,r5,r14 /* xor 4 keys */ + xor r6,r6,r5 + xor r7,r7,r6 + xor r8,r8,r7 + mr r14,r8 + LS_BOX(r14, r15, r4) /* apply LS_BOX to 4th temp */ + xor r9,r9,r14 /* xor 4 keys */ + xor r10,r10,r9 + xor r11,r11,r10 + xor r12,r12,r11 + stw r5,0(r3) + stw r6,4(r3) + stw r7,8(r3) + stw r8,12(r3) + subi r16,r16,1 + cmpwi r16,0 /* last round early kick out */ + bt eq,ppc_expand_256_end + stw r9,16(r3) + stw r10,20(r3) + stw r11,24(r3) + stw r12,28(r3) + GF8_MUL(r0, r0, r4, r14) + b ppc_expand_256_loop +ppc_expand_256_end: + FINALIZE_KEY + blr + +/* + * ppc_generate_decrypt_key: derive decryption key from encryption key + * number of bytes to handle are calculated from length of key (16/24/32) + * + */ +_GLOBAL(ppc_generate_decrypt_key) + addi r6,r5,24 + slwi r6,r6,2 + lwzx r7,r4,r6 /* first/last 4 words are same */ + stw r7,0(r3) + lwz r7,0(r4) + stwx r7,r3,r6 + addi r6,r6,4 + lwzx r7,r4,r6 + stw r7,4(r3) + lwz r7,4(r4) + stwx r7,r3,r6 + addi r6,r6,4 + lwzx r7,r4,r6 + stw r7,8(r3) + lwz r7,8(r4) + stwx r7,r3,r6 + addi r6,r6,4 + lwzx r7,r4,r6 + stw r7,12(r3) + lwz r7,12(r4) + stwx r7,r3,r6 + addi r3,r3,16 + add r4,r4,r6 + subi r4,r4,28 + addi r5,r5,20 + srwi r5,r5,2 +ppc_generate_decrypt_block: + li r6,4 + mtctr r6 +ppc_generate_decrypt_word: + lwz r6,0(r4) + GF8_MUL(r7, r6, r0, r7) + GF8_MUL(r8, r7, r0, r8) + GF8_MUL(r9, r8, r0, r9) + xor r10,r9,r6 + xor r11,r7,r8 + xor r11,r11,r9 + xor r12,r7,r10 + rotrwi r12,r12,24 + xor r11,r11,r12 + xor r12,r8,r10 + rotrwi r12,r12,16 + xor r11,r11,r12 + rotrwi r12,r10,8 + xor r11,r11,r12 + stw r11,0(r3) + addi r3,r3,4 + addi r4,r4,4 + bdnz ppc_generate_decrypt_word + subi r4,r4,32 + subi r5,r5,1 + cmpwi r5,0 + bt gt,ppc_generate_decrypt_block + blr diff --git a/arch/powerpc/crypto/aes-spe-modes.S b/arch/powerpc/crypto/aes-spe-modes.S new file mode 100644 index 000000000000..ad48032ca8e0 --- /dev/null +++ b/arch/powerpc/crypto/aes-spe-modes.S @@ -0,0 +1,630 @@ +/* + * AES modes (ECB/CBC/CTR/XTS) for PPC AES implementation + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> +#include "aes-spe-regs.h" + +#ifdef __BIG_ENDIAN__ /* Macros for big endian builds */ + +#define LOAD_DATA(reg, off) \ + lwz reg,off(rSP); /* load with offset */ +#define SAVE_DATA(reg, off) \ + stw reg,off(rDP); /* save with offset */ +#define NEXT_BLOCK \ + addi rSP,rSP,16; /* increment pointers per bloc */ \ + addi rDP,rDP,16; +#define LOAD_IV(reg, off) \ + lwz reg,off(rIP); /* IV loading with offset */ +#define SAVE_IV(reg, off) \ + stw reg,off(rIP); /* IV saving with offset */ +#define START_IV /* nothing to reset */ +#define CBC_DEC 16 /* CBC decrement per block */ +#define CTR_DEC 1 /* CTR decrement one byte */ + +#else /* Macros for little endian */ + +#define LOAD_DATA(reg, off) \ + lwbrx reg,0,rSP; /* load reversed */ \ + addi rSP,rSP,4; /* and increment pointer */ +#define SAVE_DATA(reg, off) \ + stwbrx reg,0,rDP; /* save reversed */ \ + addi rDP,rDP,4; /* and increment pointer */ +#define NEXT_BLOCK /* nothing todo */ +#define LOAD_IV(reg, off) \ + lwbrx reg,0,rIP; /* load reversed */ \ + addi rIP,rIP,4; /* and increment pointer */ +#define SAVE_IV(reg, off) \ + stwbrx reg,0,rIP; /* load reversed */ \ + addi rIP,rIP,4; /* and increment pointer */ +#define START_IV \ + subi rIP,rIP,16; /* must reset pointer */ +#define CBC_DEC 32 /* 2 blocks because of incs */ +#define CTR_DEC 17 /* 1 block because of incs */ + +#endif + +#define SAVE_0_REGS +#define LOAD_0_REGS + +#define SAVE_4_REGS \ + stw rI0,96(r1); /* save 32 bit registers */ \ + stw rI1,100(r1); \ + stw rI2,104(r1); \ + stw rI3,108(r1); + +#define LOAD_4_REGS \ + lwz rI0,96(r1); /* restore 32 bit registers */ \ + lwz rI1,100(r1); \ + lwz rI2,104(r1); \ + lwz rI3,108(r1); + +#define SAVE_8_REGS \ + SAVE_4_REGS \ + stw rG0,112(r1); /* save 32 bit registers */ \ + stw rG1,116(r1); \ + stw rG2,120(r1); \ + stw rG3,124(r1); + +#define LOAD_8_REGS \ + LOAD_4_REGS \ + lwz rG0,112(r1); /* restore 32 bit registers */ \ + lwz rG1,116(r1); \ + lwz rG2,120(r1); \ + lwz rG3,124(r1); + +#define INITIALIZE_CRYPT(tab,nr32bitregs) \ + mflr r0; \ + stwu r1,-160(r1); /* create stack frame */ \ + lis rT0,tab@h; /* en-/decryption table pointer */ \ + stw r0,8(r1); /* save link register */ \ + ori rT0,rT0,tab@l; \ + evstdw r14,16(r1); \ + mr rKS,rKP; \ + evstdw r15,24(r1); /* We must save non volatile */ \ + evstdw r16,32(r1); /* registers. Take the chance */ \ + evstdw r17,40(r1); /* and save the SPE part too */ \ + evstdw r18,48(r1); \ + evstdw r19,56(r1); \ + evstdw r20,64(r1); \ + evstdw r21,72(r1); \ + evstdw r22,80(r1); \ + evstdw r23,88(r1); \ + SAVE_##nr32bitregs##_REGS + +#define FINALIZE_CRYPT(nr32bitregs) \ + lwz r0,8(r1); \ + evldw r14,16(r1); /* restore SPE registers */ \ + evldw r15,24(r1); \ + evldw r16,32(r1); \ + evldw r17,40(r1); \ + evldw r18,48(r1); \ + evldw r19,56(r1); \ + evldw r20,64(r1); \ + evldw r21,72(r1); \ + evldw r22,80(r1); \ + evldw r23,88(r1); \ + LOAD_##nr32bitregs##_REGS \ + mtlr r0; /* restore link register */ \ + xor r0,r0,r0; \ + stw r0,16(r1); /* delete sensitive data */ \ + stw r0,24(r1); /* that we might have pushed */ \ + stw r0,32(r1); /* from other context that runs */ \ + stw r0,40(r1); /* the same code */ \ + stw r0,48(r1); \ + stw r0,56(r1); \ + stw r0,64(r1); \ + stw r0,72(r1); \ + stw r0,80(r1); \ + stw r0,88(r1); \ + addi r1,r1,160; /* cleanup stack frame */ + +#define ENDIAN_SWAP(t0, t1, s0, s1) \ + rotrwi t0,s0,8; /* swap endianness for 2 GPRs */ \ + rotrwi t1,s1,8; \ + rlwimi t0,s0,8,8,15; \ + rlwimi t1,s1,8,8,15; \ + rlwimi t0,s0,8,24,31; \ + rlwimi t1,s1,8,24,31; + +#define GF128_MUL(d0, d1, d2, d3, t0) \ + li t0,0x87; /* multiplication in GF128 */ \ + cmpwi d3,-1; \ + iselgt t0,0,t0; \ + rlwimi d3,d2,0,0,0; /* propagate "carry" bits */ \ + rotlwi d3,d3,1; \ + rlwimi d2,d1,0,0,0; \ + rotlwi d2,d2,1; \ + rlwimi d1,d0,0,0,0; \ + slwi d0,d0,1; /* shift left 128 bit */ \ + rotlwi d1,d1,1; \ + xor d0,d0,t0; + +#define START_KEY(d0, d1, d2, d3) \ + lwz rW0,0(rKP); \ + mtctr rRR; \ + lwz rW1,4(rKP); \ + lwz rW2,8(rKP); \ + lwz rW3,12(rKP); \ + xor rD0,d0,rW0; \ + xor rD1,d1,rW1; \ + xor rD2,d2,rW2; \ + xor rD3,d3,rW3; + +/* + * ppc_encrypt_aes(u8 *out, const u8 *in, u32 *key_enc, + * u32 rounds) + * + * called from glue layer to encrypt a single 16 byte block + * round values are AES128 = 4, AES192 = 5, AES256 = 6 + * + */ +_GLOBAL(ppc_encrypt_aes) + INITIALIZE_CRYPT(PPC_AES_4K_ENCTAB, 0) + LOAD_DATA(rD0, 0) + LOAD_DATA(rD1, 4) + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_encrypt_block + xor rD0,rD0,rW0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rW1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rW2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rW3 + SAVE_DATA(rD3, 12) + FINALIZE_CRYPT(0) + blr + +/* + * ppc_decrypt_aes(u8 *out, const u8 *in, u32 *key_dec, + * u32 rounds) + * + * called from glue layer to decrypt a single 16 byte block + * round values are AES128 = 4, AES192 = 5, AES256 = 6 + * + */ +_GLOBAL(ppc_decrypt_aes) + INITIALIZE_CRYPT(PPC_AES_4K_DECTAB,0) + LOAD_DATA(rD0, 0) + addi rT1,rT0,4096 + LOAD_DATA(rD1, 4) + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_decrypt_block + xor rD0,rD0,rW0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rW1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rW2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rW3 + SAVE_DATA(rD3, 12) + FINALIZE_CRYPT(0) + blr + +/* + * ppc_encrypt_ecb(u8 *out, const u8 *in, u32 *key_enc, + * u32 rounds, u32 bytes); + * + * called from glue layer to encrypt multiple blocks via ECB + * Bytes must be larger or equal 16 and only whole blocks are + * processed. round values are AES128 = 4, AES192 = 5 and + * AES256 = 6 + * + */ +_GLOBAL(ppc_encrypt_ecb) + INITIALIZE_CRYPT(PPC_AES_4K_ENCTAB, 0) +ppc_encrypt_ecb_loop: + LOAD_DATA(rD0, 0) + mr rKP,rKS + LOAD_DATA(rD1, 4) + subi rLN,rLN,16 + LOAD_DATA(rD2, 8) + cmpwi rLN,15 + LOAD_DATA(rD3, 12) + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_encrypt_block + xor rD0,rD0,rW0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rW1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rW2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rW3 + SAVE_DATA(rD3, 12) + NEXT_BLOCK + bt gt,ppc_encrypt_ecb_loop + FINALIZE_CRYPT(0) + blr + +/* + * ppc_decrypt_ecb(u8 *out, const u8 *in, u32 *key_dec, + * u32 rounds, u32 bytes); + * + * called from glue layer to decrypt multiple blocks via ECB + * Bytes must be larger or equal 16 and only whole blocks are + * processed. round values are AES128 = 4, AES192 = 5 and + * AES256 = 6 + * + */ +_GLOBAL(ppc_decrypt_ecb) + INITIALIZE_CRYPT(PPC_AES_4K_DECTAB, 0) + addi rT1,rT0,4096 +ppc_decrypt_ecb_loop: + LOAD_DATA(rD0, 0) + mr rKP,rKS + LOAD_DATA(rD1, 4) + subi rLN,rLN,16 + LOAD_DATA(rD2, 8) + cmpwi rLN,15 + LOAD_DATA(rD3, 12) + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_decrypt_block + xor rD0,rD0,rW0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rW1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rW2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rW3 + SAVE_DATA(rD3, 12) + NEXT_BLOCK + bt gt,ppc_decrypt_ecb_loop + FINALIZE_CRYPT(0) + blr + +/* + * ppc_encrypt_cbc(u8 *out, const u8 *in, u32 *key_enc, + * 32 rounds, u32 bytes, u8 *iv); + * + * called from glue layer to encrypt multiple blocks via CBC + * Bytes must be larger or equal 16 and only whole blocks are + * processed. round values are AES128 = 4, AES192 = 5 and + * AES256 = 6 + * + */ +_GLOBAL(ppc_encrypt_cbc) + INITIALIZE_CRYPT(PPC_AES_4K_ENCTAB, 4) + LOAD_IV(rI0, 0) + LOAD_IV(rI1, 4) + LOAD_IV(rI2, 8) + LOAD_IV(rI3, 12) +ppc_encrypt_cbc_loop: + LOAD_DATA(rD0, 0) + mr rKP,rKS + LOAD_DATA(rD1, 4) + subi rLN,rLN,16 + LOAD_DATA(rD2, 8) + cmpwi rLN,15 + LOAD_DATA(rD3, 12) + xor rD0,rD0,rI0 + xor rD1,rD1,rI1 + xor rD2,rD2,rI2 + xor rD3,rD3,rI3 + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_encrypt_block + xor rI0,rD0,rW0 + SAVE_DATA(rI0, 0) + xor rI1,rD1,rW1 + SAVE_DATA(rI1, 4) + xor rI2,rD2,rW2 + SAVE_DATA(rI2, 8) + xor rI3,rD3,rW3 + SAVE_DATA(rI3, 12) + NEXT_BLOCK + bt gt,ppc_encrypt_cbc_loop + START_IV + SAVE_IV(rI0, 0) + SAVE_IV(rI1, 4) + SAVE_IV(rI2, 8) + SAVE_IV(rI3, 12) + FINALIZE_CRYPT(4) + blr + +/* + * ppc_decrypt_cbc(u8 *out, const u8 *in, u32 *key_dec, + * u32 rounds, u32 bytes, u8 *iv); + * + * called from glue layer to decrypt multiple blocks via CBC + * round values are AES128 = 4, AES192 = 5, AES256 = 6 + * + */ +_GLOBAL(ppc_decrypt_cbc) + INITIALIZE_CRYPT(PPC_AES_4K_DECTAB, 4) + li rT1,15 + LOAD_IV(rI0, 0) + andc rLN,rLN,rT1 + LOAD_IV(rI1, 4) + subi rLN,rLN,16 + LOAD_IV(rI2, 8) + add rSP,rSP,rLN /* reverse processing */ + LOAD_IV(rI3, 12) + add rDP,rDP,rLN + LOAD_DATA(rD0, 0) + addi rT1,rT0,4096 + LOAD_DATA(rD1, 4) + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + START_IV + SAVE_IV(rD0, 0) + SAVE_IV(rD1, 4) + SAVE_IV(rD2, 8) + cmpwi rLN,16 + SAVE_IV(rD3, 12) + bt lt,ppc_decrypt_cbc_end +ppc_decrypt_cbc_loop: + mr rKP,rKS + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_decrypt_block + subi rLN,rLN,16 + subi rSP,rSP,CBC_DEC + xor rW0,rD0,rW0 + LOAD_DATA(rD0, 0) + xor rW1,rD1,rW1 + LOAD_DATA(rD1, 4) + xor rW2,rD2,rW2 + LOAD_DATA(rD2, 8) + xor rW3,rD3,rW3 + LOAD_DATA(rD3, 12) + xor rW0,rW0,rD0 + SAVE_DATA(rW0, 0) + xor rW1,rW1,rD1 + SAVE_DATA(rW1, 4) + xor rW2,rW2,rD2 + SAVE_DATA(rW2, 8) + xor rW3,rW3,rD3 + SAVE_DATA(rW3, 12) + cmpwi rLN,15 + subi rDP,rDP,CBC_DEC + bt gt,ppc_decrypt_cbc_loop +ppc_decrypt_cbc_end: + mr rKP,rKS + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_decrypt_block + xor rW0,rW0,rD0 + xor rW1,rW1,rD1 + xor rW2,rW2,rD2 + xor rW3,rW3,rD3 + xor rW0,rW0,rI0 /* decrypt with initial IV */ + SAVE_DATA(rW0, 0) + xor rW1,rW1,rI1 + SAVE_DATA(rW1, 4) + xor rW2,rW2,rI2 + SAVE_DATA(rW2, 8) + xor rW3,rW3,rI3 + SAVE_DATA(rW3, 12) + FINALIZE_CRYPT(4) + blr + +/* + * ppc_crypt_ctr(u8 *out, const u8 *in, u32 *key_enc, + * u32 rounds, u32 bytes, u8 *iv); + * + * called from glue layer to encrypt/decrypt multiple blocks + * via CTR. Number of bytes does not need to be a multiple of + * 16. Round values are AES128 = 4, AES192 = 5, AES256 = 6 + * + */ +_GLOBAL(ppc_crypt_ctr) + INITIALIZE_CRYPT(PPC_AES_4K_ENCTAB, 4) + LOAD_IV(rI0, 0) + LOAD_IV(rI1, 4) + LOAD_IV(rI2, 8) + cmpwi rLN,16 + LOAD_IV(rI3, 12) + START_IV + bt lt,ppc_crypt_ctr_partial +ppc_crypt_ctr_loop: + mr rKP,rKS + START_KEY(rI0, rI1, rI2, rI3) + bl ppc_encrypt_block + xor rW0,rD0,rW0 + xor rW1,rD1,rW1 + xor rW2,rD2,rW2 + xor rW3,rD3,rW3 + LOAD_DATA(rD0, 0) + subi rLN,rLN,16 + LOAD_DATA(rD1, 4) + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + xor rD0,rD0,rW0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rW1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rW2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rW3 + SAVE_DATA(rD3, 12) + addic rI3,rI3,1 /* increase counter */ + addze rI2,rI2 + addze rI1,rI1 + addze rI0,rI0 + NEXT_BLOCK + cmpwi rLN,15 + bt gt,ppc_crypt_ctr_loop +ppc_crypt_ctr_partial: + cmpwi rLN,0 + bt eq,ppc_crypt_ctr_end + mr rKP,rKS + START_KEY(rI0, rI1, rI2, rI3) + bl ppc_encrypt_block + xor rW0,rD0,rW0 + SAVE_IV(rW0, 0) + xor rW1,rD1,rW1 + SAVE_IV(rW1, 4) + xor rW2,rD2,rW2 + SAVE_IV(rW2, 8) + xor rW3,rD3,rW3 + SAVE_IV(rW3, 12) + mtctr rLN + subi rIP,rIP,CTR_DEC + subi rSP,rSP,1 + subi rDP,rDP,1 +ppc_crypt_ctr_xorbyte: + lbzu rW4,1(rIP) /* bytewise xor for partial block */ + lbzu rW5,1(rSP) + xor rW4,rW4,rW5 + stbu rW4,1(rDP) + bdnz ppc_crypt_ctr_xorbyte + subf rIP,rLN,rIP + addi rIP,rIP,1 + addic rI3,rI3,1 + addze rI2,rI2 + addze rI1,rI1 + addze rI0,rI0 +ppc_crypt_ctr_end: + SAVE_IV(rI0, 0) + SAVE_IV(rI1, 4) + SAVE_IV(rI2, 8) + SAVE_IV(rI3, 12) + FINALIZE_CRYPT(4) + blr + +/* + * ppc_encrypt_xts(u8 *out, const u8 *in, u32 *key_enc, + * u32 rounds, u32 bytes, u8 *iv, u32 *key_twk); + * + * called from glue layer to encrypt multiple blocks via XTS + * If key_twk is given, the initial IV encryption will be + * processed too. Round values are AES128 = 4, AES192 = 5, + * AES256 = 6 + * + */ +_GLOBAL(ppc_encrypt_xts) + INITIALIZE_CRYPT(PPC_AES_4K_ENCTAB, 8) + LOAD_IV(rI0, 0) + LOAD_IV(rI1, 4) + LOAD_IV(rI2, 8) + cmpwi rKT,0 + LOAD_IV(rI3, 12) + bt eq,ppc_encrypt_xts_notweak + mr rKP,rKT + START_KEY(rI0, rI1, rI2, rI3) + bl ppc_encrypt_block + xor rI0,rD0,rW0 + xor rI1,rD1,rW1 + xor rI2,rD2,rW2 + xor rI3,rD3,rW3 +ppc_encrypt_xts_notweak: + ENDIAN_SWAP(rG0, rG1, rI0, rI1) + ENDIAN_SWAP(rG2, rG3, rI2, rI3) +ppc_encrypt_xts_loop: + LOAD_DATA(rD0, 0) + mr rKP,rKS + LOAD_DATA(rD1, 4) + subi rLN,rLN,16 + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + xor rD0,rD0,rI0 + xor rD1,rD1,rI1 + xor rD2,rD2,rI2 + xor rD3,rD3,rI3 + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_encrypt_block + xor rD0,rD0,rW0 + xor rD1,rD1,rW1 + xor rD2,rD2,rW2 + xor rD3,rD3,rW3 + xor rD0,rD0,rI0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rI1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rI2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rI3 + SAVE_DATA(rD3, 12) + GF128_MUL(rG0, rG1, rG2, rG3, rW0) + ENDIAN_SWAP(rI0, rI1, rG0, rG1) + ENDIAN_SWAP(rI2, rI3, rG2, rG3) + cmpwi rLN,0 + NEXT_BLOCK + bt gt,ppc_encrypt_xts_loop + START_IV + SAVE_IV(rI0, 0) + SAVE_IV(rI1, 4) + SAVE_IV(rI2, 8) + SAVE_IV(rI3, 12) + FINALIZE_CRYPT(8) + blr + +/* + * ppc_decrypt_xts(u8 *out, const u8 *in, u32 *key_dec, + * u32 rounds, u32 blocks, u8 *iv, u32 *key_twk); + * + * called from glue layer to decrypt multiple blocks via XTS + * If key_twk is given, the initial IV encryption will be + * processed too. Round values are AES128 = 4, AES192 = 5, + * AES256 = 6 + * + */ +_GLOBAL(ppc_decrypt_xts) + INITIALIZE_CRYPT(PPC_AES_4K_DECTAB, 8) + LOAD_IV(rI0, 0) + addi rT1,rT0,4096 + LOAD_IV(rI1, 4) + LOAD_IV(rI2, 8) + cmpwi rKT,0 + LOAD_IV(rI3, 12) + bt eq,ppc_decrypt_xts_notweak + subi rT0,rT0,4096 + mr rKP,rKT + START_KEY(rI0, rI1, rI2, rI3) + bl ppc_encrypt_block + xor rI0,rD0,rW0 + xor rI1,rD1,rW1 + xor rI2,rD2,rW2 + xor rI3,rD3,rW3 + addi rT0,rT0,4096 +ppc_decrypt_xts_notweak: + ENDIAN_SWAP(rG0, rG1, rI0, rI1) + ENDIAN_SWAP(rG2, rG3, rI2, rI3) +ppc_decrypt_xts_loop: + LOAD_DATA(rD0, 0) + mr rKP,rKS + LOAD_DATA(rD1, 4) + subi rLN,rLN,16 + LOAD_DATA(rD2, 8) + LOAD_DATA(rD3, 12) + xor rD0,rD0,rI0 + xor rD1,rD1,rI1 + xor rD2,rD2,rI2 + xor rD3,rD3,rI3 + START_KEY(rD0, rD1, rD2, rD3) + bl ppc_decrypt_block + xor rD0,rD0,rW0 + xor rD1,rD1,rW1 + xor rD2,rD2,rW2 + xor rD3,rD3,rW3 + xor rD0,rD0,rI0 + SAVE_DATA(rD0, 0) + xor rD1,rD1,rI1 + SAVE_DATA(rD1, 4) + xor rD2,rD2,rI2 + SAVE_DATA(rD2, 8) + xor rD3,rD3,rI3 + SAVE_DATA(rD3, 12) + GF128_MUL(rG0, rG1, rG2, rG3, rW0) + ENDIAN_SWAP(rI0, rI1, rG0, rG1) + ENDIAN_SWAP(rI2, rI3, rG2, rG3) + cmpwi rLN,0 + NEXT_BLOCK + bt gt,ppc_decrypt_xts_loop + START_IV + SAVE_IV(rI0, 0) + SAVE_IV(rI1, 4) + SAVE_IV(rI2, 8) + SAVE_IV(rI3, 12) + FINALIZE_CRYPT(8) + blr diff --git a/arch/powerpc/crypto/aes-spe-regs.h b/arch/powerpc/crypto/aes-spe-regs.h new file mode 100644 index 000000000000..30d217b399c3 --- /dev/null +++ b/arch/powerpc/crypto/aes-spe-regs.h @@ -0,0 +1,42 @@ +/* + * Common registers for PPC AES implementation + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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. + * + */ + +#define rKS r0 /* copy of en-/decryption key pointer */ +#define rDP r3 /* destination pointer */ +#define rSP r4 /* source pointer */ +#define rKP r5 /* pointer to en-/decryption key pointer */ +#define rRR r6 /* en-/decryption rounds */ +#define rLN r7 /* length of data to be processed */ +#define rIP r8 /* potiner to IV (CBC/CTR/XTS modes) */ +#define rKT r9 /* pointer to tweak key (XTS mode) */ +#define rT0 r11 /* pointers to en-/decrpytion tables */ +#define rT1 r10 +#define rD0 r9 /* data */ +#define rD1 r14 +#define rD2 r12 +#define rD3 r15 +#define rW0 r16 /* working registers */ +#define rW1 r17 +#define rW2 r18 +#define rW3 r19 +#define rW4 r20 +#define rW5 r21 +#define rW6 r22 +#define rW7 r23 +#define rI0 r24 /* IV */ +#define rI1 r25 +#define rI2 r26 +#define rI3 r27 +#define rG0 r28 /* endian reversed tweak (XTS mode) */ +#define rG1 r29 +#define rG2 r30 +#define rG3 r31 diff --git a/arch/powerpc/crypto/aes-tab-4k.S b/arch/powerpc/crypto/aes-tab-4k.S new file mode 100644 index 000000000000..701e60240dc3 --- /dev/null +++ b/arch/powerpc/crypto/aes-tab-4k.S @@ -0,0 +1,331 @@ +/* + * 4K AES tables for PPC AES implementation + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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. + * + */ + +/* + * These big endian AES encryption/decryption tables have been taken from + * crypto/aes_generic.c and are designed to be simply accessed by a combination + * of rlwimi/lwz instructions with a minimum of table registers (usually only + * one required). Thus they are aligned to 4K. The locality of rotated values + * is derived from the reduced offsets that are available in the SPE load + * instructions. E.g. evldw, evlwwsplat, ... + * + * For the safety-conscious it has to be noted that they might be vulnerable + * to cache timing attacks because of their size. Nevertheless in contrast to + * the generic tables they have been reduced from 16KB to 8KB + 256 bytes. + * This is a quite good tradeoff for low power devices (e.g. routers) without + * dedicated encryption hardware where we usually have no multiuser + * environment. + * + */ + +#define R(a, b, c, d) \ + 0x##a##b##c##d, 0x##d##a##b##c, 0x##c##d##a##b, 0x##b##c##d##a + +.data +.align 12 +.globl PPC_AES_4K_ENCTAB +PPC_AES_4K_ENCTAB: +/* encryption table, same as crypto_ft_tab in crypto/aes-generic.c */ + .long R(c6, 63, 63, a5), R(f8, 7c, 7c, 84) + .long R(ee, 77, 77, 99), R(f6, 7b, 7b, 8d) + .long R(ff, f2, f2, 0d), R(d6, 6b, 6b, bd) + .long R(de, 6f, 6f, b1), R(91, c5, c5, 54) + .long R(60, 30, 30, 50), R(02, 01, 01, 03) + .long R(ce, 67, 67, a9), R(56, 2b, 2b, 7d) + .long R(e7, fe, fe, 19), R(b5, d7, d7, 62) + .long R(4d, ab, ab, e6), R(ec, 76, 76, 9a) + .long R(8f, ca, ca, 45), R(1f, 82, 82, 9d) + .long R(89, c9, c9, 40), R(fa, 7d, 7d, 87) + .long R(ef, fa, fa, 15), R(b2, 59, 59, eb) + .long R(8e, 47, 47, c9), R(fb, f0, f0, 0b) + .long R(41, ad, ad, ec), R(b3, d4, d4, 67) + .long R(5f, a2, a2, fd), R(45, af, af, ea) + .long R(23, 9c, 9c, bf), R(53, a4, a4, f7) + .long R(e4, 72, 72, 96), R(9b, c0, c0, 5b) + .long R(75, b7, b7, c2), R(e1, fd, fd, 1c) + .long R(3d, 93, 93, ae), R(4c, 26, 26, 6a) + .long R(6c, 36, 36, 5a), R(7e, 3f, 3f, 41) + .long R(f5, f7, f7, 02), R(83, cc, cc, 4f) + .long R(68, 34, 34, 5c), R(51, a5, a5, f4) + .long R(d1, e5, e5, 34), R(f9, f1, f1, 08) + .long R(e2, 71, 71, 93), R(ab, d8, d8, 73) + .long R(62, 31, 31, 53), R(2a, 15, 15, 3f) + .long R(08, 04, 04, 0c), R(95, c7, c7, 52) + .long R(46, 23, 23, 65), R(9d, c3, c3, 5e) + .long R(30, 18, 18, 28), R(37, 96, 96, a1) + .long R(0a, 05, 05, 0f), R(2f, 9a, 9a, b5) + .long R(0e, 07, 07, 09), R(24, 12, 12, 36) + .long R(1b, 80, 80, 9b), R(df, e2, e2, 3d) + .long R(cd, eb, eb, 26), R(4e, 27, 27, 69) + .long R(7f, b2, b2, cd), R(ea, 75, 75, 9f) + .long R(12, 09, 09, 1b), R(1d, 83, 83, 9e) + .long R(58, 2c, 2c, 74), R(34, 1a, 1a, 2e) + .long R(36, 1b, 1b, 2d), R(dc, 6e, 6e, b2) + .long R(b4, 5a, 5a, ee), R(5b, a0, a0, fb) + .long R(a4, 52, 52, f6), R(76, 3b, 3b, 4d) + .long R(b7, d6, d6, 61), R(7d, b3, b3, ce) + .long R(52, 29, 29, 7b), R(dd, e3, e3, 3e) + .long R(5e, 2f, 2f, 71), R(13, 84, 84, 97) + .long R(a6, 53, 53, f5), R(b9, d1, d1, 68) + .long R(00, 00, 00, 00), R(c1, ed, ed, 2c) + .long R(40, 20, 20, 60), R(e3, fc, fc, 1f) + .long R(79, b1, b1, c8), R(b6, 5b, 5b, ed) + .long R(d4, 6a, 6a, be), R(8d, cb, cb, 46) + .long R(67, be, be, d9), R(72, 39, 39, 4b) + .long R(94, 4a, 4a, de), R(98, 4c, 4c, d4) + .long R(b0, 58, 58, e8), R(85, cf, cf, 4a) + .long R(bb, d0, d0, 6b), R(c5, ef, ef, 2a) + .long R(4f, aa, aa, e5), R(ed, fb, fb, 16) + .long R(86, 43, 43, c5), R(9a, 4d, 4d, d7) + .long R(66, 33, 33, 55), R(11, 85, 85, 94) + .long R(8a, 45, 45, cf), R(e9, f9, f9, 10) + .long R(04, 02, 02, 06), R(fe, 7f, 7f, 81) + .long R(a0, 50, 50, f0), R(78, 3c, 3c, 44) + .long R(25, 9f, 9f, ba), R(4b, a8, a8, e3) + .long R(a2, 51, 51, f3), R(5d, a3, a3, fe) + .long R(80, 40, 40, c0), R(05, 8f, 8f, 8a) + .long R(3f, 92, 92, ad), R(21, 9d, 9d, bc) + .long R(70, 38, 38, 48), R(f1, f5, f5, 04) + .long R(63, bc, bc, df), R(77, b6, b6, c1) + .long R(af, da, da, 75), R(42, 21, 21, 63) + .long R(20, 10, 10, 30), R(e5, ff, ff, 1a) + .long R(fd, f3, f3, 0e), R(bf, d2, d2, 6d) + .long R(81, cd, cd, 4c), R(18, 0c, 0c, 14) + .long R(26, 13, 13, 35), R(c3, ec, ec, 2f) + .long R(be, 5f, 5f, e1), R(35, 97, 97, a2) + .long R(88, 44, 44, cc), R(2e, 17, 17, 39) + .long R(93, c4, c4, 57), R(55, a7, a7, f2) + .long R(fc, 7e, 7e, 82), R(7a, 3d, 3d, 47) + .long R(c8, 64, 64, ac), R(ba, 5d, 5d, e7) + .long R(32, 19, 19, 2b), R(e6, 73, 73, 95) + .long R(c0, 60, 60, a0), R(19, 81, 81, 98) + .long R(9e, 4f, 4f, d1), R(a3, dc, dc, 7f) + .long R(44, 22, 22, 66), R(54, 2a, 2a, 7e) + .long R(3b, 90, 90, ab), R(0b, 88, 88, 83) + .long R(8c, 46, 46, ca), R(c7, ee, ee, 29) + .long R(6b, b8, b8, d3), R(28, 14, 14, 3c) + .long R(a7, de, de, 79), R(bc, 5e, 5e, e2) + .long R(16, 0b, 0b, 1d), R(ad, db, db, 76) + .long R(db, e0, e0, 3b), R(64, 32, 32, 56) + .long R(74, 3a, 3a, 4e), R(14, 0a, 0a, 1e) + .long R(92, 49, 49, db), R(0c, 06, 06, 0a) + .long R(48, 24, 24, 6c), R(b8, 5c, 5c, e4) + .long R(9f, c2, c2, 5d), R(bd, d3, d3, 6e) + .long R(43, ac, ac, ef), R(c4, 62, 62, a6) + .long R(39, 91, 91, a8), R(31, 95, 95, a4) + .long R(d3, e4, e4, 37), R(f2, 79, 79, 8b) + .long R(d5, e7, e7, 32), R(8b, c8, c8, 43) + .long R(6e, 37, 37, 59), R(da, 6d, 6d, b7) + .long R(01, 8d, 8d, 8c), R(b1, d5, d5, 64) + .long R(9c, 4e, 4e, d2), R(49, a9, a9, e0) + .long R(d8, 6c, 6c, b4), R(ac, 56, 56, fa) + .long R(f3, f4, f4, 07), R(cf, ea, ea, 25) + .long R(ca, 65, 65, af), R(f4, 7a, 7a, 8e) + .long R(47, ae, ae, e9), R(10, 08, 08, 18) + .long R(6f, ba, ba, d5), R(f0, 78, 78, 88) + .long R(4a, 25, 25, 6f), R(5c, 2e, 2e, 72) + .long R(38, 1c, 1c, 24), R(57, a6, a6, f1) + .long R(73, b4, b4, c7), R(97, c6, c6, 51) + .long R(cb, e8, e8, 23), R(a1, dd, dd, 7c) + .long R(e8, 74, 74, 9c), R(3e, 1f, 1f, 21) + .long R(96, 4b, 4b, dd), R(61, bd, bd, dc) + .long R(0d, 8b, 8b, 86), R(0f, 8a, 8a, 85) + .long R(e0, 70, 70, 90), R(7c, 3e, 3e, 42) + .long R(71, b5, b5, c4), R(cc, 66, 66, aa) + .long R(90, 48, 48, d8), R(06, 03, 03, 05) + .long R(f7, f6, f6, 01), R(1c, 0e, 0e, 12) + .long R(c2, 61, 61, a3), R(6a, 35, 35, 5f) + .long R(ae, 57, 57, f9), R(69, b9, b9, d0) + .long R(17, 86, 86, 91), R(99, c1, c1, 58) + .long R(3a, 1d, 1d, 27), R(27, 9e, 9e, b9) + .long R(d9, e1, e1, 38), R(eb, f8, f8, 13) + .long R(2b, 98, 98, b3), R(22, 11, 11, 33) + .long R(d2, 69, 69, bb), R(a9, d9, d9, 70) + .long R(07, 8e, 8e, 89), R(33, 94, 94, a7) + .long R(2d, 9b, 9b, b6), R(3c, 1e, 1e, 22) + .long R(15, 87, 87, 92), R(c9, e9, e9, 20) + .long R(87, ce, ce, 49), R(aa, 55, 55, ff) + .long R(50, 28, 28, 78), R(a5, df, df, 7a) + .long R(03, 8c, 8c, 8f), R(59, a1, a1, f8) + .long R(09, 89, 89, 80), R(1a, 0d, 0d, 17) + .long R(65, bf, bf, da), R(d7, e6, e6, 31) + .long R(84, 42, 42, c6), R(d0, 68, 68, b8) + .long R(82, 41, 41, c3), R(29, 99, 99, b0) + .long R(5a, 2d, 2d, 77), R(1e, 0f, 0f, 11) + .long R(7b, b0, b0, cb), R(a8, 54, 54, fc) + .long R(6d, bb, bb, d6), R(2c, 16, 16, 3a) +.globl PPC_AES_4K_DECTAB +PPC_AES_4K_DECTAB: +/* decryption table, same as crypto_it_tab in crypto/aes-generic.c */ + .long R(51, f4, a7, 50), R(7e, 41, 65, 53) + .long R(1a, 17, a4, c3), R(3a, 27, 5e, 96) + .long R(3b, ab, 6b, cb), R(1f, 9d, 45, f1) + .long R(ac, fa, 58, ab), R(4b, e3, 03, 93) + .long R(20, 30, fa, 55), R(ad, 76, 6d, f6) + .long R(88, cc, 76, 91), R(f5, 02, 4c, 25) + .long R(4f, e5, d7, fc), R(c5, 2a, cb, d7) + .long R(26, 35, 44, 80), R(b5, 62, a3, 8f) + .long R(de, b1, 5a, 49), R(25, ba, 1b, 67) + .long R(45, ea, 0e, 98), R(5d, fe, c0, e1) + .long R(c3, 2f, 75, 02), R(81, 4c, f0, 12) + .long R(8d, 46, 97, a3), R(6b, d3, f9, c6) + .long R(03, 8f, 5f, e7), R(15, 92, 9c, 95) + .long R(bf, 6d, 7a, eb), R(95, 52, 59, da) + .long R(d4, be, 83, 2d), R(58, 74, 21, d3) + .long R(49, e0, 69, 29), R(8e, c9, c8, 44) + .long R(75, c2, 89, 6a), R(f4, 8e, 79, 78) + .long R(99, 58, 3e, 6b), R(27, b9, 71, dd) + .long R(be, e1, 4f, b6), R(f0, 88, ad, 17) + .long R(c9, 20, ac, 66), R(7d, ce, 3a, b4) + .long R(63, df, 4a, 18), R(e5, 1a, 31, 82) + .long R(97, 51, 33, 60), R(62, 53, 7f, 45) + .long R(b1, 64, 77, e0), R(bb, 6b, ae, 84) + .long R(fe, 81, a0, 1c), R(f9, 08, 2b, 94) + .long R(70, 48, 68, 58), R(8f, 45, fd, 19) + .long R(94, de, 6c, 87), R(52, 7b, f8, b7) + .long R(ab, 73, d3, 23), R(72, 4b, 02, e2) + .long R(e3, 1f, 8f, 57), R(66, 55, ab, 2a) + .long R(b2, eb, 28, 07), R(2f, b5, c2, 03) + .long R(86, c5, 7b, 9a), R(d3, 37, 08, a5) + .long R(30, 28, 87, f2), R(23, bf, a5, b2) + .long R(02, 03, 6a, ba), R(ed, 16, 82, 5c) + .long R(8a, cf, 1c, 2b), R(a7, 79, b4, 92) + .long R(f3, 07, f2, f0), R(4e, 69, e2, a1) + .long R(65, da, f4, cd), R(06, 05, be, d5) + .long R(d1, 34, 62, 1f), R(c4, a6, fe, 8a) + .long R(34, 2e, 53, 9d), R(a2, f3, 55, a0) + .long R(05, 8a, e1, 32), R(a4, f6, eb, 75) + .long R(0b, 83, ec, 39), R(40, 60, ef, aa) + .long R(5e, 71, 9f, 06), R(bd, 6e, 10, 51) + .long R(3e, 21, 8a, f9), R(96, dd, 06, 3d) + .long R(dd, 3e, 05, ae), R(4d, e6, bd, 46) + .long R(91, 54, 8d, b5), R(71, c4, 5d, 05) + .long R(04, 06, d4, 6f), R(60, 50, 15, ff) + .long R(19, 98, fb, 24), R(d6, bd, e9, 97) + .long R(89, 40, 43, cc), R(67, d9, 9e, 77) + .long R(b0, e8, 42, bd), R(07, 89, 8b, 88) + .long R(e7, 19, 5b, 38), R(79, c8, ee, db) + .long R(a1, 7c, 0a, 47), R(7c, 42, 0f, e9) + .long R(f8, 84, 1e, c9), R(00, 00, 00, 00) + .long R(09, 80, 86, 83), R(32, 2b, ed, 48) + .long R(1e, 11, 70, ac), R(6c, 5a, 72, 4e) + .long R(fd, 0e, ff, fb), R(0f, 85, 38, 56) + .long R(3d, ae, d5, 1e), R(36, 2d, 39, 27) + .long R(0a, 0f, d9, 64), R(68, 5c, a6, 21) + .long R(9b, 5b, 54, d1), R(24, 36, 2e, 3a) + .long R(0c, 0a, 67, b1), R(93, 57, e7, 0f) + .long R(b4, ee, 96, d2), R(1b, 9b, 91, 9e) + .long R(80, c0, c5, 4f), R(61, dc, 20, a2) + .long R(5a, 77, 4b, 69), R(1c, 12, 1a, 16) + .long R(e2, 93, ba, 0a), R(c0, a0, 2a, e5) + .long R(3c, 22, e0, 43), R(12, 1b, 17, 1d) + .long R(0e, 09, 0d, 0b), R(f2, 8b, c7, ad) + .long R(2d, b6, a8, b9), R(14, 1e, a9, c8) + .long R(57, f1, 19, 85), R(af, 75, 07, 4c) + .long R(ee, 99, dd, bb), R(a3, 7f, 60, fd) + .long R(f7, 01, 26, 9f), R(5c, 72, f5, bc) + .long R(44, 66, 3b, c5), R(5b, fb, 7e, 34) + .long R(8b, 43, 29, 76), R(cb, 23, c6, dc) + .long R(b6, ed, fc, 68), R(b8, e4, f1, 63) + .long R(d7, 31, dc, ca), R(42, 63, 85, 10) + .long R(13, 97, 22, 40), R(84, c6, 11, 20) + .long R(85, 4a, 24, 7d), R(d2, bb, 3d, f8) + .long R(ae, f9, 32, 11), R(c7, 29, a1, 6d) + .long R(1d, 9e, 2f, 4b), R(dc, b2, 30, f3) + .long R(0d, 86, 52, ec), R(77, c1, e3, d0) + .long R(2b, b3, 16, 6c), R(a9, 70, b9, 99) + .long R(11, 94, 48, fa), R(47, e9, 64, 22) + .long R(a8, fc, 8c, c4), R(a0, f0, 3f, 1a) + .long R(56, 7d, 2c, d8), R(22, 33, 90, ef) + .long R(87, 49, 4e, c7), R(d9, 38, d1, c1) + .long R(8c, ca, a2, fe), R(98, d4, 0b, 36) + .long R(a6, f5, 81, cf), R(a5, 7a, de, 28) + .long R(da, b7, 8e, 26), R(3f, ad, bf, a4) + .long R(2c, 3a, 9d, e4), R(50, 78, 92, 0d) + .long R(6a, 5f, cc, 9b), R(54, 7e, 46, 62) + .long R(f6, 8d, 13, c2), R(90, d8, b8, e8) + .long R(2e, 39, f7, 5e), R(82, c3, af, f5) + .long R(9f, 5d, 80, be), R(69, d0, 93, 7c) + .long R(6f, d5, 2d, a9), R(cf, 25, 12, b3) + .long R(c8, ac, 99, 3b), R(10, 18, 7d, a7) + .long R(e8, 9c, 63, 6e), R(db, 3b, bb, 7b) + .long R(cd, 26, 78, 09), R(6e, 59, 18, f4) + .long R(ec, 9a, b7, 01), R(83, 4f, 9a, a8) + .long R(e6, 95, 6e, 65), R(aa, ff, e6, 7e) + .long R(21, bc, cf, 08), R(ef, 15, e8, e6) + .long R(ba, e7, 9b, d9), R(4a, 6f, 36, ce) + .long R(ea, 9f, 09, d4), R(29, b0, 7c, d6) + .long R(31, a4, b2, af), R(2a, 3f, 23, 31) + .long R(c6, a5, 94, 30), R(35, a2, 66, c0) + .long R(74, 4e, bc, 37), R(fc, 82, ca, a6) + .long R(e0, 90, d0, b0), R(33, a7, d8, 15) + .long R(f1, 04, 98, 4a), R(41, ec, da, f7) + .long R(7f, cd, 50, 0e), R(17, 91, f6, 2f) + .long R(76, 4d, d6, 8d), R(43, ef, b0, 4d) + .long R(cc, aa, 4d, 54), R(e4, 96, 04, df) + .long R(9e, d1, b5, e3), R(4c, 6a, 88, 1b) + .long R(c1, 2c, 1f, b8), R(46, 65, 51, 7f) + .long R(9d, 5e, ea, 04), R(01, 8c, 35, 5d) + .long R(fa, 87, 74, 73), R(fb, 0b, 41, 2e) + .long R(b3, 67, 1d, 5a), R(92, db, d2, 52) + .long R(e9, 10, 56, 33), R(6d, d6, 47, 13) + .long R(9a, d7, 61, 8c), R(37, a1, 0c, 7a) + .long R(59, f8, 14, 8e), R(eb, 13, 3c, 89) + .long R(ce, a9, 27, ee), R(b7, 61, c9, 35) + .long R(e1, 1c, e5, ed), R(7a, 47, b1, 3c) + .long R(9c, d2, df, 59), R(55, f2, 73, 3f) + .long R(18, 14, ce, 79), R(73, c7, 37, bf) + .long R(53, f7, cd, ea), R(5f, fd, aa, 5b) + .long R(df, 3d, 6f, 14), R(78, 44, db, 86) + .long R(ca, af, f3, 81), R(b9, 68, c4, 3e) + .long R(38, 24, 34, 2c), R(c2, a3, 40, 5f) + .long R(16, 1d, c3, 72), R(bc, e2, 25, 0c) + .long R(28, 3c, 49, 8b), R(ff, 0d, 95, 41) + .long R(39, a8, 01, 71), R(08, 0c, b3, de) + .long R(d8, b4, e4, 9c), R(64, 56, c1, 90) + .long R(7b, cb, 84, 61), R(d5, 32, b6, 70) + .long R(48, 6c, 5c, 74), R(d0, b8, 57, 42) +.globl PPC_AES_4K_DECTAB2 +PPC_AES_4K_DECTAB2: +/* decryption table, same as crypto_il_tab in crypto/aes-generic.c */ + .byte 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38 + .byte 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb + .byte 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87 + .byte 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb + .byte 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d + .byte 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e + .byte 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2 + .byte 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25 + .byte 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16 + .byte 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92 + .byte 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda + .byte 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84 + .byte 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a + .byte 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06 + .byte 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02 + .byte 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b + .byte 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea + .byte 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73 + .byte 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85 + .byte 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e + .byte 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89 + .byte 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b + .byte 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20 + .byte 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4 + .byte 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31 + .byte 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f + .byte 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d + .byte 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef + .byte 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0 + .byte 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61 + .byte 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26 + .byte 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d diff --git a/arch/powerpc/crypto/md5-asm.S b/arch/powerpc/crypto/md5-asm.S new file mode 100644 index 000000000000..10cdf5bceebb --- /dev/null +++ b/arch/powerpc/crypto/md5-asm.S @@ -0,0 +1,243 @@ +/* + * Fast MD5 implementation for PPC + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> +#include <asm/asm-offsets.h> + +#define rHP r3 +#define rWP r4 + +#define rH0 r0 +#define rH1 r6 +#define rH2 r7 +#define rH3 r5 + +#define rW00 r8 +#define rW01 r9 +#define rW02 r10 +#define rW03 r11 +#define rW04 r12 +#define rW05 r14 +#define rW06 r15 +#define rW07 r16 +#define rW08 r17 +#define rW09 r18 +#define rW10 r19 +#define rW11 r20 +#define rW12 r21 +#define rW13 r22 +#define rW14 r23 +#define rW15 r24 + +#define rT0 r25 +#define rT1 r26 + +#define INITIALIZE \ + PPC_STLU r1,-INT_FRAME_SIZE(r1); \ + SAVE_8GPRS(14, r1); /* push registers onto stack */ \ + SAVE_4GPRS(22, r1); \ + SAVE_GPR(26, r1) + +#define FINALIZE \ + REST_8GPRS(14, r1); /* pop registers from stack */ \ + REST_4GPRS(22, r1); \ + REST_GPR(26, r1); \ + addi r1,r1,INT_FRAME_SIZE; + +#ifdef __BIG_ENDIAN__ +#define LOAD_DATA(reg, off) \ + lwbrx reg,0,rWP; /* load data */ +#define INC_PTR \ + addi rWP,rWP,4; /* increment per word */ +#define NEXT_BLOCK /* nothing to do */ +#else +#define LOAD_DATA(reg, off) \ + lwz reg,off(rWP); /* load data */ +#define INC_PTR /* nothing to do */ +#define NEXT_BLOCK \ + addi rWP,rWP,64; /* increment per block */ +#endif + +#define R_00_15(a, b, c, d, w0, w1, p, q, off, k0h, k0l, k1h, k1l) \ + LOAD_DATA(w0, off) /* W */ \ + and rT0,b,c; /* 1: f = b and c */ \ + INC_PTR /* ptr++ */ \ + andc rT1,d,b; /* 1: f' = ~b and d */ \ + LOAD_DATA(w1, off+4) /* W */ \ + or rT0,rT0,rT1; /* 1: f = f or f' */ \ + addi w0,w0,k0l; /* 1: wk = w + k */ \ + add a,a,rT0; /* 1: a = a + f */ \ + addis w0,w0,k0h; /* 1: wk = w + k' */ \ + addis w1,w1,k1h; /* 2: wk = w + k */ \ + add a,a,w0; /* 1: a = a + wk */ \ + addi w1,w1,k1l; /* 2: wk = w + k' */ \ + rotrwi a,a,p; /* 1: a = a rotl x */ \ + add d,d,w1; /* 2: a = a + wk */ \ + add a,a,b; /* 1: a = a + b */ \ + and rT0,a,b; /* 2: f = b and c */ \ + andc rT1,c,a; /* 2: f' = ~b and d */ \ + or rT0,rT0,rT1; /* 2: f = f or f' */ \ + add d,d,rT0; /* 2: a = a + f */ \ + INC_PTR /* ptr++ */ \ + rotrwi d,d,q; /* 2: a = a rotl x */ \ + add d,d,a; /* 2: a = a + b */ + +#define R_16_31(a, b, c, d, w0, w1, p, q, k0h, k0l, k1h, k1l) \ + andc rT0,c,d; /* 1: f = c and ~d */ \ + and rT1,b,d; /* 1: f' = b and d */ \ + addi w0,w0,k0l; /* 1: wk = w + k */ \ + or rT0,rT0,rT1; /* 1: f = f or f' */ \ + addis w0,w0,k0h; /* 1: wk = w + k' */ \ + add a,a,rT0; /* 1: a = a + f */ \ + addi w1,w1,k1l; /* 2: wk = w + k */ \ + add a,a,w0; /* 1: a = a + wk */ \ + addis w1,w1,k1h; /* 2: wk = w + k' */ \ + andc rT0,b,c; /* 2: f = c and ~d */ \ + rotrwi a,a,p; /* 1: a = a rotl x */ \ + add a,a,b; /* 1: a = a + b */ \ + add d,d,w1; /* 2: a = a + wk */ \ + and rT1,a,c; /* 2: f' = b and d */ \ + or rT0,rT0,rT1; /* 2: f = f or f' */ \ + add d,d,rT0; /* 2: a = a + f */ \ + rotrwi d,d,q; /* 2: a = a rotl x */ \ + add d,d,a; /* 2: a = a +b */ + +#define R_32_47(a, b, c, d, w0, w1, p, q, k0h, k0l, k1h, k1l) \ + xor rT0,b,c; /* 1: f' = b xor c */ \ + addi w0,w0,k0l; /* 1: wk = w + k */ \ + xor rT1,rT0,d; /* 1: f = f xor f' */ \ + addis w0,w0,k0h; /* 1: wk = w + k' */ \ + add a,a,rT1; /* 1: a = a + f */ \ + addi w1,w1,k1l; /* 2: wk = w + k */ \ + add a,a,w0; /* 1: a = a + wk */ \ + addis w1,w1,k1h; /* 2: wk = w + k' */ \ + rotrwi a,a,p; /* 1: a = a rotl x */ \ + add d,d,w1; /* 2: a = a + wk */ \ + add a,a,b; /* 1: a = a + b */ \ + xor rT1,rT0,a; /* 2: f = b xor f' */ \ + add d,d,rT1; /* 2: a = a + f */ \ + rotrwi d,d,q; /* 2: a = a rotl x */ \ + add d,d,a; /* 2: a = a + b */ + +#define R_48_63(a, b, c, d, w0, w1, p, q, k0h, k0l, k1h, k1l) \ + addi w0,w0,k0l; /* 1: w = w + k */ \ + orc rT0,b,d; /* 1: f = b or ~d */ \ + addis w0,w0,k0h; /* 1: w = w + k' */ \ + xor rT0,rT0,c; /* 1: f = f xor c */ \ + add a,a,w0; /* 1: a = a + wk */ \ + addi w1,w1,k1l; /* 2: w = w + k */ \ + add a,a,rT0; /* 1: a = a + f */ \ + addis w1,w1,k1h; /* 2: w = w + k' */ \ + rotrwi a,a,p; /* 1: a = a rotl x */ \ + add a,a,b; /* 1: a = a + b */ \ + orc rT0,a,c; /* 2: f = b or ~d */ \ + add d,d,w1; /* 2: a = a + wk */ \ + xor rT0,rT0,b; /* 2: f = f xor c */ \ + add d,d,rT0; /* 2: a = a + f */ \ + rotrwi d,d,q; /* 2: a = a rotl x */ \ + add d,d,a; /* 2: a = a + b */ + +_GLOBAL(ppc_md5_transform) + INITIALIZE + + mtctr r5 + lwz rH0,0(rHP) + lwz rH1,4(rHP) + lwz rH2,8(rHP) + lwz rH3,12(rHP) + +ppc_md5_main: + R_00_15(rH0, rH1, rH2, rH3, rW00, rW01, 25, 20, 0, + 0xd76b, -23432, 0xe8c8, -18602) + R_00_15(rH2, rH3, rH0, rH1, rW02, rW03, 15, 10, 8, + 0x2420, 0x70db, 0xc1be, -12562) + R_00_15(rH0, rH1, rH2, rH3, rW04, rW05, 25, 20, 16, + 0xf57c, 0x0faf, 0x4788, -14806) + R_00_15(rH2, rH3, rH0, rH1, rW06, rW07, 15, 10, 24, + 0xa830, 0x4613, 0xfd47, -27391) + R_00_15(rH0, rH1, rH2, rH3, rW08, rW09, 25, 20, 32, + 0x6981, -26408, 0x8b45, -2129) + R_00_15(rH2, rH3, rH0, rH1, rW10, rW11, 15, 10, 40, + 0xffff, 0x5bb1, 0x895d, -10306) + R_00_15(rH0, rH1, rH2, rH3, rW12, rW13, 25, 20, 48, + 0x6b90, 0x1122, 0xfd98, 0x7193) + R_00_15(rH2, rH3, rH0, rH1, rW14, rW15, 15, 10, 56, + 0xa679, 0x438e, 0x49b4, 0x0821) + + R_16_31(rH0, rH1, rH2, rH3, rW01, rW06, 27, 23, + 0x0d56, 0x6e0c, 0x1810, 0x6d2d) + R_16_31(rH2, rH3, rH0, rH1, rW11, rW00, 18, 12, + 0x9d02, -32109, 0x124c, 0x2332) + R_16_31(rH0, rH1, rH2, rH3, rW05, rW10, 27, 23, + 0x8ea7, 0x4a33, 0x0245, -18270) + R_16_31(rH2, rH3, rH0, rH1, rW15, rW04, 18, 12, + 0x8eee, -8608, 0xf258, -5095) + R_16_31(rH0, rH1, rH2, rH3, rW09, rW14, 27, 23, + 0x969d, -10697, 0x1cbe, -15288) + R_16_31(rH2, rH3, rH0, rH1, rW03, rW08, 18, 12, + 0x3317, 0x3e99, 0xdbd9, 0x7c15) + R_16_31(rH0, rH1, rH2, rH3, rW13, rW02, 27, 23, + 0xac4b, 0x7772, 0xd8cf, 0x331d) + R_16_31(rH2, rH3, rH0, rH1, rW07, rW12, 18, 12, + 0x6a28, 0x6dd8, 0x219a, 0x3b68) + + R_32_47(rH0, rH1, rH2, rH3, rW05, rW08, 28, 21, + 0x29cb, 0x28e5, 0x4218, -7788) + R_32_47(rH2, rH3, rH0, rH1, rW11, rW14, 16, 9, + 0x473f, 0x06d1, 0x3aae, 0x3036) + R_32_47(rH0, rH1, rH2, rH3, rW01, rW04, 28, 21, + 0xaea1, -15134, 0x640b, -11295) + R_32_47(rH2, rH3, rH0, rH1, rW07, rW10, 16, 9, + 0x8f4c, 0x4887, 0xbc7c, -22499) + R_32_47(rH0, rH1, rH2, rH3, rW13, rW00, 28, 21, + 0x7eb8, -27199, 0x00ea, 0x6050) + R_32_47(rH2, rH3, rH0, rH1, rW03, rW06, 16, 9, + 0xe01a, 0x22fe, 0x4447, 0x69c5) + R_32_47(rH0, rH1, rH2, rH3, rW09, rW12, 28, 21, + 0xb7f3, 0x0253, 0x59b1, 0x4d5b) + R_32_47(rH2, rH3, rH0, rH1, rW15, rW02, 16, 9, + 0x4701, -27017, 0xc7bd, -19859) + + R_48_63(rH0, rH1, rH2, rH3, rW00, rW07, 26, 22, + 0x0988, -1462, 0x4c70, -19401) + R_48_63(rH2, rH3, rH0, rH1, rW14, rW05, 17, 11, + 0xadaf, -5221, 0xfc99, 0x66f7) + R_48_63(rH0, rH1, rH2, rH3, rW12, rW03, 26, 22, + 0x7e80, -16418, 0xba1e, -25587) + R_48_63(rH2, rH3, rH0, rH1, rW10, rW01, 17, 11, + 0x4130, 0x380d, 0xe0c5, 0x738d) + lwz rW00,0(rHP) + R_48_63(rH0, rH1, rH2, rH3, rW08, rW15, 26, 22, + 0xe837, -30770, 0xde8a, 0x69e8) + lwz rW14,4(rHP) + R_48_63(rH2, rH3, rH0, rH1, rW06, rW13, 17, 11, + 0x9e79, 0x260f, 0x256d, -27941) + lwz rW12,8(rHP) + R_48_63(rH0, rH1, rH2, rH3, rW04, rW11, 26, 22, + 0xab75, -20775, 0x4f9e, -28397) + lwz rW10,12(rHP) + R_48_63(rH2, rH3, rH0, rH1, rW02, rW09, 17, 11, + 0x662b, 0x7c56, 0x11b2, 0x0358) + + add rH0,rH0,rW00 + stw rH0,0(rHP) + add rH1,rH1,rW14 + stw rH1,4(rHP) + add rH2,rH2,rW12 + stw rH2,8(rHP) + add rH3,rH3,rW10 + stw rH3,12(rHP) + NEXT_BLOCK + + bdnz ppc_md5_main + + FINALIZE + blr diff --git a/arch/powerpc/crypto/md5-glue.c b/arch/powerpc/crypto/md5-glue.c new file mode 100644 index 000000000000..452fb4dc575f --- /dev/null +++ b/arch/powerpc/crypto/md5-glue.c @@ -0,0 +1,165 @@ +/* + * Glue code for MD5 implementation for PPC assembler + * + * Based on generic implementation. + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <crypto/internal/hash.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/cryptohash.h> +#include <linux/types.h> +#include <crypto/md5.h> +#include <asm/byteorder.h> + +extern void ppc_md5_transform(u32 *state, const u8 *src, u32 blocks); + +static inline void ppc_md5_clear_context(struct md5_state *sctx) +{ + int count = sizeof(struct md5_state) >> 2; + u32 *ptr = (u32 *)sctx; + + /* make sure we can clear the fast way */ + BUILD_BUG_ON(sizeof(struct md5_state) % 4); + do { *ptr++ = 0; } while (--count); +} + +static int ppc_md5_init(struct shash_desc *desc) +{ + struct md5_state *sctx = shash_desc_ctx(desc); + + sctx->hash[0] = 0x67452301; + sctx->hash[1] = 0xefcdab89; + sctx->hash[2] = 0x98badcfe; + sctx->hash[3] = 0x10325476; + sctx->byte_count = 0; + + return 0; +} + +static int ppc_md5_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct md5_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->byte_count & 0x3f; + unsigned int avail = 64 - offset; + const u8 *src = data; + + sctx->byte_count += len; + + if (avail > len) { + memcpy((char *)sctx->block + offset, src, len); + return 0; + } + + if (offset) { + memcpy((char *)sctx->block + offset, src, avail); + ppc_md5_transform(sctx->hash, (const u8 *)sctx->block, 1); + len -= avail; + src += avail; + } + + if (len > 63) { + ppc_md5_transform(sctx->hash, src, len >> 6); + src += len & ~0x3f; + len &= 0x3f; + } + + memcpy((char *)sctx->block, src, len); + return 0; +} + +static int ppc_md5_final(struct shash_desc *desc, u8 *out) +{ + struct md5_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->byte_count & 0x3f; + const u8 *src = (const u8 *)sctx->block; + u8 *p = (u8 *)src + offset; + int padlen = 55 - offset; + __le64 *pbits = (__le64 *)((char *)sctx->block + 56); + __le32 *dst = (__le32 *)out; + + *p++ = 0x80; + + if (padlen < 0) { + memset(p, 0x00, padlen + sizeof (u64)); + ppc_md5_transform(sctx->hash, src, 1); + p = (char *)sctx->block; + padlen = 56; + } + + memset(p, 0, padlen); + *pbits = cpu_to_le64(sctx->byte_count << 3); + ppc_md5_transform(sctx->hash, src, 1); + + dst[0] = cpu_to_le32(sctx->hash[0]); + dst[1] = cpu_to_le32(sctx->hash[1]); + dst[2] = cpu_to_le32(sctx->hash[2]); + dst[3] = cpu_to_le32(sctx->hash[3]); + + ppc_md5_clear_context(sctx); + return 0; +} + +static int ppc_md5_export(struct shash_desc *desc, void *out) +{ + struct md5_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int ppc_md5_import(struct shash_desc *desc, const void *in) +{ + struct md5_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg alg = { + .digestsize = MD5_DIGEST_SIZE, + .init = ppc_md5_init, + .update = ppc_md5_update, + .final = ppc_md5_final, + .export = ppc_md5_export, + .import = ppc_md5_import, + .descsize = sizeof(struct md5_state), + .statesize = sizeof(struct md5_state), + .base = { + .cra_name = "md5", + .cra_driver_name= "md5-ppc", + .cra_priority = 200, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = MD5_HMAC_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init ppc_md5_mod_init(void) +{ + return crypto_register_shash(&alg); +} + +static void __exit ppc_md5_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(ppc_md5_mod_init); +module_exit(ppc_md5_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("MD5 Secure Hash Algorithm, PPC assembler"); + +MODULE_ALIAS_CRYPTO("md5"); +MODULE_ALIAS_CRYPTO("md5-ppc"); diff --git a/arch/powerpc/crypto/sha1-spe-asm.S b/arch/powerpc/crypto/sha1-spe-asm.S new file mode 100644 index 000000000000..fcb6cf002889 --- /dev/null +++ b/arch/powerpc/crypto/sha1-spe-asm.S @@ -0,0 +1,299 @@ +/* + * Fast SHA-1 implementation for SPE instruction set (PPC) + * + * This code makes use of the SPE SIMD instruction set as defined in + * http://cache.freescale.com/files/32bit/doc/ref_manual/SPEPIM.pdf + * Implementation is based on optimization guide notes from + * http://cache.freescale.com/files/32bit/doc/app_note/AN2665.pdf + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> +#include <asm/asm-offsets.h> + +#define rHP r3 /* pointer to hash value */ +#define rWP r4 /* pointer to input */ +#define rKP r5 /* pointer to constants */ + +#define rW0 r14 /* 64 bit round words */ +#define rW1 r15 +#define rW2 r16 +#define rW3 r17 +#define rW4 r18 +#define rW5 r19 +#define rW6 r20 +#define rW7 r21 + +#define rH0 r6 /* 32 bit hash values */ +#define rH1 r7 +#define rH2 r8 +#define rH3 r9 +#define rH4 r10 + +#define rT0 r22 /* 64 bit temporary */ +#define rT1 r0 /* 32 bit temporaries */ +#define rT2 r11 +#define rT3 r12 + +#define rK r23 /* 64 bit constant in volatile register */ + +#define LOAD_K01 + +#define LOAD_K11 \ + evlwwsplat rK,0(rKP); + +#define LOAD_K21 \ + evlwwsplat rK,4(rKP); + +#define LOAD_K31 \ + evlwwsplat rK,8(rKP); + +#define LOAD_K41 \ + evlwwsplat rK,12(rKP); + +#define INITIALIZE \ + stwu r1,-128(r1); /* create stack frame */ \ + evstdw r14,8(r1); /* We must save non volatile */ \ + evstdw r15,16(r1); /* registers. Take the chance */ \ + evstdw r16,24(r1); /* and save the SPE part too */ \ + evstdw r17,32(r1); \ + evstdw r18,40(r1); \ + evstdw r19,48(r1); \ + evstdw r20,56(r1); \ + evstdw r21,64(r1); \ + evstdw r22,72(r1); \ + evstdw r23,80(r1); + + +#define FINALIZE \ + evldw r14,8(r1); /* restore SPE registers */ \ + evldw r15,16(r1); \ + evldw r16,24(r1); \ + evldw r17,32(r1); \ + evldw r18,40(r1); \ + evldw r19,48(r1); \ + evldw r20,56(r1); \ + evldw r21,64(r1); \ + evldw r22,72(r1); \ + evldw r23,80(r1); \ + xor r0,r0,r0; \ + stw r0,8(r1); /* Delete sensitive data */ \ + stw r0,16(r1); /* that we might have pushed */ \ + stw r0,24(r1); /* from other context that runs */ \ + stw r0,32(r1); /* the same code. Assume that */ \ + stw r0,40(r1); /* the lower part of the GPRs */ \ + stw r0,48(r1); /* were already overwritten on */ \ + stw r0,56(r1); /* the way down to here */ \ + stw r0,64(r1); \ + stw r0,72(r1); \ + stw r0,80(r1); \ + addi r1,r1,128; /* cleanup stack frame */ + +#ifdef __BIG_ENDIAN__ +#define LOAD_DATA(reg, off) \ + lwz reg,off(rWP); /* load data */ +#define NEXT_BLOCK \ + addi rWP,rWP,64; /* increment per block */ +#else +#define LOAD_DATA(reg, off) \ + lwbrx reg,0,rWP; /* load data */ \ + addi rWP,rWP,4; /* increment per word */ +#define NEXT_BLOCK /* nothing to do */ +#endif + +#define R_00_15(a, b, c, d, e, w0, w1, k, off) \ + LOAD_DATA(w0, off) /* 1: W */ \ + and rT2,b,c; /* 1: F' = B and C */ \ + LOAD_K##k##1 \ + andc rT1,d,b; /* 1: F" = ~B and D */ \ + rotrwi rT0,a,27; /* 1: A' = A rotl 5 */ \ + or rT2,rT2,rT1; /* 1: F = F' or F" */ \ + add e,e,rT0; /* 1: E = E + A' */ \ + rotrwi b,b,2; /* 1: B = B rotl 30 */ \ + add e,e,w0; /* 1: E = E + W */ \ + LOAD_DATA(w1, off+4) /* 2: W */ \ + add e,e,rT2; /* 1: E = E + F */ \ + and rT1,a,b; /* 2: F' = B and C */ \ + add e,e,rK; /* 1: E = E + K */ \ + andc rT2,c,a; /* 2: F" = ~B and D */ \ + add d,d,rK; /* 2: E = E + K */ \ + or rT2,rT2,rT1; /* 2: F = F' or F" */ \ + rotrwi rT0,e,27; /* 2: A' = A rotl 5 */ \ + add d,d,w1; /* 2: E = E + W */ \ + rotrwi a,a,2; /* 2: B = B rotl 30 */ \ + add d,d,rT0; /* 2: E = E + A' */ \ + evmergelo w1,w1,w0; /* mix W[0]/W[1] */ \ + add d,d,rT2 /* 2: E = E + F */ + +#define R_16_19(a, b, c, d, e, w0, w1, w4, w6, w7, k) \ + and rT2,b,c; /* 1: F' = B and C */ \ + evmergelohi rT0,w7,w6; /* W[-3] */ \ + andc rT1,d,b; /* 1: F" = ~B and D */ \ + evxor w0,w0,rT0; /* W = W[-16] xor W[-3] */ \ + or rT1,rT1,rT2; /* 1: F = F' or F" */ \ + evxor w0,w0,w4; /* W = W xor W[-8] */ \ + add e,e,rT1; /* 1: E = E + F */ \ + evxor w0,w0,w1; /* W = W xor W[-14] */ \ + rotrwi rT2,a,27; /* 1: A' = A rotl 5 */ \ + evrlwi w0,w0,1; /* W = W rotl 1 */ \ + add e,e,rT2; /* 1: E = E + A' */ \ + evaddw rT0,w0,rK; /* WK = W + K */ \ + rotrwi b,b,2; /* 1: B = B rotl 30 */ \ + LOAD_K##k##1 \ + evmergehi rT1,rT1,rT0; /* WK1/WK2 */ \ + add e,e,rT0; /* 1: E = E + WK */ \ + add d,d,rT1; /* 2: E = E + WK */ \ + and rT2,a,b; /* 2: F' = B and C */ \ + andc rT1,c,a; /* 2: F" = ~B and D */ \ + rotrwi rT0,e,27; /* 2: A' = A rotl 5 */ \ + or rT1,rT1,rT2; /* 2: F = F' or F" */ \ + add d,d,rT0; /* 2: E = E + A' */ \ + rotrwi a,a,2; /* 2: B = B rotl 30 */ \ + add d,d,rT1 /* 2: E = E + F */ + +#define R_20_39(a, b, c, d, e, w0, w1, w4, w6, w7, k) \ + evmergelohi rT0,w7,w6; /* W[-3] */ \ + xor rT2,b,c; /* 1: F' = B xor C */ \ + evxor w0,w0,rT0; /* W = W[-16] xor W[-3] */ \ + xor rT2,rT2,d; /* 1: F = F' xor D */ \ + evxor w0,w0,w4; /* W = W xor W[-8] */ \ + add e,e,rT2; /* 1: E = E + F */ \ + evxor w0,w0,w1; /* W = W xor W[-14] */ \ + rotrwi rT2,a,27; /* 1: A' = A rotl 5 */ \ + evrlwi w0,w0,1; /* W = W rotl 1 */ \ + add e,e,rT2; /* 1: E = E + A' */ \ + evaddw rT0,w0,rK; /* WK = W + K */ \ + rotrwi b,b,2; /* 1: B = B rotl 30 */ \ + LOAD_K##k##1 \ + evmergehi rT1,rT1,rT0; /* WK1/WK2 */ \ + add e,e,rT0; /* 1: E = E + WK */ \ + xor rT2,a,b; /* 2: F' = B xor C */ \ + add d,d,rT1; /* 2: E = E + WK */ \ + xor rT2,rT2,c; /* 2: F = F' xor D */ \ + rotrwi rT0,e,27; /* 2: A' = A rotl 5 */ \ + add d,d,rT2; /* 2: E = E + F */ \ + rotrwi a,a,2; /* 2: B = B rotl 30 */ \ + add d,d,rT0 /* 2: E = E + A' */ + +#define R_40_59(a, b, c, d, e, w0, w1, w4, w6, w7, k) \ + and rT2,b,c; /* 1: F' = B and C */ \ + evmergelohi rT0,w7,w6; /* W[-3] */ \ + or rT1,b,c; /* 1: F" = B or C */ \ + evxor w0,w0,rT0; /* W = W[-16] xor W[-3] */ \ + and rT1,d,rT1; /* 1: F" = F" and D */ \ + evxor w0,w0,w4; /* W = W xor W[-8] */ \ + or rT2,rT2,rT1; /* 1: F = F' or F" */ \ + evxor w0,w0,w1; /* W = W xor W[-14] */ \ + add e,e,rT2; /* 1: E = E + F */ \ + evrlwi w0,w0,1; /* W = W rotl 1 */ \ + rotrwi rT2,a,27; /* 1: A' = A rotl 5 */ \ + evaddw rT0,w0,rK; /* WK = W + K */ \ + add e,e,rT2; /* 1: E = E + A' */ \ + LOAD_K##k##1 \ + evmergehi rT1,rT1,rT0; /* WK1/WK2 */ \ + rotrwi b,b,2; /* 1: B = B rotl 30 */ \ + add e,e,rT0; /* 1: E = E + WK */ \ + and rT2,a,b; /* 2: F' = B and C */ \ + or rT0,a,b; /* 2: F" = B or C */ \ + add d,d,rT1; /* 2: E = E + WK */ \ + and rT0,c,rT0; /* 2: F" = F" and D */ \ + rotrwi a,a,2; /* 2: B = B rotl 30 */ \ + or rT2,rT2,rT0; /* 2: F = F' or F" */ \ + rotrwi rT0,e,27; /* 2: A' = A rotl 5 */ \ + add d,d,rT2; /* 2: E = E + F */ \ + add d,d,rT0 /* 2: E = E + A' */ + +#define R_60_79(a, b, c, d, e, w0, w1, w4, w6, w7, k) \ + R_20_39(a, b, c, d, e, w0, w1, w4, w6, w7, k) + +_GLOBAL(ppc_spe_sha1_transform) + INITIALIZE + + lwz rH0,0(rHP) + lwz rH1,4(rHP) + mtctr r5 + lwz rH2,8(rHP) + lis rKP,PPC_SPE_SHA1_K@h + lwz rH3,12(rHP) + ori rKP,rKP,PPC_SPE_SHA1_K@l + lwz rH4,16(rHP) + +ppc_spe_sha1_main: + R_00_15(rH0, rH1, rH2, rH3, rH4, rW1, rW0, 1, 0) + R_00_15(rH3, rH4, rH0, rH1, rH2, rW2, rW1, 0, 8) + R_00_15(rH1, rH2, rH3, rH4, rH0, rW3, rW2, 0, 16) + R_00_15(rH4, rH0, rH1, rH2, rH3, rW4, rW3, 0, 24) + R_00_15(rH2, rH3, rH4, rH0, rH1, rW5, rW4, 0, 32) + R_00_15(rH0, rH1, rH2, rH3, rH4, rW6, rW5, 0, 40) + R_00_15(rH3, rH4, rH0, rH1, rH2, rT3, rW6, 0, 48) + R_00_15(rH1, rH2, rH3, rH4, rH0, rT3, rW7, 0, 56) + + R_16_19(rH4, rH0, rH1, rH2, rH3, rW0, rW1, rW4, rW6, rW7, 0) + R_16_19(rH2, rH3, rH4, rH0, rH1, rW1, rW2, rW5, rW7, rW0, 2) + + R_20_39(rH0, rH1, rH2, rH3, rH4, rW2, rW3, rW6, rW0, rW1, 0) + R_20_39(rH3, rH4, rH0, rH1, rH2, rW3, rW4, rW7, rW1, rW2, 0) + R_20_39(rH1, rH2, rH3, rH4, rH0, rW4, rW5, rW0, rW2, rW3, 0) + R_20_39(rH4, rH0, rH1, rH2, rH3, rW5, rW6, rW1, rW3, rW4, 0) + R_20_39(rH2, rH3, rH4, rH0, rH1, rW6, rW7, rW2, rW4, rW5, 0) + R_20_39(rH0, rH1, rH2, rH3, rH4, rW7, rW0, rW3, rW5, rW6, 0) + R_20_39(rH3, rH4, rH0, rH1, rH2, rW0, rW1, rW4, rW6, rW7, 0) + R_20_39(rH1, rH2, rH3, rH4, rH0, rW1, rW2, rW5, rW7, rW0, 0) + R_20_39(rH4, rH0, rH1, rH2, rH3, rW2, rW3, rW6, rW0, rW1, 0) + R_20_39(rH2, rH3, rH4, rH0, rH1, rW3, rW4, rW7, rW1, rW2, 3) + + R_40_59(rH0, rH1, rH2, rH3, rH4, rW4, rW5, rW0, rW2, rW3, 0) + R_40_59(rH3, rH4, rH0, rH1, rH2, rW5, rW6, rW1, rW3, rW4, 0) + R_40_59(rH1, rH2, rH3, rH4, rH0, rW6, rW7, rW2, rW4, rW5, 0) + R_40_59(rH4, rH0, rH1, rH2, rH3, rW7, rW0, rW3, rW5, rW6, 0) + R_40_59(rH2, rH3, rH4, rH0, rH1, rW0, rW1, rW4, rW6, rW7, 0) + R_40_59(rH0, rH1, rH2, rH3, rH4, rW1, rW2, rW5, rW7, rW0, 0) + R_40_59(rH3, rH4, rH0, rH1, rH2, rW2, rW3, rW6, rW0, rW1, 0) + R_40_59(rH1, rH2, rH3, rH4, rH0, rW3, rW4, rW7, rW1, rW2, 0) + R_40_59(rH4, rH0, rH1, rH2, rH3, rW4, rW5, rW0, rW2, rW3, 0) + R_40_59(rH2, rH3, rH4, rH0, rH1, rW5, rW6, rW1, rW3, rW4, 4) + + R_60_79(rH0, rH1, rH2, rH3, rH4, rW6, rW7, rW2, rW4, rW5, 0) + R_60_79(rH3, rH4, rH0, rH1, rH2, rW7, rW0, rW3, rW5, rW6, 0) + R_60_79(rH1, rH2, rH3, rH4, rH0, rW0, rW1, rW4, rW6, rW7, 0) + R_60_79(rH4, rH0, rH1, rH2, rH3, rW1, rW2, rW5, rW7, rW0, 0) + R_60_79(rH2, rH3, rH4, rH0, rH1, rW2, rW3, rW6, rW0, rW1, 0) + R_60_79(rH0, rH1, rH2, rH3, rH4, rW3, rW4, rW7, rW1, rW2, 0) + R_60_79(rH3, rH4, rH0, rH1, rH2, rW4, rW5, rW0, rW2, rW3, 0) + lwz rT3,0(rHP) + R_60_79(rH1, rH2, rH3, rH4, rH0, rW5, rW6, rW1, rW3, rW4, 0) + lwz rW1,4(rHP) + R_60_79(rH4, rH0, rH1, rH2, rH3, rW6, rW7, rW2, rW4, rW5, 0) + lwz rW2,8(rHP) + R_60_79(rH2, rH3, rH4, rH0, rH1, rW7, rW0, rW3, rW5, rW6, 0) + lwz rW3,12(rHP) + NEXT_BLOCK + lwz rW4,16(rHP) + + add rH0,rH0,rT3 + stw rH0,0(rHP) + add rH1,rH1,rW1 + stw rH1,4(rHP) + add rH2,rH2,rW2 + stw rH2,8(rHP) + add rH3,rH3,rW3 + stw rH3,12(rHP) + add rH4,rH4,rW4 + stw rH4,16(rHP) + + bdnz ppc_spe_sha1_main + + FINALIZE + blr + +.data +.align 4 +PPC_SPE_SHA1_K: + .long 0x5A827999,0x6ED9EBA1,0x8F1BBCDC,0xCA62C1D6 diff --git a/arch/powerpc/crypto/sha1-spe-glue.c b/arch/powerpc/crypto/sha1-spe-glue.c new file mode 100644 index 000000000000..3e1d22212521 --- /dev/null +++ b/arch/powerpc/crypto/sha1-spe-glue.c @@ -0,0 +1,210 @@ +/* + * Glue code for SHA-1 implementation for SPE instructions (PPC) + * + * Based on generic implementation. + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <crypto/internal/hash.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/cryptohash.h> +#include <linux/types.h> +#include <crypto/sha.h> +#include <asm/byteorder.h> +#include <asm/switch_to.h> +#include <linux/hardirq.h> + +/* + * MAX_BYTES defines the number of bytes that are allowed to be processed + * between preempt_disable() and preempt_enable(). SHA1 takes ~1000 + * operations per 64 bytes. e500 cores can issue two arithmetic instructions + * per clock cycle using one 32/64 bit unit (SU1) and one 32 bit unit (SU2). + * Thus 2KB of input data will need an estimated maximum of 18,000 cycles. + * Headroom for cache misses included. Even with the low end model clocked + * at 667 MHz this equals to a critical time window of less than 27us. + * + */ +#define MAX_BYTES 2048 + +extern void ppc_spe_sha1_transform(u32 *state, const u8 *src, u32 blocks); + +static void spe_begin(void) +{ + /* We just start SPE operations and will save SPE registers later. */ + preempt_disable(); + enable_kernel_spe(); +} + +static void spe_end(void) +{ + /* reenable preemption */ + preempt_enable(); +} + +static inline void ppc_sha1_clear_context(struct sha1_state *sctx) +{ + int count = sizeof(struct sha1_state) >> 2; + u32 *ptr = (u32 *)sctx; + + /* make sure we can clear the fast way */ + BUILD_BUG_ON(sizeof(struct sha1_state) % 4); + do { *ptr++ = 0; } while (--count); +} + +static int ppc_spe_sha1_init(struct shash_desc *desc) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA1_H0; + sctx->state[1] = SHA1_H1; + sctx->state[2] = SHA1_H2; + sctx->state[3] = SHA1_H3; + sctx->state[4] = SHA1_H4; + sctx->count = 0; + + return 0; +} + +static int ppc_spe_sha1_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->count & 0x3f; + const unsigned int avail = 64 - offset; + unsigned int bytes; + const u8 *src = data; + + if (avail > len) { + sctx->count += len; + memcpy((char *)sctx->buffer + offset, src, len); + return 0; + } + + sctx->count += len; + + if (offset) { + memcpy((char *)sctx->buffer + offset, src, avail); + + spe_begin(); + ppc_spe_sha1_transform(sctx->state, (const u8 *)sctx->buffer, 1); + spe_end(); + + len -= avail; + src += avail; + } + + while (len > 63) { + bytes = (len > MAX_BYTES) ? MAX_BYTES : len; + bytes = bytes & ~0x3f; + + spe_begin(); + ppc_spe_sha1_transform(sctx->state, src, bytes >> 6); + spe_end(); + + src += bytes; + len -= bytes; + }; + + memcpy((char *)sctx->buffer, src, len); + return 0; +} + +static int ppc_spe_sha1_final(struct shash_desc *desc, u8 *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->count & 0x3f; + char *p = (char *)sctx->buffer + offset; + int padlen; + __be64 *pbits = (__be64 *)(((char *)&sctx->buffer) + 56); + __be32 *dst = (__be32 *)out; + + padlen = 55 - offset; + *p++ = 0x80; + + spe_begin(); + + if (padlen < 0) { + memset(p, 0x00, padlen + sizeof (u64)); + ppc_spe_sha1_transform(sctx->state, sctx->buffer, 1); + p = (char *)sctx->buffer; + padlen = 56; + } + + memset(p, 0, padlen); + *pbits = cpu_to_be64(sctx->count << 3); + ppc_spe_sha1_transform(sctx->state, sctx->buffer, 1); + + spe_end(); + + dst[0] = cpu_to_be32(sctx->state[0]); + dst[1] = cpu_to_be32(sctx->state[1]); + dst[2] = cpu_to_be32(sctx->state[2]); + dst[3] = cpu_to_be32(sctx->state[3]); + dst[4] = cpu_to_be32(sctx->state[4]); + + ppc_sha1_clear_context(sctx); + return 0; +} + +static int ppc_spe_sha1_export(struct shash_desc *desc, void *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int ppc_spe_sha1_import(struct shash_desc *desc, const void *in) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg alg = { + .digestsize = SHA1_DIGEST_SIZE, + .init = ppc_spe_sha1_init, + .update = ppc_spe_sha1_update, + .final = ppc_spe_sha1_final, + .export = ppc_spe_sha1_export, + .import = ppc_spe_sha1_import, + .descsize = sizeof(struct sha1_state), + .statesize = sizeof(struct sha1_state), + .base = { + .cra_name = "sha1", + .cra_driver_name= "sha1-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}; + +static int __init ppc_spe_sha1_mod_init(void) +{ + return crypto_register_shash(&alg); +} + +static void __exit ppc_spe_sha1_mod_fini(void) +{ + crypto_unregister_shash(&alg); +} + +module_init(ppc_spe_sha1_mod_init); +module_exit(ppc_spe_sha1_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA1 Secure Hash Algorithm, SPE optimized"); + +MODULE_ALIAS_CRYPTO("sha1"); +MODULE_ALIAS_CRYPTO("sha1-ppc-spe"); diff --git a/arch/powerpc/crypto/sha256-spe-asm.S b/arch/powerpc/crypto/sha256-spe-asm.S new file mode 100644 index 000000000000..2d10e4c08f03 --- /dev/null +++ b/arch/powerpc/crypto/sha256-spe-asm.S @@ -0,0 +1,323 @@ +/* + * Fast SHA-256 implementation for SPE instruction set (PPC) + * + * This code makes use of the SPE SIMD instruction set as defined in + * http://cache.freescale.com/files/32bit/doc/ref_manual/SPEPIM.pdf + * Implementation is based on optimization guide notes from + * http://cache.freescale.com/files/32bit/doc/app_note/AN2665.pdf + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <asm/ppc_asm.h> +#include <asm/asm-offsets.h> + +#define rHP r3 /* pointer to hash values in memory */ +#define rKP r24 /* pointer to round constants */ +#define rWP r4 /* pointer to input data */ + +#define rH0 r5 /* 8 32 bit hash values in 8 registers */ +#define rH1 r6 +#define rH2 r7 +#define rH3 r8 +#define rH4 r9 +#define rH5 r10 +#define rH6 r11 +#define rH7 r12 + +#define rW0 r14 /* 64 bit registers. 16 words in 8 registers */ +#define rW1 r15 +#define rW2 r16 +#define rW3 r17 +#define rW4 r18 +#define rW5 r19 +#define rW6 r20 +#define rW7 r21 + +#define rT0 r22 /* 64 bit temporaries */ +#define rT1 r23 +#define rT2 r0 /* 32 bit temporaries */ +#define rT3 r25 + +#define CMP_KN_LOOP +#define CMP_KC_LOOP \ + cmpwi rT1,0; + +#define INITIALIZE \ + stwu r1,-128(r1); /* create stack frame */ \ + evstdw r14,8(r1); /* We must save non volatile */ \ + evstdw r15,16(r1); /* registers. Take the chance */ \ + evstdw r16,24(r1); /* and save the SPE part too */ \ + evstdw r17,32(r1); \ + evstdw r18,40(r1); \ + evstdw r19,48(r1); \ + evstdw r20,56(r1); \ + evstdw r21,64(r1); \ + evstdw r22,72(r1); \ + evstdw r23,80(r1); \ + stw r24,88(r1); /* save normal registers */ \ + stw r25,92(r1); + + +#define FINALIZE \ + evldw r14,8(r1); /* restore SPE registers */ \ + evldw r15,16(r1); \ + evldw r16,24(r1); \ + evldw r17,32(r1); \ + evldw r18,40(r1); \ + evldw r19,48(r1); \ + evldw r20,56(r1); \ + evldw r21,64(r1); \ + evldw r22,72(r1); \ + evldw r23,80(r1); \ + lwz r24,88(r1); /* restore normal registers */ \ + lwz r25,92(r1); \ + xor r0,r0,r0; \ + stw r0,8(r1); /* Delete sensitive data */ \ + stw r0,16(r1); /* that we might have pushed */ \ + stw r0,24(r1); /* from other context that runs */ \ + stw r0,32(r1); /* the same code. Assume that */ \ + stw r0,40(r1); /* the lower part of the GPRs */ \ + stw r0,48(r1); /* was already overwritten on */ \ + stw r0,56(r1); /* the way down to here */ \ + stw r0,64(r1); \ + stw r0,72(r1); \ + stw r0,80(r1); \ + addi r1,r1,128; /* cleanup stack frame */ + +#ifdef __BIG_ENDIAN__ +#define LOAD_DATA(reg, off) \ + lwz reg,off(rWP); /* load data */ +#define NEXT_BLOCK \ + addi rWP,rWP,64; /* increment per block */ +#else +#define LOAD_DATA(reg, off) \ + lwbrx reg,0,rWP; /* load data */ \ + addi rWP,rWP,4; /* increment per word */ +#define NEXT_BLOCK /* nothing to do */ +#endif + +#define R_LOAD_W(a, b, c, d, e, f, g, h, w, off) \ + LOAD_DATA(w, off) /* 1: W */ \ + rotrwi rT0,e,6; /* 1: S1 = e rotr 6 */ \ + rotrwi rT1,e,11; /* 1: S1' = e rotr 11 */ \ + rotrwi rT2,e,25; /* 1: S1" = e rotr 25 */ \ + xor rT0,rT0,rT1; /* 1: S1 = S1 xor S1' */ \ + and rT3,e,f; /* 1: ch = e and f */ \ + xor rT0,rT0,rT2; /* 1: S1 = S1 xor S1" */ \ + andc rT1,g,e; /* 1: ch' = ~e and g */ \ + lwz rT2,off(rKP); /* 1: K */ \ + xor rT3,rT3,rT1; /* 1: ch = ch xor ch' */ \ + add h,h,rT0; /* 1: temp1 = h + S1 */ \ + add rT3,rT3,w; /* 1: temp1' = ch + w */ \ + rotrwi rT0,a,2; /* 1: S0 = a rotr 2 */ \ + add h,h,rT3; /* 1: temp1 = temp1 + temp1' */ \ + rotrwi rT1,a,13; /* 1: S0' = a rotr 13 */ \ + add h,h,rT2; /* 1: temp1 = temp1 + K */ \ + rotrwi rT3,a,22; /* 1: S0" = a rotr 22 */ \ + xor rT0,rT0,rT1; /* 1: S0 = S0 xor S0' */ \ + add d,d,h; /* 1: d = d + temp1 */ \ + xor rT3,rT0,rT3; /* 1: S0 = S0 xor S0" */ \ + evmergelo w,w,w; /* shift W */ \ + or rT2,a,b; /* 1: maj = a or b */ \ + and rT1,a,b; /* 1: maj' = a and b */ \ + and rT2,rT2,c; /* 1: maj = maj and c */ \ + LOAD_DATA(w, off+4) /* 2: W */ \ + or rT2,rT1,rT2; /* 1: maj = maj or maj' */ \ + rotrwi rT0,d,6; /* 2: S1 = e rotr 6 */ \ + add rT3,rT3,rT2; /* 1: temp2 = S0 + maj */ \ + rotrwi rT1,d,11; /* 2: S1' = e rotr 11 */ \ + add h,h,rT3; /* 1: h = temp1 + temp2 */ \ + rotrwi rT2,d,25; /* 2: S1" = e rotr 25 */ \ + xor rT0,rT0,rT1; /* 2: S1 = S1 xor S1' */ \ + and rT3,d,e; /* 2: ch = e and f */ \ + xor rT0,rT0,rT2; /* 2: S1 = S1 xor S1" */ \ + andc rT1,f,d; /* 2: ch' = ~e and g */ \ + lwz rT2,off+4(rKP); /* 2: K */ \ + xor rT3,rT3,rT1; /* 2: ch = ch xor ch' */ \ + add g,g,rT0; /* 2: temp1 = h + S1 */ \ + add rT3,rT3,w; /* 2: temp1' = ch + w */ \ + rotrwi rT0,h,2; /* 2: S0 = a rotr 2 */ \ + add g,g,rT3; /* 2: temp1 = temp1 + temp1' */ \ + rotrwi rT1,h,13; /* 2: S0' = a rotr 13 */ \ + add g,g,rT2; /* 2: temp1 = temp1 + K */ \ + rotrwi rT3,h,22; /* 2: S0" = a rotr 22 */ \ + xor rT0,rT0,rT1; /* 2: S0 = S0 xor S0' */ \ + or rT2,h,a; /* 2: maj = a or b */ \ + xor rT3,rT0,rT3; /* 2: S0 = S0 xor S0" */ \ + and rT1,h,a; /* 2: maj' = a and b */ \ + and rT2,rT2,b; /* 2: maj = maj and c */ \ + add c,c,g; /* 2: d = d + temp1 */ \ + or rT2,rT1,rT2; /* 2: maj = maj or maj' */ \ + add rT3,rT3,rT2; /* 2: temp2 = S0 + maj */ \ + add g,g,rT3 /* 2: h = temp1 + temp2 */ + +#define R_CALC_W(a, b, c, d, e, f, g, h, w0, w1, w4, w5, w7, k, off) \ + rotrwi rT2,e,6; /* 1: S1 = e rotr 6 */ \ + evmergelohi rT0,w0,w1; /* w[-15] */ \ + rotrwi rT3,e,11; /* 1: S1' = e rotr 11 */ \ + evsrwiu rT1,rT0,3; /* s0 = w[-15] >> 3 */ \ + xor rT2,rT2,rT3; /* 1: S1 = S1 xor S1' */ \ + evrlwi rT0,rT0,25; /* s0' = w[-15] rotr 7 */ \ + rotrwi rT3,e,25; /* 1: S1' = e rotr 25 */ \ + evxor rT1,rT1,rT0; /* s0 = s0 xor s0' */ \ + xor rT2,rT2,rT3; /* 1: S1 = S1 xor S1' */ \ + evrlwi rT0,rT0,21; /* s0' = w[-15] rotr 18 */ \ + add h,h,rT2; /* 1: temp1 = h + S1 */ \ + evxor rT0,rT0,rT1; /* s0 = s0 xor s0' */ \ + and rT2,e,f; /* 1: ch = e and f */ \ + evaddw w0,w0,rT0; /* w = w[-16] + s0 */ \ + andc rT3,g,e; /* 1: ch' = ~e and g */ \ + evsrwiu rT0,w7,10; /* s1 = w[-2] >> 10 */ \ + xor rT2,rT2,rT3; /* 1: ch = ch xor ch' */ \ + evrlwi rT1,w7,15; /* s1' = w[-2] rotr 17 */ \ + add h,h,rT2; /* 1: temp1 = temp1 + ch */ \ + evxor rT0,rT0,rT1; /* s1 = s1 xor s1' */ \ + rotrwi rT2,a,2; /* 1: S0 = a rotr 2 */ \ + evrlwi rT1,w7,13; /* s1' = w[-2] rotr 19 */ \ + rotrwi rT3,a,13; /* 1: S0' = a rotr 13 */ \ + evxor rT0,rT0,rT1; /* s1 = s1 xor s1' */ \ + xor rT2,rT2,rT3; /* 1: S0 = S0 xor S0' */ \ + evldw rT1,off(rKP); /* k */ \ + rotrwi rT3,a,22; /* 1: S0' = a rotr 22 */ \ + evaddw w0,w0,rT0; /* w = w + s1 */ \ + xor rT2,rT2,rT3; /* 1: S0 = S0 xor S0' */ \ + evmergelohi rT0,w4,w5; /* w[-7] */ \ + and rT3,a,b; /* 1: maj = a and b */ \ + evaddw w0,w0,rT0; /* w = w + w[-7] */ \ + CMP_K##k##_LOOP \ + add rT2,rT2,rT3; /* 1: temp2 = S0 + maj */ \ + evaddw rT1,rT1,w0; /* wk = w + k */ \ + xor rT3,a,b; /* 1: maj = a xor b */ \ + evmergehi rT0,rT1,rT1; /* wk1/wk2 */ \ + and rT3,rT3,c; /* 1: maj = maj and c */ \ + add h,h,rT0; /* 1: temp1 = temp1 + wk */ \ + add rT2,rT2,rT3; /* 1: temp2 = temp2 + maj */ \ + add g,g,rT1; /* 2: temp1 = temp1 + wk */ \ + add d,d,h; /* 1: d = d + temp1 */ \ + rotrwi rT0,d,6; /* 2: S1 = e rotr 6 */ \ + add h,h,rT2; /* 1: h = temp1 + temp2 */ \ + rotrwi rT1,d,11; /* 2: S1' = e rotr 11 */ \ + rotrwi rT2,d,25; /* 2: S" = e rotr 25 */ \ + xor rT0,rT0,rT1; /* 2: S1 = S1 xor S1' */ \ + and rT3,d,e; /* 2: ch = e and f */ \ + xor rT0,rT0,rT2; /* 2: S1 = S1 xor S1" */ \ + andc rT1,f,d; /* 2: ch' = ~e and g */ \ + add g,g,rT0; /* 2: temp1 = h + S1 */ \ + xor rT3,rT3,rT1; /* 2: ch = ch xor ch' */ \ + rotrwi rT0,h,2; /* 2: S0 = a rotr 2 */ \ + add g,g,rT3; /* 2: temp1 = temp1 + ch */ \ + rotrwi rT1,h,13; /* 2: S0' = a rotr 13 */ \ + rotrwi rT3,h,22; /* 2: S0" = a rotr 22 */ \ + xor rT0,rT0,rT1; /* 2: S0 = S0 xor S0' */ \ + or rT2,h,a; /* 2: maj = a or b */ \ + and rT1,h,a; /* 2: maj' = a and b */ \ + and rT2,rT2,b; /* 2: maj = maj and c */ \ + xor rT3,rT0,rT3; /* 2: S0 = S0 xor S0" */ \ + or rT2,rT1,rT2; /* 2: maj = maj or maj' */ \ + add c,c,g; /* 2: d = d + temp1 */ \ + add rT3,rT3,rT2; /* 2: temp2 = S0 + maj */ \ + add g,g,rT3 /* 2: h = temp1 + temp2 */ + +_GLOBAL(ppc_spe_sha256_transform) + INITIALIZE + + mtctr r5 + lwz rH0,0(rHP) + lwz rH1,4(rHP) + lwz rH2,8(rHP) + lwz rH3,12(rHP) + lwz rH4,16(rHP) + lwz rH5,20(rHP) + lwz rH6,24(rHP) + lwz rH7,28(rHP) + +ppc_spe_sha256_main: + lis rKP,PPC_SPE_SHA256_K@ha + addi rKP,rKP,PPC_SPE_SHA256_K@l + + R_LOAD_W(rH0, rH1, rH2, rH3, rH4, rH5, rH6, rH7, rW0, 0) + R_LOAD_W(rH6, rH7, rH0, rH1, rH2, rH3, rH4, rH5, rW1, 8) + R_LOAD_W(rH4, rH5, rH6, rH7, rH0, rH1, rH2, rH3, rW2, 16) + R_LOAD_W(rH2, rH3, rH4, rH5, rH6, rH7, rH0, rH1, rW3, 24) + R_LOAD_W(rH0, rH1, rH2, rH3, rH4, rH5, rH6, rH7, rW4, 32) + R_LOAD_W(rH6, rH7, rH0, rH1, rH2, rH3, rH4, rH5, rW5, 40) + R_LOAD_W(rH4, rH5, rH6, rH7, rH0, rH1, rH2, rH3, rW6, 48) + R_LOAD_W(rH2, rH3, rH4, rH5, rH6, rH7, rH0, rH1, rW7, 56) +ppc_spe_sha256_16_rounds: + addi rKP,rKP,64 + R_CALC_W(rH0, rH1, rH2, rH3, rH4, rH5, rH6, rH7, + rW0, rW1, rW4, rW5, rW7, N, 0) + R_CALC_W(rH6, rH7, rH0, rH1, rH2, rH3, rH4, rH5, + rW1, rW2, rW5, rW6, rW0, N, 8) + R_CALC_W(rH4, rH5, rH6, rH7, rH0, rH1, rH2, rH3, + rW2, rW3, rW6, rW7, rW1, N, 16) + R_CALC_W(rH2, rH3, rH4, rH5, rH6, rH7, rH0, rH1, + rW3, rW4, rW7, rW0, rW2, N, 24) + R_CALC_W(rH0, rH1, rH2, rH3, rH4, rH5, rH6, rH7, + rW4, rW5, rW0, rW1, rW3, N, 32) + R_CALC_W(rH6, rH7, rH0, rH1, rH2, rH3, rH4, rH5, + rW5, rW6, rW1, rW2, rW4, N, 40) + R_CALC_W(rH4, rH5, rH6, rH7, rH0, rH1, rH2, rH3, + rW6, rW7, rW2, rW3, rW5, N, 48) + R_CALC_W(rH2, rH3, rH4, rH5, rH6, rH7, rH0, rH1, + rW7, rW0, rW3, rW4, rW6, C, 56) + bt gt,ppc_spe_sha256_16_rounds + + lwz rW0,0(rHP) + NEXT_BLOCK + lwz rW1,4(rHP) + lwz rW2,8(rHP) + lwz rW3,12(rHP) + lwz rW4,16(rHP) + lwz rW5,20(rHP) + lwz rW6,24(rHP) + lwz rW7,28(rHP) + + add rH0,rH0,rW0 + stw rH0,0(rHP) + add rH1,rH1,rW1 + stw rH1,4(rHP) + add rH2,rH2,rW2 + stw rH2,8(rHP) + add rH3,rH3,rW3 + stw rH3,12(rHP) + add rH4,rH4,rW4 + stw rH4,16(rHP) + add rH5,rH5,rW5 + stw rH5,20(rHP) + add rH6,rH6,rW6 + stw rH6,24(rHP) + add rH7,rH7,rW7 + stw rH7,28(rHP) + + bdnz ppc_spe_sha256_main + + FINALIZE + blr + +.data +.align 5 +PPC_SPE_SHA256_K: + .long 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5 + .long 0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5 + .long 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3 + .long 0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174 + .long 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc + .long 0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da + .long 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7 + .long 0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967 + .long 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13 + .long 0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85 + .long 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3 + .long 0xd192e819,0xd6990624,0xf40e3585,0x106aa070 + .long 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5 + .long 0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3 + .long 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208 + .long 0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 diff --git a/arch/powerpc/crypto/sha256-spe-glue.c b/arch/powerpc/crypto/sha256-spe-glue.c new file mode 100644 index 000000000000..f4a616fe1a82 --- /dev/null +++ b/arch/powerpc/crypto/sha256-spe-glue.c @@ -0,0 +1,275 @@ +/* + * Glue code for SHA-256 implementation for SPE instructions (PPC) + * + * Based on generic implementation. The assembler module takes care + * about the SPE registers so it can run from interrupt context. + * + * Copyright (c) 2015 Markus Stockhausen <stockhausen@collogia.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 <crypto/internal/hash.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/cryptohash.h> +#include <linux/types.h> +#include <crypto/sha.h> +#include <asm/byteorder.h> +#include <asm/switch_to.h> +#include <linux/hardirq.h> + +/* + * MAX_BYTES defines the number of bytes that are allowed to be processed + * between preempt_disable() and preempt_enable(). SHA256 takes ~2,000 + * operations per 64 bytes. e500 cores can issue two arithmetic instructions + * per clock cycle using one 32/64 bit unit (SU1) and one 32 bit unit (SU2). + * Thus 1KB of input data will need an estimated maximum of 18,000 cycles. + * Headroom for cache misses included. Even with the low end model clocked + * at 667 MHz this equals to a critical time window of less than 27us. + * + */ +#define MAX_BYTES 1024 + +extern void ppc_spe_sha256_transform(u32 *state, const u8 *src, u32 blocks); + +static void spe_begin(void) +{ + /* We just start SPE operations and will save SPE registers later. */ + preempt_disable(); + enable_kernel_spe(); +} + +static void spe_end(void) +{ + /* reenable preemption */ + preempt_enable(); +} + +static inline void ppc_sha256_clear_context(struct sha256_state *sctx) +{ + int count = sizeof(struct sha256_state) >> 2; + u32 *ptr = (u32 *)sctx; + + /* make sure we can clear the fast way */ + BUILD_BUG_ON(sizeof(struct sha256_state) % 4); + do { *ptr++ = 0; } while (--count); +} + +static int ppc_spe_sha256_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +static int ppc_spe_sha224_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA224_H0; + sctx->state[1] = SHA224_H1; + sctx->state[2] = SHA224_H2; + sctx->state[3] = SHA224_H3; + sctx->state[4] = SHA224_H4; + sctx->state[5] = SHA224_H5; + sctx->state[6] = SHA224_H6; + sctx->state[7] = SHA224_H7; + sctx->count = 0; + + return 0; +} + +static int ppc_spe_sha256_update(struct shash_desc *desc, const u8 *data, + unsigned int len) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->count & 0x3f; + const unsigned int avail = 64 - offset; + unsigned int bytes; + const u8 *src = data; + + if (avail > len) { + sctx->count += len; + memcpy((char *)sctx->buf + offset, src, len); + return 0; + } + + sctx->count += len; + + if (offset) { + memcpy((char *)sctx->buf + offset, src, avail); + + spe_begin(); + ppc_spe_sha256_transform(sctx->state, (const u8 *)sctx->buf, 1); + spe_end(); + + len -= avail; + src += avail; + } + + while (len > 63) { + /* cut input data into smaller blocks */ + bytes = (len > MAX_BYTES) ? MAX_BYTES : len; + bytes = bytes & ~0x3f; + + spe_begin(); + ppc_spe_sha256_transform(sctx->state, src, bytes >> 6); + spe_end(); + + src += bytes; + len -= bytes; + }; + + memcpy((char *)sctx->buf, src, len); + return 0; +} + +static int ppc_spe_sha256_final(struct shash_desc *desc, u8 *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + const unsigned int offset = sctx->count & 0x3f; + char *p = (char *)sctx->buf + offset; + int padlen; + __be64 *pbits = (__be64 *)(((char *)&sctx->buf) + 56); + __be32 *dst = (__be32 *)out; + + padlen = 55 - offset; + *p++ = 0x80; + + spe_begin(); + + if (padlen < 0) { + memset(p, 0x00, padlen + sizeof (u64)); + ppc_spe_sha256_transform(sctx->state, sctx->buf, 1); + p = (char *)sctx->buf; + padlen = 56; + } + + memset(p, 0, padlen); + *pbits = cpu_to_be64(sctx->count << 3); + ppc_spe_sha256_transform(sctx->state, sctx->buf, 1); + + spe_end(); + + dst[0] = cpu_to_be32(sctx->state[0]); + dst[1] = cpu_to_be32(sctx->state[1]); + dst[2] = cpu_to_be32(sctx->state[2]); + dst[3] = cpu_to_be32(sctx->state[3]); + dst[4] = cpu_to_be32(sctx->state[4]); + dst[5] = cpu_to_be32(sctx->state[5]); + dst[6] = cpu_to_be32(sctx->state[6]); + dst[7] = cpu_to_be32(sctx->state[7]); + + ppc_sha256_clear_context(sctx); + return 0; +} + +static int ppc_spe_sha224_final(struct shash_desc *desc, u8 *out) +{ + u32 D[SHA256_DIGEST_SIZE >> 2]; + __be32 *dst = (__be32 *)out; + + ppc_spe_sha256_final(desc, (u8 *)D); + + /* avoid bytewise memcpy */ + dst[0] = D[0]; + dst[1] = D[1]; + dst[2] = D[2]; + dst[3] = D[3]; + dst[4] = D[4]; + dst[5] = D[5]; + dst[6] = D[6]; + + /* clear sensitive data */ + memzero_explicit(D, SHA256_DIGEST_SIZE); + return 0; +} + +static int ppc_spe_sha256_export(struct shash_desc *desc, void *out) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + memcpy(out, sctx, sizeof(*sctx)); + return 0; +} + +static int ppc_spe_sha256_import(struct shash_desc *desc, const void *in) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + memcpy(sctx, in, sizeof(*sctx)); + return 0; +} + +static struct shash_alg algs[2] = { { + .digestsize = SHA256_DIGEST_SIZE, + .init = ppc_spe_sha256_init, + .update = ppc_spe_sha256_update, + .final = ppc_spe_sha256_final, + .export = ppc_spe_sha256_export, + .import = ppc_spe_sha256_import, + .descsize = sizeof(struct sha256_state), + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha256", + .cra_driver_name= "sha256-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +}, { + .digestsize = SHA224_DIGEST_SIZE, + .init = ppc_spe_sha224_init, + .update = ppc_spe_sha256_update, + .final = ppc_spe_sha224_final, + .export = ppc_spe_sha256_export, + .import = ppc_spe_sha256_import, + .descsize = sizeof(struct sha256_state), + .statesize = sizeof(struct sha256_state), + .base = { + .cra_name = "sha224", + .cra_driver_name= "sha224-ppc-spe", + .cra_priority = 300, + .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_module = THIS_MODULE, + } +} }; + +static int __init ppc_spe_sha256_mod_init(void) +{ + return crypto_register_shashes(algs, ARRAY_SIZE(algs)); +} + +static void __exit ppc_spe_sha256_mod_fini(void) +{ + crypto_unregister_shashes(algs, ARRAY_SIZE(algs)); +} + +module_init(ppc_spe_sha256_mod_init); +module_exit(ppc_spe_sha256_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SHA-224 and SHA-256 Secure Hash Algorithm, SPE optimized"); + +MODULE_ALIAS_CRYPTO("sha224"); +MODULE_ALIAS_CRYPTO("sha224-ppc-spe"); +MODULE_ALIAS_CRYPTO("sha256"); +MODULE_ALIAS_CRYPTO("sha256-ppc-spe"); diff --git a/arch/powerpc/include/asm/thread_info.h b/arch/powerpc/include/asm/thread_info.h index 72489799cf02..7efee4a3240b 100644 --- a/arch/powerpc/include/asm/thread_info.h +++ b/arch/powerpc/include/asm/thread_info.h @@ -39,7 +39,6 @@ */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ int cpu; /* cpu we're on */ int preempt_count; /* 0 => preemptable, <0 => BUG */ @@ -55,7 +54,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ .flags = 0, \ diff --git a/arch/s390/hypfs/inode.c b/arch/s390/hypfs/inode.c index df7d8cbee377..3f5c799b7fb5 100644 --- a/arch/s390/hypfs/inode.c +++ b/arch/s390/hypfs/inode.c @@ -437,8 +437,6 @@ struct dentry *hypfs_create_str(struct dentry *dir, static const struct file_operations hypfs_file_ops = { .open = hypfs_open, .release = hypfs_release, - .read = new_sync_read, - .write = new_sync_write, .read_iter = hypfs_read_iter, .write_iter = hypfs_write_iter, .llseek = no_llseek, diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index d532098d98bf..4c27ec764c36 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h @@ -29,7 +29,6 @@ */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long sys_call_table; /* System call table address */ unsigned int cpu; /* current CPU */ @@ -46,7 +45,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index f35058da8eaf..c7d1b9d09011 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c @@ -34,7 +34,6 @@ int main(void) DEFINE(__THREAD_per_paid, offsetof(struct task_struct, thread.per_event.paid)); BLANK(); DEFINE(__TI_task, offsetof(struct thread_info, task)); - DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain)); DEFINE(__TI_flags, offsetof(struct thread_info, flags)); DEFINE(__TI_sysc_table, offsetof(struct thread_info, sys_call_table)); DEFINE(__TI_cpu, offsetof(struct thread_info, cpu)); diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index bc1df12dd4f8..fe8d6924efaa 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c @@ -370,16 +370,6 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) return (void __user *)((sp - frame_size) & -8ul); } -static inline int map_signal(int sig) -{ - if (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32) - return current_thread_info()->exec_domain->signal_invmap[sig]; - else - return sig; -} - static int setup_frame32(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs) { @@ -449,7 +439,7 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, (regs->psw.mask & ~PSW_MASK_ASC); regs->psw.addr = (__force __u64) ksig->ka.sa.sa_handler; - regs->gprs[2] = map_signal(sig); + regs->gprs[2] = sig; regs->gprs[3] = (__force __u64) &frame->sc; /* We forgot to include these in the sigcontext. @@ -532,7 +522,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, (regs->psw.mask & ~PSW_MASK_ASC); regs->psw.addr = (__u64 __force) ksig->ka.sa.sa_handler; - regs->gprs[2] = map_signal(ksig->sig); + regs->gprs[2] = ksig->sig; regs->gprs[3] = (__force __u64) &frame->info; regs->gprs[4] = (__force __u64) &frame->uc; regs->gprs[5] = task_thread_info(current)->last_break; diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 7fec60cb0b75..c551f22ce066 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -301,16 +301,6 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) return (void __user *)((sp - frame_size) & -8ul); } -static inline int map_signal(int sig) -{ - if (current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32) - return current_thread_info()->exec_domain->signal_invmap[sig]; - else - return sig; -} - static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { @@ -378,7 +368,7 @@ static int setup_frame(int sig, struct k_sigaction *ka, (regs->psw.mask & ~PSW_MASK_ASC); regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; - regs->gprs[2] = map_signal(sig); + regs->gprs[2] = sig; regs->gprs[3] = (unsigned long) &frame->sc; /* We forgot to include these in the sigcontext. @@ -458,7 +448,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, (regs->psw.mask & ~PSW_MASK_ASC); regs->psw.addr = (unsigned long) ksig->ka.sa.sa_handler | PSW_ADDR_AMODE; - regs->gprs[2] = map_signal(ksig->sig); + regs->gprs[2] = ksig->sig; regs->gprs[3] = (unsigned long) &frame->info; regs->gprs[4] = (unsigned long) &frame->uc; regs->gprs[5] = task_thread_info(current)->last_break; diff --git a/arch/score/include/asm/thread_info.h b/arch/score/include/asm/thread_info.h index 33864fa2a8d4..7d9ffb15c477 100644 --- a/arch/score/include/asm/thread_info.h +++ b/arch/score/include/asm/thread_info.h @@ -28,7 +28,6 @@ */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long tp_value; /* thread pointer */ __u32 cpu; /* current CPU */ @@ -53,7 +52,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .cpu = 0, \ .preempt_count = 1, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/score/kernel/asm-offsets.c b/arch/score/kernel/asm-offsets.c index b4d5214a7a7e..52794f9421e2 100644 --- a/arch/score/kernel/asm-offsets.c +++ b/arch/score/kernel/asm-offsets.c @@ -100,7 +100,6 @@ void output_thread_info_defines(void) { COMMENT("SCORE thread_info offsets."); OFFSET(TI_TASK, thread_info, task); - OFFSET(TI_EXEC_DOMAIN, thread_info, exec_domain); OFFSET(TI_FLAGS, thread_info, flags); OFFSET(TI_TP_VALUE, thread_info, tp_value); OFFSET(TI_CPU, thread_info, cpu); diff --git a/arch/sh/include/asm/thread_info.h b/arch/sh/include/asm/thread_info.h index 657c03919627..2afa321157be 100644 --- a/arch/sh/include/asm/thread_info.h +++ b/arch/sh/include/asm/thread_info.h @@ -27,7 +27,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 status; /* thread synchronous flags */ __u32 cpu; @@ -56,7 +55,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .status = 0, \ .cpu = 0, \ diff --git a/arch/sh/kernel/asm-offsets.c b/arch/sh/kernel/asm-offsets.c index 542225fedb11..4bd44da910f3 100644 --- a/arch/sh/kernel/asm-offsets.c +++ b/arch/sh/kernel/asm-offsets.c @@ -21,7 +21,6 @@ int main(void) { /* offsets into the thread_info struct */ DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); DEFINE(TI_PRE_COUNT, offsetof(struct thread_info, preempt_count)); diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 65a1ecd77f96..eb10ff84015c 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -124,7 +124,6 @@ void irq_ctx_init(int cpu) irqctx = (union irq_ctx *)&hardirq_stack[cpu * THREAD_SIZE]; irqctx->tinfo.task = NULL; - irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.cpu = cpu; irqctx->tinfo.preempt_count = HARDIRQ_OFFSET; irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); @@ -133,7 +132,6 @@ void irq_ctx_init(int cpu) irqctx = (union irq_ctx *)&softirq_stack[cpu * THREAD_SIZE]; irqctx->tinfo.task = NULL; - irqctx->tinfo.exec_domain = NULL; irqctx->tinfo.cpu = cpu; irqctx->tinfo.preempt_count = 0; irqctx->tinfo.addr_limit = MAKE_MM_SEG(0); diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c index 97292890b51b..f7c3d5c25caf 100644 --- a/arch/sh/kernel/signal_32.c +++ b/arch/sh/kernel/signal_32.c @@ -267,19 +267,12 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, { struct sigframe __user *frame; int err = 0, sig = ksig->sig; - int signal; frame = get_sigframe(&ksig->ka, regs->regs[15], sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); if (_NSIG_WORDS > 1) @@ -313,7 +306,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, /* Set up registers for signal handler */ regs->regs[15] = (unsigned long) frame; - regs->regs[4] = signal; /* Arg for signal handler */ + regs->regs[4] = sig; /* Arg for signal handler */ regs->regs[5] = 0; regs->regs[6] = (unsigned long) &frame->sc; @@ -340,19 +333,12 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, { struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - int signal; frame = get_sigframe(&ksig->ka, regs->regs[15], sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - err |= copy_siginfo_to_user(&frame->info, &ksig->info); /* Create the ucontext. */ @@ -390,7 +376,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, /* Set up registers for signal handler */ regs->regs[15] = (unsigned long) frame; - regs->regs[4] = signal; /* Arg for signal handler */ + regs->regs[4] = sig; /* Arg for signal handler */ regs->regs[5] = (unsigned long) &frame->info; regs->regs[6] = (unsigned long) &frame->uc; diff --git a/arch/sh/kernel/signal_64.c b/arch/sh/kernel/signal_64.c index 0462995d4d7f..d8a3f0d22809 100644 --- a/arch/sh/kernel/signal_64.c +++ b/arch/sh/kernel/signal_64.c @@ -385,12 +385,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); /* Give up earlier as i386, in case */ @@ -441,7 +435,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs * All edited pointers are subject to NEFF. */ regs->regs[REG_SP] = neff_sign_extend((unsigned long)frame); - regs->regs[REG_ARG1] = signal; /* Arg for signal handler */ + regs->regs[REG_ARG1] = sig; /* Arg for signal handler */ /* FIXME: The glibc profiling support for SH-5 needs to be passed a sigcontext @@ -459,7 +453,7 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs /* Broken %016Lx */ pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n", - signal, current->comm, current->pid, frame, + sig, current->comm, current->pid, frame, regs->pc >> 32, regs->pc & 0xffffffff, DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff); @@ -471,19 +465,12 @@ static int setup_rt_frame(struct ksignal *kig, sigset_t *set, { struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - int signal; frame = get_sigframe(&ksig->ka, regs->regs[REG_SP], sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) return -EFAULT; - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - err |= __put_user(&frame->info, &frame->pinfo); err |= __put_user(&frame->uc, &frame->puc); err |= copy_siginfo_to_user(&frame->info, &ksig->info); @@ -540,13 +527,13 @@ static int setup_rt_frame(struct ksignal *kig, sigset_t *set, * All edited pointers are subject to NEFF. */ regs->regs[REG_SP] = neff_sign_extend((unsigned long)frame); - regs->regs[REG_ARG1] = signal; /* Arg for signal handler */ + regs->regs[REG_ARG1] = sig; /* Arg for signal handler */ regs->regs[REG_ARG2] = (unsigned long long)(unsigned long)(signed long)&frame->info; regs->regs[REG_ARG3] = (unsigned long long)(unsigned long)(signed long)&frame->uc.uc_mcontext; regs->pc = neff_sign_extend((unsigned long)ksig->ka.sa.sa_handler); pr_debug("SIG deliver (#%d,%s:%d): sp=%p pc=%08Lx%08Lx link=%08Lx%08Lx\n", - signal, current->comm, current->pid, frame, + sig, current->comm, current->pid, frame, regs->pc >> 32, regs->pc & 0xffffffff, DEREF_REG_PR >> 32, DEREF_REG_PR & 0xffffffff); diff --git a/arch/sparc/include/asm/thread_info_32.h b/arch/sparc/include/asm/thread_info_32.h index fd7bd0a440ca..229475f0d7ce 100644 --- a/arch/sparc/include/asm/thread_info_32.h +++ b/arch/sparc/include/asm/thread_info_32.h @@ -27,7 +27,6 @@ struct thread_info { unsigned long uwinmask; struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ int cpu; /* cpu we're on */ int preempt_count; /* 0 => preemptable, @@ -35,6 +34,8 @@ struct thread_info { int softirq_count; int hardirq_count; + u32 __unused; + /* Context switch saved kernel state. */ unsigned long ksp; /* ... ksp __attribute__ ((aligned (8))); */ unsigned long kpc; @@ -56,7 +57,6 @@ struct thread_info { { \ .uwinmask = 0, \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ @@ -85,12 +85,11 @@ register struct thread_info *current_thread_info_reg asm("g6"); */ #define TI_UWINMASK 0x00 /* uwinmask */ #define TI_TASK 0x04 -#define TI_EXECDOMAIN 0x08 /* exec_domain */ -#define TI_FLAGS 0x0c -#define TI_CPU 0x10 -#define TI_PREEMPT 0x14 /* preempt_count */ -#define TI_SOFTIRQ 0x18 /* softirq_count */ -#define TI_HARDIRQ 0x1c /* hardirq_count */ +#define TI_FLAGS 0x08 +#define TI_CPU 0x0c +#define TI_PREEMPT 0x10 /* preempt_count */ +#define TI_SOFTIRQ 0x14 /* softirq_count */ +#define TI_HARDIRQ 0x18 /* hardirq_count */ #define TI_KSP 0x20 /* ksp */ #define TI_KPC 0x24 /* kpc (ldd'ed with kpc) */ #define TI_KPSR 0x28 /* kpsr */ diff --git a/arch/sparc/include/asm/thread_info_64.h b/arch/sparc/include/asm/thread_info_64.h index ff455164732a..bde59825d06c 100644 --- a/arch/sparc/include/asm/thread_info_64.h +++ b/arch/sparc/include/asm/thread_info_64.h @@ -31,7 +31,6 @@ #include <asm/types.h> struct task_struct; -struct exec_domain; struct thread_info { /* D$ line 1 */ @@ -44,7 +43,6 @@ struct thread_info { /* D$ line 2 */ unsigned long fault_address; struct pt_regs *kregs; - struct exec_domain *exec_domain; int preempt_count; /* 0 => preemptable, <0 => BUG */ __u8 new_child; __u8 current_ds; @@ -80,18 +78,17 @@ struct thread_info { #define TI_KSP 0x00000018 #define TI_FAULT_ADDR 0x00000020 #define TI_KREGS 0x00000028 -#define TI_EXEC_DOMAIN 0x00000030 -#define TI_PRE_COUNT 0x00000038 -#define TI_NEW_CHILD 0x0000003c -#define TI_CURRENT_DS 0x0000003d -#define TI_CPU 0x0000003e -#define TI_UTRAPS 0x00000040 -#define TI_REG_WINDOW 0x00000048 -#define TI_RWIN_SPTRS 0x000003c8 -#define TI_GSR 0x00000400 -#define TI_XFSR 0x00000438 -#define TI_KUNA_REGS 0x00000470 -#define TI_KUNA_INSN 0x00000478 +#define TI_PRE_COUNT 0x00000030 +#define TI_NEW_CHILD 0x00000034 +#define TI_CURRENT_DS 0x00000035 +#define TI_CPU 0x00000036 +#define TI_UTRAPS 0x00000038 +#define TI_REG_WINDOW 0x00000040 +#define TI_RWIN_SPTRS 0x000003c0 +#define TI_GSR 0x000003f8 +#define TI_XFSR 0x00000430 +#define TI_KUNA_REGS 0x00000468 +#define TI_KUNA_INSN 0x00000470 #define TI_FPREGS 0x00000480 /* We embed this in the uppermost byte of thread_info->flags */ @@ -119,7 +116,6 @@ struct thread_info { { \ .task = &tsk, \ .current_ds = ASI_P, \ - .exec_domain = &default_exec_domain, \ .preempt_count = INIT_PREEMPT_COUNT, \ } diff --git a/arch/sparc/kernel/traps_32.c b/arch/sparc/kernel/traps_32.c index 6fd386c5232a..4f21df7d4f13 100644 --- a/arch/sparc/kernel/traps_32.c +++ b/arch/sparc/kernel/traps_32.c @@ -433,7 +433,6 @@ void trap_init(void) /* Force linker to barf if mismatched */ if (TI_UWINMASK != offsetof(struct thread_info, uwinmask) || TI_TASK != offsetof(struct thread_info, task) || - TI_EXECDOMAIN != offsetof(struct thread_info, exec_domain) || TI_FLAGS != offsetof(struct thread_info, flags) || TI_CPU != offsetof(struct thread_info, cpu) || TI_PREEMPT != offsetof(struct thread_info, preempt_count) || diff --git a/arch/sparc/kernel/traps_64.c b/arch/sparc/kernel/traps_64.c index 0e699745d643..d21cd625c0de 100644 --- a/arch/sparc/kernel/traps_64.c +++ b/arch/sparc/kernel/traps_64.c @@ -2691,8 +2691,6 @@ void __init trap_init(void) fault_address) || TI_KREGS != offsetof(struct thread_info, kregs) || TI_UTRAPS != offsetof(struct thread_info, utraps) || - TI_EXEC_DOMAIN != offsetof(struct thread_info, - exec_domain) || TI_REG_WINDOW != offsetof(struct thread_info, reg_window) || TI_RWIN_SPTRS != offsetof(struct thread_info, diff --git a/arch/tile/include/asm/thread_info.h b/arch/tile/include/asm/thread_info.h index 96c14c1430d8..98ee10a0ae89 100644 --- a/arch/tile/include/asm/thread_info.h +++ b/arch/tile/include/asm/thread_info.h @@ -26,7 +26,6 @@ */ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 homecache_cpu; /* CPU we are homecached on */ @@ -51,7 +50,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c index 8c5abf2e4794..5cbc864398d3 100644 --- a/arch/tile/kernel/compat_signal.c +++ b/arch/tile/kernel/compat_signal.c @@ -196,19 +196,12 @@ int compat_setup_rt_frame(struct ksignal *ksig, sigset_t *set, unsigned long restorer; struct compat_rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - int usig; frame = compat_get_sigframe(&ksig->ka, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto err; - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - /* Always write at least the signal number for the stack backtracer. */ if (ksig->ka.sa.sa_flags & SA_SIGINFO) { /* At sigreturn time, restore the callee-save registers too. */ @@ -243,7 +236,7 @@ int compat_setup_rt_frame(struct ksignal *ksig, sigset_t *set, regs->ex1 = PL_ICS_EX1(USER_PL, 1); /* set crit sec in handler */ regs->sp = ptr_to_compat_reg(frame); regs->lr = restorer; - regs->regs[0] = (unsigned long) usig; + regs->regs[0] = (unsigned long) sig; regs->regs[1] = ptr_to_compat_reg(&frame->info); regs->regs[2] = ptr_to_compat_reg(&frame->uc); regs->flags |= PT_FLAGS_CALLER_SAVES; diff --git a/arch/tile/kernel/signal.c b/arch/tile/kernel/signal.c index 8a524e332c1a..87299a6cfec8 100644 --- a/arch/tile/kernel/signal.c +++ b/arch/tile/kernel/signal.c @@ -151,19 +151,12 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, unsigned long restorer; struct rt_sigframe __user *frame; int err = 0, sig = ksig->sig; - int usig; frame = get_sigframe(&ksig->ka, regs, sizeof(*frame)); if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) goto err; - usig = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - /* Always write at least the signal number for the stack backtracer. */ if (ksig->ka.sa.sa_flags & SA_SIGINFO) { /* At sigreturn time, restore the callee-save registers too. */ @@ -198,7 +191,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, regs->ex1 = PL_ICS_EX1(USER_PL, 1); /* set crit sec in handler */ regs->sp = (unsigned long) frame; regs->lr = restorer; - regs->regs[0] = (unsigned long) usig; + regs->regs[0] = (unsigned long) sig; regs->regs[1] = (unsigned long) &frame->info; regs->regs[2] = (unsigned long) &frame->uc; regs->flags |= PT_FLAGS_CALLER_SAVES; diff --git a/arch/um/Kconfig.um b/arch/um/Kconfig.um index 5dbfe3d9107c..6e67847f5272 100644 --- a/arch/um/Kconfig.um +++ b/arch/um/Kconfig.um @@ -95,48 +95,6 @@ config MAGIC_SYSRQ The keys are documented in <file:Documentation/sysrq.txt>. Don't say Y unless you really know what this hack does. -config SMP - bool "Symmetric multi-processing support" - default n - depends on BROKEN - help - This option enables UML SMP support. - It is NOT related to having a real SMP box. Not directly, at least. - - UML implements virtual SMP by allowing as many processes to run - simultaneously on the host as there are virtual processors configured. - - Obviously, if the host is a uniprocessor, those processes will - timeshare, but, inside UML, will appear to be running simultaneously. - If the host is a multiprocessor, then UML processes may run - simultaneously, depending on the host scheduler. - - This, however, is supported only in TT mode. So, if you use the SKAS - patch on your host, switching to TT mode and enabling SMP usually - gives you worse performances. - Also, since the support for SMP has been under-developed, there could - be some bugs being exposed by enabling SMP. - - If you don't know what to do, say N. - -config NR_CPUS - int "Maximum number of CPUs (2-32)" - range 2 32 - depends on SMP - default "32" - -config HIGHMEM - bool "Highmem support" - depends on !64BIT && BROKEN - default n - help - This was used to allow UML to run with big amounts of memory. - Currently it is unstable, so if unsure say N. - - To use big amounts of memory, it is recommended enable static - linking (i.e. CONFIG_STATIC_LINK) - this should allow the - guest to use up to 2.75G of memory. - config KERNEL_STACK_ORDER int "Kernel stack size order" default 1 if 64BIT diff --git a/arch/um/Makefile b/arch/um/Makefile index e4b1a9639c4d..17d4460b1af3 100644 --- a/arch/um/Makefile +++ b/arch/um/Makefile @@ -43,8 +43,8 @@ endif HOST_DIR := arch/$(HEADER_ARCH) -include $(srctree)/$(ARCH_DIR)/Makefile-skas -include $(srctree)/$(HOST_DIR)/Makefile.um +include $(ARCH_DIR)/Makefile-skas +include $(HOST_DIR)/Makefile.um core-y += $(HOST_DIR)/um/ @@ -73,7 +73,7 @@ USER_CFLAGS = $(patsubst $(KERNEL_DEFINES),,$(patsubst -D__KERNEL__,,\ $(filter -I%,$(CFLAGS)) -D_FILE_OFFSET_BITS=64 -idirafter include #This will adjust *FLAGS accordingly to the platform. -include $(srctree)/$(ARCH_DIR)/Makefile-os-$(OS) +include $(ARCH_DIR)/Makefile-os-$(OS) KBUILD_CPPFLAGS += -I$(srctree)/$(HOST_DIR)/include \ -I$(srctree)/$(HOST_DIR)/include/uapi \ diff --git a/arch/um/Makefile-ia64 b/arch/um/Makefile-ia64 deleted file mode 100644 index f84dc23b0f6e..000000000000 --- a/arch/um/Makefile-ia64 +++ /dev/null @@ -1 +0,0 @@ -START_ADDR = 0x1000000000000000 diff --git a/arch/um/Makefile-ppc b/arch/um/Makefile-ppc deleted file mode 100644 index 66fd2003e165..000000000000 --- a/arch/um/Makefile-ppc +++ /dev/null @@ -1,9 +0,0 @@ -ifeq ($(CONFIG_HOST_2G_2G), y) -START_ADDR = 0x80000000 -else -START_ADDR = 0xc0000000 -endif -ARCH_CFLAGS = -U__powerpc__ -D__UM_PPC__ - -# The arch is ppc, but the elf32 name is powerpc -ELF_SUBARCH = powerpc diff --git a/arch/um/include/asm/fixmap.h b/arch/um/include/asm/fixmap.h index 3094ea3c73b0..1761fd75bf13 100644 --- a/arch/um/include/asm/fixmap.h +++ b/arch/um/include/asm/fixmap.h @@ -33,10 +33,6 @@ * fix-mapped? */ enum fixed_addresses { -#ifdef CONFIG_HIGHMEM - FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */ - FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1, -#endif __end_of_fixed_addresses }; diff --git a/arch/um/include/asm/pgtable.h b/arch/um/include/asm/pgtable.h index 2324b624f195..18eb9924dda3 100644 --- a/arch/um/include/asm/pgtable.h +++ b/arch/um/include/asm/pgtable.h @@ -47,11 +47,7 @@ extern unsigned long end_iomem; #define VMALLOC_OFFSET (__va_space) #define VMALLOC_START ((end_iomem + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) #define PKMAP_BASE ((FIXADDR_START - LAST_PKMAP * PAGE_SIZE) & PMD_MASK) -#ifdef CONFIG_HIGHMEM -# define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE) -#else -# define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE) -#endif +#define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE) #define MODULES_VADDR VMALLOC_START #define MODULES_END VMALLOC_END #define MODULES_LEN (MODULES_VADDR - MODULES_END) diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h index cbc5edd5a901..2d1e0dd5bb0b 100644 --- a/arch/um/include/asm/processor-generic.h +++ b/arch/um/include/asm/processor-generic.h @@ -98,16 +98,8 @@ struct cpuinfo_um { extern struct cpuinfo_um boot_cpu_data; -#define my_cpu_data cpu_data[smp_processor_id()] - -#ifdef CONFIG_SMP -extern struct cpuinfo_um cpu_data[]; -#define current_cpu_data cpu_data[smp_processor_id()] -#else #define cpu_data (&boot_cpu_data) #define current_cpu_data boot_cpu_data -#endif - #define KSTK_REG(tsk, reg) get_thread_reg(reg, &tsk->thread.switch_buf) extern unsigned long get_wchan(struct task_struct *p); diff --git a/arch/um/include/asm/smp.h b/arch/um/include/asm/smp.h index e4507938d8cf..9c3be355ed01 100644 --- a/arch/um/include/asm/smp.h +++ b/arch/um/include/asm/smp.h @@ -1,32 +1,6 @@ #ifndef __UM_SMP_H #define __UM_SMP_H -#ifdef CONFIG_SMP - -#include <linux/bitops.h> -#include <asm/current.h> -#include <linux/cpumask.h> - -#define raw_smp_processor_id() (current_thread->cpu) - -#define cpu_logical_map(n) (n) -#define cpu_number_map(n) (n) -extern int hard_smp_processor_id(void); -#define NO_PROC_ID -1 - -extern int ncpus; - - -static inline void smp_cpus_done(unsigned int maxcpus) -{ -} - -extern struct task_struct *idle_threads[NR_CPUS]; - -#else - #define hard_smp_processor_id() 0 #endif - -#endif diff --git a/arch/um/include/asm/thread_info.h b/arch/um/include/asm/thread_info.h index e04114c4fcd9..b30c85b141d9 100644 --- a/arch/um/include/asm/thread_info.h +++ b/arch/um/include/asm/thread_info.h @@ -14,7 +14,6 @@ struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ __u32 cpu; /* current CPU */ int preempt_count; /* 0 => preemptable, @@ -28,7 +27,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/um/include/shared/as-layout.h b/arch/um/include/shared/as-layout.h index 41c8c774ec10..ca1843e1df15 100644 --- a/arch/um/include/shared/as-layout.h +++ b/arch/um/include/shared/as-layout.h @@ -56,6 +56,7 @@ extern unsigned long brk_start; extern unsigned long host_task_size; extern int linux_main(int argc, char **argv); +extern void uml_finishsetup(void); struct siginfo; extern void (*sig_info[])(int, struct siginfo *si, struct uml_pt_regs *); diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index 08eec0b691b0..d824528f6f62 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h @@ -174,7 +174,6 @@ extern unsigned long long os_makedev(unsigned major, unsigned minor); /* start_up.c */ extern void os_early_checks(void); -extern void can_do_skas(void); extern void os_check_bugs(void); extern void check_host_supports_tls(int *supports_tls, int *tls_min); @@ -187,7 +186,6 @@ extern int os_process_parent(int pid); extern void os_stop_process(int pid); extern void os_kill_process(int pid, int reap_child); extern void os_kill_ptraced_process(int pid, int reap_child); -extern long os_ptrace_ldt(long pid, long addr, long data); extern int os_getpid(void); extern int os_getpgrp(void); diff --git a/arch/um/include/shared/skas/proc_mm.h b/arch/um/include/shared/skas/proc_mm.h deleted file mode 100644 index 902809209603..000000000000 --- a/arch/um/include/shared/skas/proc_mm.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SKAS_PROC_MM_H -#define __SKAS_PROC_MM_H - -#define MM_MMAP 54 -#define MM_MUNMAP 55 -#define MM_MPROTECT 56 -#define MM_COPY_SEGMENTS 57 - -struct mm_mmap { - unsigned long addr; - unsigned long len; - unsigned long prot; - unsigned long flags; - unsigned long fd; - unsigned long offset; -}; - -struct mm_munmap { - unsigned long addr; - unsigned long len; -}; - -struct mm_mprotect { - unsigned long addr; - unsigned long len; - unsigned int prot; -}; - -struct proc_mm_op { - int op; - union { - struct mm_mmap mmap; - struct mm_munmap munmap; - struct mm_mprotect mprotect; - int copy_segments; - } u; -}; - -#endif diff --git a/arch/um/include/shared/skas/skas.h b/arch/um/include/shared/skas/skas.h index c45df961c874..911f3c45ad1f 100644 --- a/arch/um/include/shared/skas/skas.h +++ b/arch/um/include/shared/skas/skas.h @@ -9,13 +9,10 @@ #include <sysdep/ptrace.h> extern int userspace_pid[]; -extern int proc_mm, ptrace_faultinfo, ptrace_ldt; -extern int skas_needs_stub; extern int user_thread(unsigned long stack, int flags); extern void new_thread_handler(void); extern void handle_syscall(struct uml_pt_regs *regs); -extern int new_mm(unsigned long stack); extern long execute_syscall_skas(void *r); extern unsigned long current_stub_stack(void); diff --git a/arch/um/include/shared/skas_ptrace.h b/arch/um/include/shared/skas_ptrace.h deleted file mode 100644 index 630a9c92b93c..000000000000 --- a/arch/um/include/shared/skas_ptrace.h +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) - * Licensed under the GPL - */ - -#ifndef __SKAS_PTRACE_H -#define __SKAS_PTRACE_H - -#define PTRACE_FAULTINFO 52 -#define PTRACE_SWITCH_MM 55 - -#include <sysdep/skas_ptrace.h> - -#endif diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index 2d840a070c8b..a6a5e42caaef 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile @@ -12,8 +12,8 @@ clean-files := obj-y = config.o exec.o exitcode.o irq.o ksyms.o mem.o \ physmem.o process.o ptrace.o reboot.o sigio.o \ - signal.o smp.o syscall.o sysrq.o time.o tlb.o trap.o \ - um_arch.o umid.o maccess.o skas/ + signal.o syscall.o sysrq.o time.o tlb.o trap.o \ + um_arch.o umid.o maccess.o kmsg_dump.o skas/ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o obj-$(CONFIG_GPROF) += gprof_syms.o diff --git a/arch/um/kernel/irq.c b/arch/um/kernel/irq.c index 1d8505b1e290..23cb9350d47e 100644 --- a/arch/um/kernel/irq.c +++ b/arch/um/kernel/irq.c @@ -35,9 +35,6 @@ void sigio_handler(int sig, struct siginfo *unused_si, struct uml_pt_regs *regs) struct irq_fd *irq_fd; int n; - if (smp_sigio_handler()) - return; - while (1) { n = os_waiting_for_events(active_fds); if (n <= 0) { diff --git a/arch/um/kernel/kmsg_dump.c b/arch/um/kernel/kmsg_dump.c new file mode 100644 index 000000000000..407d49251d6f --- /dev/null +++ b/arch/um/kernel/kmsg_dump.c @@ -0,0 +1,43 @@ +#include <linux/kmsg_dump.h> +#include <linux/console.h> +#include <shared/init.h> +#include <shared/kern.h> +#include <os.h> + +static void kmsg_dumper_stdout(struct kmsg_dumper *dumper, + enum kmsg_dump_reason reason) +{ + static char line[1024]; + + size_t len = 0; + bool con_available = false; + + /* only dump kmsg when no console is available */ + if (!console_trylock()) + return; + + if (console_drivers != NULL) + con_available = true; + + console_unlock(); + + if (con_available == true) + return; + + printf("kmsg_dump:\n"); + while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) { + line[len] = '\0'; + printf("%s", line); + } +} + +static struct kmsg_dumper kmsg_dumper = { + .dump = kmsg_dumper_stdout +}; + +int __init kmsg_dumper_stdout_init(void) +{ + return kmsg_dump_register(&kmsg_dumper); +} + +__uml_postsetup(kmsg_dumper_stdout_init); diff --git a/arch/um/kernel/mem.c b/arch/um/kernel/mem.c index 8636e905426f..b2a2dff50b4e 100644 --- a/arch/um/kernel/mem.c +++ b/arch/um/kernel/mem.c @@ -38,19 +38,6 @@ int kmalloc_ok = 0; /* Used during early boot */ static unsigned long brk_end; -#ifdef CONFIG_HIGHMEM -static void setup_highmem(unsigned long highmem_start, - unsigned long highmem_len) -{ - unsigned long highmem_pfn; - int i; - - highmem_pfn = __pa(highmem_start) >> PAGE_SHIFT; - for (i = 0; i < highmem_len >> PAGE_SHIFT; i++) - free_highmem_page(&mem_map[highmem_pfn + i]); -} -#endif - void __init mem_init(void) { /* clear the zero-page */ @@ -67,9 +54,6 @@ void __init mem_init(void) /* this will put all low memory onto the freelists */ free_all_bootmem(); max_low_pfn = totalram_pages; -#ifdef CONFIG_HIGHMEM - setup_highmem(end_iomem, highmem); -#endif max_pfn = totalram_pages; mem_init_print_info(NULL); kmalloc_ok = 1; @@ -127,49 +111,6 @@ static void __init fixrange_init(unsigned long start, unsigned long end, } } -#ifdef CONFIG_HIGHMEM -pte_t *kmap_pte; -pgprot_t kmap_prot; - -#define kmap_get_fixmap_pte(vaddr) \ - pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)),\ - (vaddr)), (vaddr)) - -static void __init kmap_init(void) -{ - unsigned long kmap_vstart; - - /* cache the first kmap pte */ - kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN); - kmap_pte = kmap_get_fixmap_pte(kmap_vstart); - - kmap_prot = PAGE_KERNEL; -} - -static void __init init_highmem(void) -{ - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; - pte_t *pte; - unsigned long vaddr; - - /* - * Permanent kmaps: - */ - vaddr = PKMAP_BASE; - fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, swapper_pg_dir); - - pgd = swapper_pg_dir + pgd_index(vaddr); - pud = pud_offset(pgd, vaddr); - pmd = pmd_offset(pud, vaddr); - pte = pte_offset_kernel(pmd, vaddr); - pkmap_page_table = pte; - - kmap_init(); -} -#endif /* CONFIG_HIGHMEM */ - static void __init fixaddr_user_init( void) { #ifdef CONFIG_ARCH_REUSE_HOST_VSYSCALL_AREA @@ -211,9 +152,6 @@ void __init paging_init(void) zones_size[ZONE_NORMAL] = (end_iomem >> PAGE_SHIFT) - (uml_physmem >> PAGE_SHIFT); -#ifdef CONFIG_HIGHMEM - zones_size[ZONE_HIGHMEM] = highmem >> PAGE_SHIFT; -#endif free_area_init(zones_size); /* @@ -224,10 +162,6 @@ void __init paging_init(void) fixrange_init(vaddr, FIXADDR_TOP, swapper_pg_dir); fixaddr_user_init(); - -#ifdef CONFIG_HIGHMEM - init_highmem(); -#endif } /* diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index 549ecf3f5857..9034fc8056b4 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c @@ -57,22 +57,51 @@ void map_memory(unsigned long virt, unsigned long phys, unsigned long len, extern int __syscall_stub_start; +/** + * setup_physmem() - Setup physical memory for UML + * @start: Start address of the physical kernel memory, + * i.e start address of the executable image. + * @reserve_end: end address of the physical kernel memory. + * @len: Length of total physical memory that should be mapped/made + * available, in bytes. + * @highmem: Number of highmem bytes that should be mapped/made available. + * + * Creates an unlinked temporary file of size (len + highmem) and memory maps + * it on the last executable image address (uml_reserved). + * + * The offset is needed as the length of the total physical memory + * (len + highmem) includes the size of the memory used be the executable image, + * but the mapped-to address is the last address of the executable image + * (uml_reserved == end address of executable image). + * + * The memory mapped memory of the temporary file is used as backing memory + * of all user space processes/kernel tasks. + */ void __init setup_physmem(unsigned long start, unsigned long reserve_end, unsigned long len, unsigned long long highmem) { unsigned long reserve = reserve_end - start; - int pfn = PFN_UP(__pa(reserve_end)); - int delta = (len - reserve) >> PAGE_SHIFT; - int err, offset, bootmap_size; + unsigned long pfn = PFN_UP(__pa(reserve_end)); + unsigned long delta = (len - reserve) >> PAGE_SHIFT; + unsigned long offset, bootmap_size; + long map_size; + int err; + + offset = uml_reserved - uml_physmem; + map_size = len - offset; + if(map_size <= 0) { + printf("Too few physical memory! Needed=%d, given=%d\n", + offset, len); + exit(1); + } physmem_fd = create_mem_file(len + highmem); - offset = uml_reserved - uml_physmem; err = os_map_memory((void *) uml_reserved, physmem_fd, offset, - len - offset, 1, 1, 1); + map_size, 1, 1, 1); if (err < 0) { printf("setup_physmem - mapping %ld bytes of memory at 0x%p " - "failed - errno = %d\n", len - offset, + "failed - errno = %d\n", map_size, (void *) uml_reserved, err); exit(1); } diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c index f17bca8ed2ce..68b9119841cd 100644 --- a/arch/um/kernel/process.c +++ b/arch/um/kernel/process.c @@ -259,17 +259,6 @@ int strlen_user_proc(char __user *str) return strlen_user(str); } -int smp_sigio_handler(void) -{ -#ifdef CONFIG_SMP - int cpu = current_thread_info()->cpu; - IPI_handler(cpu); - if (cpu != 0) - return 1; -#endif - return 0; -} - int cpu(void) { return current_thread_info()->cpu; diff --git a/arch/um/kernel/ptrace.c b/arch/um/kernel/ptrace.c index 62435ef003d9..174ee5017264 100644 --- a/arch/um/kernel/ptrace.c +++ b/arch/um/kernel/ptrace.c @@ -8,9 +8,6 @@ #include <linux/sched.h> #include <linux/tracehook.h> #include <asm/uaccess.h> -#include <skas_ptrace.h> - - void user_enable_single_step(struct task_struct *child) { @@ -104,35 +101,6 @@ long arch_ptrace(struct task_struct *child, long request, ret = ptrace_set_thread_area(child, addr, vp); break; - case PTRACE_FAULTINFO: { - /* - * Take the info from thread->arch->faultinfo, - * but transfer max. sizeof(struct ptrace_faultinfo). - * On i386, ptrace_faultinfo is smaller! - */ - ret = copy_to_user(p, &child->thread.arch.faultinfo, - sizeof(struct ptrace_faultinfo)) ? - -EIO : 0; - break; - } - -#ifdef PTRACE_LDT - case PTRACE_LDT: { - struct ptrace_ldt ldt; - - if (copy_from_user(&ldt, p, sizeof(ldt))) { - ret = -EIO; - break; - } - - /* - * This one is confusing, so just punt and return -EIO for - * now - */ - ret = -EIO; - break; - } -#endif default: ret = ptrace_request(child, request, addr, data); if (ret == -EIO) diff --git a/arch/um/kernel/reboot.c b/arch/um/kernel/reboot.c index ced8903921ae..9bdf67a092a5 100644 --- a/arch/um/kernel/reboot.c +++ b/arch/um/kernel/reboot.c @@ -15,28 +15,21 @@ void (*pm_power_off)(void); static void kill_off_processes(void) { - if (proc_mm) - /* - * FIXME: need to loop over userspace_pids - */ - os_kill_ptraced_process(userspace_pid[0], 1); - else { - struct task_struct *p; - int pid; - - read_lock(&tasklist_lock); - for_each_process(p) { - struct task_struct *t; - - t = find_lock_task_mm(p); - if (!t) - continue; - pid = t->mm->context.id.u.pid; - task_unlock(t); - os_kill_ptraced_process(pid, 1); - } - read_unlock(&tasklist_lock); + struct task_struct *p; + int pid; + + read_lock(&tasklist_lock); + for_each_process(p) { + struct task_struct *t; + + t = find_lock_task_mm(p); + if (!t) + continue; + pid = t->mm->context.id.u.pid; + task_unlock(t); + os_kill_ptraced_process(pid, 1); } + read_unlock(&tasklist_lock); } void uml_cleanup(void) diff --git a/arch/um/kernel/skas/mmu.c b/arch/um/kernel/skas/mmu.c index 007d5503f49b..94abdcc1d6ad 100644 --- a/arch/um/kernel/skas/mmu.c +++ b/arch/um/kernel/skas/mmu.c @@ -54,35 +54,22 @@ int init_new_context(struct task_struct *task, struct mm_struct *mm) unsigned long stack = 0; int ret = -ENOMEM; - if (skas_needs_stub) { - stack = get_zeroed_page(GFP_KERNEL); - if (stack == 0) - goto out; - } + stack = get_zeroed_page(GFP_KERNEL); + if (stack == 0) + goto out; to_mm->id.stack = stack; if (current->mm != NULL && current->mm != &init_mm) from_mm = ¤t->mm->context; - if (proc_mm) { - ret = new_mm(stack); - if (ret < 0) { - printk(KERN_ERR "init_new_context_skas - " - "new_mm failed, errno = %d\n", ret); - goto out_free; - } - to_mm->id.u.mm_fd = ret; - } - else { - if (from_mm) - to_mm->id.u.pid = copy_context_skas0(stack, - from_mm->id.u.pid); - else to_mm->id.u.pid = start_userspace(stack); - - if (to_mm->id.u.pid < 0) { - ret = to_mm->id.u.pid; - goto out_free; - } + if (from_mm) + to_mm->id.u.pid = copy_context_skas0(stack, + from_mm->id.u.pid); + else to_mm->id.u.pid = start_userspace(stack); + + if (to_mm->id.u.pid < 0) { + ret = to_mm->id.u.pid; + goto out_free; } ret = init_new_ldt(to_mm, from_mm); @@ -105,9 +92,6 @@ void uml_setup_stubs(struct mm_struct *mm) { int err, ret; - if (!skas_needs_stub) - return; - ret = init_stub_pte(mm, STUB_CODE, (unsigned long) &__syscall_stub_start); if (ret) @@ -154,25 +138,19 @@ void destroy_context(struct mm_struct *mm) { struct mm_context *mmu = &mm->context; - if (proc_mm) - os_close_file(mmu->id.u.mm_fd); - else { - /* - * If init_new_context wasn't called, this will be - * zero, resulting in a kill(0), which will result in the - * whole UML suddenly dying. Also, cover negative and - * 1 cases, since they shouldn't happen either. - */ - if (mmu->id.u.pid < 2) { - printk(KERN_ERR "corrupt mm_context - pid = %d\n", - mmu->id.u.pid); - return; - } - os_kill_ptraced_process(mmu->id.u.pid, 1); + /* + * If init_new_context wasn't called, this will be + * zero, resulting in a kill(0), which will result in the + * whole UML suddenly dying. Also, cover negative and + * 1 cases, since they shouldn't happen either. + */ + if (mmu->id.u.pid < 2) { + printk(KERN_ERR "corrupt mm_context - pid = %d\n", + mmu->id.u.pid); + return; } + os_kill_ptraced_process(mmu->id.u.pid, 1); - if (skas_needs_stub) - free_page(mmu->id.stack); - + free_page(mmu->id.stack); free_ldt(mmu); } diff --git a/arch/um/kernel/skas/process.c b/arch/um/kernel/skas/process.c index 4da11b3c8ddb..527fa5881915 100644 --- a/arch/um/kernel/skas/process.c +++ b/arch/um/kernel/skas/process.c @@ -10,25 +10,6 @@ #include <os.h> #include <skas.h> -int new_mm(unsigned long stack) -{ - int fd, err; - - fd = os_open_file("/proc/mm", of_cloexec(of_write(OPENFLAGS())), 0); - if (fd < 0) - return fd; - - if (skas_needs_stub) { - err = map_stub_pages(fd, STUB_CODE, STUB_DATA, stack); - if (err) { - os_close_file(fd); - return err; - } - } - - return fd; -} - extern void start_kernel(void); static int __init start_kernel_proc(void *unused) @@ -40,9 +21,7 @@ static int __init start_kernel_proc(void *unused) cpu_tasks[0].pid = pid; cpu_tasks[0].task = current; -#ifdef CONFIG_SMP - init_cpu_online(get_cpu_mask(0)); -#endif + start_kernel(); return 0; } @@ -55,14 +34,6 @@ int __init start_uml(void) { stack_protections((unsigned long) &cpu0_irqstack); set_sigstack(cpu0_irqstack, THREAD_SIZE); - if (proc_mm) { - userspace_pid[0] = start_userspace(0); - if (userspace_pid[0] < 0) { - printf("start_uml - start_userspace returned %d\n", - userspace_pid[0]); - exit(1); - } - } init_new_thread_signals(); diff --git a/arch/um/kernel/smp.c b/arch/um/kernel/smp.c deleted file mode 100644 index 5c8c3ea7db7b..000000000000 --- a/arch/um/kernel/smp.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) - * Licensed under the GPL - */ - -#include <linux/percpu.h> -#include <asm/pgalloc.h> -#include <asm/tlb.h> - -#ifdef CONFIG_SMP - -#include <linux/sched.h> -#include <linux/module.h> -#include <linux/threads.h> -#include <linux/interrupt.h> -#include <linux/err.h> -#include <linux/hardirq.h> -#include <asm/smp.h> -#include <asm/processor.h> -#include <asm/spinlock.h> -#include <kern.h> -#include <irq_user.h> -#include <os.h> - -/* Per CPU bogomips and other parameters - * The only piece used here is the ipi pipe, which is set before SMP is - * started and never changed. - */ -struct cpuinfo_um cpu_data[NR_CPUS]; - -/* A statistic, can be a little off */ -int num_reschedules_sent = 0; - -/* Not changed after boot */ -struct task_struct *idle_threads[NR_CPUS]; - -void smp_send_reschedule(int cpu) -{ - os_write_file(cpu_data[cpu].ipi_pipe[1], "R", 1); - num_reschedules_sent++; -} - -void smp_send_stop(void) -{ - int i; - - printk(KERN_INFO "Stopping all CPUs..."); - for (i = 0; i < num_online_cpus(); i++) { - if (i == current_thread->cpu) - continue; - os_write_file(cpu_data[i].ipi_pipe[1], "S", 1); - } - printk(KERN_CONT "done\n"); -} - -static cpumask_t smp_commenced_mask = CPU_MASK_NONE; -static cpumask_t cpu_callin_map = CPU_MASK_NONE; - -static int idle_proc(void *cpup) -{ - int cpu = (int) cpup, err; - - err = os_pipe(cpu_data[cpu].ipi_pipe, 1, 1); - if (err < 0) - panic("CPU#%d failed to create IPI pipe, err = %d", cpu, -err); - - os_set_fd_async(cpu_data[cpu].ipi_pipe[0]); - - wmb(); - if (cpu_test_and_set(cpu, cpu_callin_map)) { - printk(KERN_ERR "huh, CPU#%d already present??\n", cpu); - BUG(); - } - - while (!cpu_isset(cpu, smp_commenced_mask)) - cpu_relax(); - - notify_cpu_starting(cpu); - set_cpu_online(cpu, true); - default_idle(); - return 0; -} - -static struct task_struct *idle_thread(int cpu) -{ - struct task_struct *new_task; - - current->thread.request.u.thread.proc = idle_proc; - current->thread.request.u.thread.arg = (void *) cpu; - new_task = fork_idle(cpu); - if (IS_ERR(new_task)) - panic("copy_process failed in idle_thread, error = %ld", - PTR_ERR(new_task)); - - cpu_tasks[cpu] = ((struct cpu_task) - { .pid = new_task->thread.mode.tt.extern_pid, - .task = new_task } ); - idle_threads[cpu] = new_task; - panic("skas mode doesn't support SMP"); - return new_task; -} - -void smp_prepare_cpus(unsigned int maxcpus) -{ - struct task_struct *idle; - unsigned long waittime; - int err, cpu, me = smp_processor_id(); - int i; - - for (i = 0; i < ncpus; ++i) - set_cpu_possible(i, true); - - set_cpu_online(me, true); - cpu_set(me, cpu_callin_map); - - err = os_pipe(cpu_data[me].ipi_pipe, 1, 1); - if (err < 0) - panic("CPU#0 failed to create IPI pipe, errno = %d", -err); - - os_set_fd_async(cpu_data[me].ipi_pipe[0]); - - for (cpu = 1; cpu < ncpus; cpu++) { - printk(KERN_INFO "Booting processor %d...\n", cpu); - - idle = idle_thread(cpu); - - init_idle(idle, cpu); - - waittime = 200000000; - while (waittime-- && !cpu_isset(cpu, cpu_callin_map)) - cpu_relax(); - - printk(KERN_INFO "%s\n", - cpu_isset(cpu, cpu_calling_map) ? "done" : "failed"); - } -} - -void smp_prepare_boot_cpu(void) -{ - set_cpu_online(smp_processor_id(), true); -} - -int __cpu_up(unsigned int cpu, struct task_struct *tidle) -{ - cpu_set(cpu, smp_commenced_mask); - while (!cpu_online(cpu)) - mb(); - return 0; -} - -int setup_profiling_timer(unsigned int multiplier) -{ - printk(KERN_INFO "setup_profiling_timer\n"); - return 0; -} - -void smp_call_function_slave(int cpu); - -void IPI_handler(int cpu) -{ - unsigned char c; - int fd; - - fd = cpu_data[cpu].ipi_pipe[0]; - while (os_read_file(fd, &c, 1) == 1) { - switch (c) { - case 'C': - smp_call_function_slave(cpu); - break; - - case 'R': - scheduler_ipi(); - break; - - case 'S': - printk(KERN_INFO "CPU#%d stopping\n", cpu); - while (1) - pause(); - break; - - default: - printk(KERN_ERR "CPU#%d received unknown IPI [%c]!\n", - cpu, c); - break; - } - } -} - -int hard_smp_processor_id(void) -{ - return pid_to_processor_id(os_getpid()); -} - -static DEFINE_SPINLOCK(call_lock); -static atomic_t scf_started; -static atomic_t scf_finished; -static void (*func)(void *info); -static void *info; - -void smp_call_function_slave(int cpu) -{ - atomic_inc(&scf_started); - (*func)(info); - atomic_inc(&scf_finished); -} - -int smp_call_function(void (*_func)(void *info), void *_info, int wait) -{ - int cpus = num_online_cpus() - 1; - int i; - - if (!cpus) - return 0; - - /* Can deadlock when called with interrupts disabled */ - WARN_ON(irqs_disabled()); - - spin_lock_bh(&call_lock); - atomic_set(&scf_started, 0); - atomic_set(&scf_finished, 0); - func = _func; - info = _info; - - for_each_online_cpu(i) - os_write_file(cpu_data[i].ipi_pipe[1], "C", 1); - - while (atomic_read(&scf_started) != cpus) - barrier(); - - if (wait) - while (atomic_read(&scf_finished) != cpus) - barrier(); - - spin_unlock_bh(&call_lock); - return 0; -} - -#endif diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c index 894c8d303cda..aa1b56f5ac68 100644 --- a/arch/um/kernel/sysrq.c +++ b/arch/um/kernel/sysrq.c @@ -29,7 +29,7 @@ static const struct stacktrace_ops stackops = { void show_stack(struct task_struct *task, unsigned long *stack) { - unsigned long *sp = stack, bp = 0; + unsigned long *sp = stack; struct pt_regs *segv_regs = current->thread.segv_regs; int i; @@ -39,10 +39,6 @@ void show_stack(struct task_struct *task, unsigned long *stack) return; } -#ifdef CONFIG_FRAME_POINTER - bp = get_frame_pointer(task, segv_regs); -#endif - if (!stack) sp = get_stack_pointer(task, segv_regs); diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 209617302df8..8e4daf44e980 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -220,7 +220,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, panic("Segfault with no mm"); } - if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi)) + if (SEGV_IS_FIXABLE(&fi)) err = handle_page_fault(address, ip, is_write, is_user, &si.si_code); else { diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 9274eae6ae7b..07f798f4bcee 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c @@ -11,6 +11,7 @@ #include <linux/string.h> #include <linux/utsname.h> #include <linux/sched.h> +#include <linux/kmsg_dump.h> #include <asm/pgtable.h> #include <asm/processor.h> #include <asm/sections.h> @@ -66,12 +67,6 @@ static int show_cpuinfo(struct seq_file *m, void *v) { int index = 0; -#ifdef CONFIG_SMP - index = (struct cpuinfo_um *) v - cpu_data; - if (!cpu_online(index)) - return 0; -#endif - seq_printf(m, "processor\t: %d\n", index); seq_printf(m, "vendor_id\t: User Mode Linux\n"); seq_printf(m, "model name\t: UML\n"); @@ -168,23 +163,6 @@ __uml_setup("debug", no_skas_debug_setup, " this flag is not needed to run gdb on UML in skas mode\n\n" ); -#ifdef CONFIG_SMP -static int __init uml_ncpus_setup(char *line, int *add) -{ - if (!sscanf(line, "%d", &ncpus)) { - printf("Couldn't parse [%s]\n", line); - return -1; - } - - return 0; -} - -__uml_setup("ncpus=", uml_ncpus_setup, -"ncpus=<# of desired CPUs>\n" -" This tells an SMP kernel how many virtual processors to start.\n\n" -); -#endif - static int __init Usage(char *line, int *add) { const char **p; @@ -234,6 +212,7 @@ static void __init uml_postsetup(void) static int panic_exit(struct notifier_block *self, unsigned long unused1, void *unused2) { + kmsg_dump(KMSG_DUMP_PANIC); bust_spinlocks(1); bust_spinlocks(0); uml_exitcode = 1; @@ -247,6 +226,16 @@ static struct notifier_block panic_exit_notifier = { .priority = 0 }; +void uml_finishsetup(void) +{ + atomic_notifier_chain_register(&panic_notifier_list, + &panic_exit_notifier); + + uml_postsetup(); + + new_thread_handler(); +} + /* Set during early boot */ unsigned long task_size; EXPORT_SYMBOL(task_size); @@ -268,7 +257,6 @@ int __init linux_main(int argc, char **argv) unsigned long stack; unsigned int i; int add; - char * mode; for (i = 1; i < argc; i++) { if ((i == 1) && (argv[i][0] == ' ')) @@ -291,15 +279,6 @@ int __init linux_main(int argc, char **argv) /* OS sanity checks that need to happen before the kernel runs */ os_early_checks(); - can_do_skas(); - - if (proc_mm && ptrace_faultinfo) - mode = "SKAS3"; - else - mode = "SKAS0"; - - printf("UML running in %s mode\n", mode); - brk_start = (unsigned long) sbrk(0); /* @@ -334,11 +313,6 @@ int __init linux_main(int argc, char **argv) if (physmem_size + iomem_size > max_physmem) { highmem = physmem_size + iomem_size - max_physmem; physmem_size -= highmem; -#ifndef CONFIG_HIGHMEM - highmem = 0; - printf("CONFIG_HIGHMEM not enabled - physical memory shrunk " - "to %Lu bytes\n", physmem_size); -#endif } high_physmem = uml_physmem + physmem_size; @@ -362,11 +336,6 @@ int __init linux_main(int argc, char **argv) printf("Kernel virtual memory size shrunk to %lu bytes\n", virtmem_size); - atomic_notifier_chain_register(&panic_notifier_list, - &panic_exit_notifier); - - uml_postsetup(); - stack_protections((unsigned long) &init_thread_info); os_flush_stdout(); @@ -390,15 +359,3 @@ void __init check_bugs(void) void apply_alternatives(struct alt_instr *start, struct alt_instr *end) { } - -#ifdef CONFIG_SMP -void alternatives_smp_module_add(struct module *mod, char *name, - void *locks, void *locks_end, - void *text, void *text_end) -{ -} - -void alternatives_smp_module_del(struct module *mod) -{ -} -#endif diff --git a/arch/um/os-Linux/process.c b/arch/um/os-Linux/process.c index 33496fe2bb52..8408aba915b2 100644 --- a/arch/um/os-Linux/process.c +++ b/arch/um/os-Linux/process.c @@ -16,7 +16,6 @@ #include <init.h> #include <longjmp.h> #include <os.h> -#include <skas_ptrace.h> #define ARBITRARY_ADDR -1 #define FAILURE_PID -1 @@ -102,21 +101,6 @@ void os_kill_process(int pid, int reap_child) CATCH_EINTR(waitpid(pid, NULL, __WALL)); } -/* This is here uniquely to have access to the userspace errno, i.e. the one - * used by ptrace in case of error. - */ - -long os_ptrace_ldt(long pid, long addr, long data) -{ - int ret; - - ret = ptrace(PTRACE_LDT, pid, addr, data); - - if (ret < 0) - return -errno; - return ret; -} - /* Kill off a ptraced child by all means available. kill it normally first, * then PTRACE_KILL it, then PTRACE_CONT it in case it's in a run state from * which it can't exit directly. diff --git a/arch/um/os-Linux/skas/mem.c b/arch/um/os-Linux/skas/mem.c index 689b18db798f..e7f8c945a573 100644 --- a/arch/um/os-Linux/skas/mem.c +++ b/arch/um/os-Linux/skas/mem.c @@ -12,7 +12,6 @@ #include <as-layout.h> #include <mm_id.h> #include <os.h> -#include <proc_mm.h> #include <ptrace_user.h> #include <registers.h> #include <skas.h> @@ -46,8 +45,6 @@ static int __init init_syscall_regs(void) __initcall(init_syscall_regs); -extern int proc_mm; - static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr) { int n, i; @@ -56,10 +53,6 @@ static inline long do_syscall_stub(struct mm_id * mm_idp, void **addr) unsigned long * syscall; int err, pid = mm_idp->u.pid; - if (proc_mm) - /* FIXME: Need to look up userspace_pid by cpu */ - pid = userspace_pid[0]; - n = ptrace_setregs(pid, syscall_regs); if (n < 0) { printk(UM_KERN_ERR "Registers - \n"); @@ -178,38 +171,12 @@ int map(struct mm_id * mm_idp, unsigned long virt, unsigned long len, int prot, int phys_fd, unsigned long long offset, int done, void **data) { int ret; + unsigned long args[] = { virt, len, prot, + MAP_SHARED | MAP_FIXED, phys_fd, + MMAP_OFFSET(offset) }; - if (proc_mm) { - struct proc_mm_op map; - int fd = mm_idp->u.mm_fd; - - map = ((struct proc_mm_op) { .op = MM_MMAP, - .u = - { .mmap = - { .addr = virt, - .len = len, - .prot = prot, - .flags = MAP_SHARED | - MAP_FIXED, - .fd = phys_fd, - .offset= offset - } } } ); - CATCH_EINTR(ret = write(fd, &map, sizeof(map))); - if (ret != sizeof(map)) { - ret = -errno; - printk(UM_KERN_ERR "map : /proc/mm map failed, " - "err = %d\n", -ret); - } - else ret = 0; - } - else { - unsigned long args[] = { virt, len, prot, - MAP_SHARED | MAP_FIXED, phys_fd, - MMAP_OFFSET(offset) }; - - ret = run_syscall_stub(mm_idp, STUB_MMAP_NR, args, virt, - data, done); - } + ret = run_syscall_stub(mm_idp, STUB_MMAP_NR, args, virt, + data, done); return ret; } @@ -218,32 +185,11 @@ int unmap(struct mm_id * mm_idp, unsigned long addr, unsigned long len, int done, void **data) { int ret; + unsigned long args[] = { (unsigned long) addr, len, 0, 0, 0, + 0 }; - if (proc_mm) { - struct proc_mm_op unmap; - int fd = mm_idp->u.mm_fd; - - unmap = ((struct proc_mm_op) { .op = MM_MUNMAP, - .u = - { .munmap = - { .addr = - (unsigned long) addr, - .len = len } } } ); - CATCH_EINTR(ret = write(fd, &unmap, sizeof(unmap))); - if (ret != sizeof(unmap)) { - ret = -errno; - printk(UM_KERN_ERR "unmap - proc_mm write returned " - "%d\n", ret); - } - else ret = 0; - } - else { - unsigned long args[] = { (unsigned long) addr, len, 0, 0, 0, - 0 }; - - ret = run_syscall_stub(mm_idp, __NR_munmap, args, 0, - data, done); - } + ret = run_syscall_stub(mm_idp, __NR_munmap, args, 0, + data, done); return ret; } @@ -251,33 +197,11 @@ int unmap(struct mm_id * mm_idp, unsigned long addr, unsigned long len, int protect(struct mm_id * mm_idp, unsigned long addr, unsigned long len, unsigned int prot, int done, void **data) { - struct proc_mm_op protect; int ret; + unsigned long args[] = { addr, len, prot, 0, 0, 0 }; - if (proc_mm) { - int fd = mm_idp->u.mm_fd; - - protect = ((struct proc_mm_op) { .op = MM_MPROTECT, - .u = - { .mprotect = - { .addr = - (unsigned long) addr, - .len = len, - .prot = prot } } } ); - - CATCH_EINTR(ret = write(fd, &protect, sizeof(protect))); - if (ret != sizeof(protect)) { - ret = -errno; - printk(UM_KERN_ERR "protect failed, err = %d", -ret); - } - else ret = 0; - } - else { - unsigned long args[] = { addr, len, prot, 0, 0, 0 }; - - ret = run_syscall_stub(mm_idp, __NR_mprotect, args, 0, - data, done); - } + ret = run_syscall_stub(mm_idp, __NR_mprotect, args, 0, + data, done); return ret; } diff --git a/arch/um/os-Linux/skas/process.c b/arch/um/os-Linux/skas/process.c index 908579f2b0ab..7a9777570a62 100644 --- a/arch/um/os-Linux/skas/process.c +++ b/arch/um/os-Linux/skas/process.c @@ -16,11 +16,9 @@ #include <kern_util.h> #include <mem.h> #include <os.h> -#include <proc_mm.h> #include <ptrace_user.h> #include <registers.h> #include <skas.h> -#include <skas_ptrace.h> #include <sysdep/stub.h> int is_skas_winch(int pid, int fd, void *data) @@ -91,50 +89,33 @@ extern unsigned long current_stub_stack(void); static void get_skas_faultinfo(int pid, struct faultinfo *fi) { int err; + unsigned long fpregs[FP_SIZE]; - if (ptrace_faultinfo) { - err = ptrace(PTRACE_FAULTINFO, pid, 0, fi); - if (err) { - printk(UM_KERN_ERR "get_skas_faultinfo - " - "PTRACE_FAULTINFO failed, errno = %d\n", errno); - fatal_sigsegv(); - } - - /* Special handling for i386, which has different structs */ - if (sizeof(struct ptrace_faultinfo) < sizeof(struct faultinfo)) - memset((char *)fi + sizeof(struct ptrace_faultinfo), 0, - sizeof(struct faultinfo) - - sizeof(struct ptrace_faultinfo)); + err = get_fp_registers(pid, fpregs); + if (err < 0) { + printk(UM_KERN_ERR "save_fp_registers returned %d\n", + err); + fatal_sigsegv(); } - else { - unsigned long fpregs[FP_SIZE]; - - err = get_fp_registers(pid, fpregs); - if (err < 0) { - printk(UM_KERN_ERR "save_fp_registers returned %d\n", - err); - fatal_sigsegv(); - } - err = ptrace(PTRACE_CONT, pid, 0, SIGSEGV); - if (err) { - printk(UM_KERN_ERR "Failed to continue stub, pid = %d, " - "errno = %d\n", pid, errno); - fatal_sigsegv(); - } - wait_stub_done(pid); + err = ptrace(PTRACE_CONT, pid, 0, SIGSEGV); + if (err) { + printk(UM_KERN_ERR "Failed to continue stub, pid = %d, " + "errno = %d\n", pid, errno); + fatal_sigsegv(); + } + wait_stub_done(pid); - /* - * faultinfo is prepared by the stub-segv-handler at start of - * the stub stack page. We just have to copy it. - */ - memcpy(fi, (void *)current_stub_stack(), sizeof(*fi)); + /* + * faultinfo is prepared by the stub-segv-handler at start of + * the stub stack page. We just have to copy it. + */ + memcpy(fi, (void *)current_stub_stack(), sizeof(*fi)); - err = put_fp_registers(pid, fpregs); - if (err < 0) { - printk(UM_KERN_ERR "put_fp_registers returned %d\n", - err); - fatal_sigsegv(); - } + err = put_fp_registers(pid, fpregs); + if (err < 0) { + printk(UM_KERN_ERR "put_fp_registers returned %d\n", + err); + fatal_sigsegv(); } } @@ -198,7 +179,8 @@ extern int __syscall_stub_start; static int userspace_tramp(void *stack) { void *addr; - int err; + int err, fd; + unsigned long long offset; ptrace(PTRACE_TRACEME, 0, 0, 0); @@ -211,36 +193,32 @@ static int userspace_tramp(void *stack) exit(1); } - if (!proc_mm) { - /* - * This has a pte, but it can't be mapped in with the usual - * tlb_flush mechanism because this is part of that mechanism - */ - int fd; - unsigned long long offset; - fd = phys_mapping(to_phys(&__syscall_stub_start), &offset); - addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE, - PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset); + /* + * This has a pte, but it can't be mapped in with the usual + * tlb_flush mechanism because this is part of that mechanism + */ + fd = phys_mapping(to_phys(&__syscall_stub_start), &offset); + addr = mmap64((void *) STUB_CODE, UM_KERN_PAGE_SIZE, + PROT_EXEC, MAP_FIXED | MAP_PRIVATE, fd, offset); + if (addr == MAP_FAILED) { + printk(UM_KERN_ERR "mapping mmap stub at 0x%lx failed, " + "errno = %d\n", STUB_CODE, errno); + exit(1); + } + + if (stack != NULL) { + fd = phys_mapping(to_phys(stack), &offset); + addr = mmap((void *) STUB_DATA, + UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED, fd, offset); if (addr == MAP_FAILED) { - printk(UM_KERN_ERR "mapping mmap stub at 0x%lx failed, " - "errno = %d\n", STUB_CODE, errno); + printk(UM_KERN_ERR "mapping segfault stack " + "at 0x%lx failed, errno = %d\n", + STUB_DATA, errno); exit(1); } - - if (stack != NULL) { - fd = phys_mapping(to_phys(stack), &offset); - addr = mmap((void *) STUB_DATA, - UM_KERN_PAGE_SIZE, PROT_READ | PROT_WRITE, - MAP_FIXED | MAP_SHARED, fd, offset); - if (addr == MAP_FAILED) { - printk(UM_KERN_ERR "mapping segfault stack " - "at 0x%lx failed, errno = %d\n", - STUB_DATA, errno); - exit(1); - } - } } - if (!ptrace_faultinfo && (stack != NULL)) { + if (stack != NULL) { struct sigaction sa; unsigned long v = STUB_CODE + @@ -286,11 +264,7 @@ int start_userspace(unsigned long stub_stack) sp = (unsigned long) stack + UM_KERN_PAGE_SIZE - sizeof(void *); - flags = CLONE_FILES; - if (proc_mm) - flags |= CLONE_VM; - else - flags |= SIGCHLD; + flags = CLONE_FILES | SIGCHLD; pid = clone(userspace_tramp, (void *) sp, flags, (void *) stub_stack); if (pid < 0) { @@ -413,8 +387,7 @@ void userspace(struct uml_pt_regs *regs) switch (sig) { case SIGSEGV: - if (PTRACE_FULL_FAULTINFO || - !ptrace_faultinfo) { + if (PTRACE_FULL_FAULTINFO) { get_skas_faultinfo(pid, ®s->faultinfo); (*sig_info[SIGSEGV])(SIGSEGV, (struct siginfo *)&si, @@ -571,67 +544,6 @@ int copy_context_skas0(unsigned long new_stack, int pid) return err; } -/* - * This is used only, if stub pages are needed, while proc_mm is - * available. Opening /proc/mm creates a new mm_context, which lacks - * the stub-pages. Thus, we map them using /proc/mm-fd - */ -int map_stub_pages(int fd, unsigned long code, unsigned long data, - unsigned long stack) -{ - struct proc_mm_op mmop; - int n; - unsigned long long code_offset; - int code_fd = phys_mapping(to_phys((void *) &__syscall_stub_start), - &code_offset); - - mmop = ((struct proc_mm_op) { .op = MM_MMAP, - .u = - { .mmap = - { .addr = code, - .len = UM_KERN_PAGE_SIZE, - .prot = PROT_EXEC, - .flags = MAP_FIXED | MAP_PRIVATE, - .fd = code_fd, - .offset = code_offset - } } }); - CATCH_EINTR(n = write(fd, &mmop, sizeof(mmop))); - if (n != sizeof(mmop)) { - n = errno; - printk(UM_KERN_ERR "mmap args - addr = 0x%lx, fd = %d, " - "offset = %llx\n", code, code_fd, - (unsigned long long) code_offset); - printk(UM_KERN_ERR "map_stub_pages : /proc/mm map for code " - "failed, err = %d\n", n); - return -n; - } - - if (stack) { - unsigned long long map_offset; - int map_fd = phys_mapping(to_phys((void *)stack), &map_offset); - mmop = ((struct proc_mm_op) - { .op = MM_MMAP, - .u = - { .mmap = - { .addr = data, - .len = UM_KERN_PAGE_SIZE, - .prot = PROT_READ | PROT_WRITE, - .flags = MAP_FIXED | MAP_SHARED, - .fd = map_fd, - .offset = map_offset - } } }); - CATCH_EINTR(n = write(fd, &mmop, sizeof(mmop))); - if (n != sizeof(mmop)) { - n = errno; - printk(UM_KERN_ERR "map_stub_pages : /proc/mm map for " - "data failed, err = %d\n", n); - return -n; - } - } - - return 0; -} - void new_thread(void *stack, jmp_buf *buf, void (*handler)(void)) { (*buf)[0].JB_IP = (unsigned long) handler; @@ -674,7 +586,7 @@ int start_idle_thread(void *stack, jmp_buf *switch_buf) n = setjmp(initial_jmpbuf); switch (n) { case INIT_JMP_NEW_THREAD: - (*switch_buf)[0].JB_IP = (unsigned long) new_thread_handler; + (*switch_buf)[0].JB_IP = (unsigned long) uml_finishsetup; (*switch_buf)[0].JB_SP = (unsigned long) stack + UM_THREAD_SIZE - sizeof(void *); break; @@ -728,17 +640,5 @@ void reboot_skas(void) void __switch_mm(struct mm_id *mm_idp) { - int err; - - /* FIXME: need cpu pid in __switch_mm */ - if (proc_mm) { - err = ptrace(PTRACE_SWITCH_MM, userspace_pid[0], 0, - mm_idp->u.mm_fd); - if (err) { - printk(UM_KERN_ERR "__switch_mm - PTRACE_SWITCH_MM " - "failed, errno = %d\n", errno); - fatal_sigsegv(); - } - } - else userspace_pid[0] = mm_idp->u.pid; + userspace_pid[0] = mm_idp->u.pid; } diff --git a/arch/um/os-Linux/start_up.c b/arch/um/os-Linux/start_up.c index 337518c5042a..47f1ff056a54 100644 --- a/arch/um/os-Linux/start_up.c +++ b/arch/um/os-Linux/start_up.c @@ -24,7 +24,6 @@ #include <ptrace_user.h> #include <registers.h> #include <skas.h> -#include <skas_ptrace.h> static void ptrace_child(void) { @@ -143,44 +142,6 @@ static int stop_ptraced_child(int pid, int exitcode, int mustexit) } /* Changed only during early boot */ -int ptrace_faultinfo; -static int disable_ptrace_faultinfo; - -int ptrace_ldt; -static int disable_ptrace_ldt; - -int proc_mm; -static int disable_proc_mm; - -int have_switch_mm; -static int disable_switch_mm; - -int skas_needs_stub; - -static int __init skas0_cmd_param(char *str, int* add) -{ - disable_ptrace_faultinfo = 1; - disable_ptrace_ldt = 1; - disable_proc_mm = 1; - disable_switch_mm = 1; - - return 0; -} - -/* The two __uml_setup would conflict, without this stupid alias. */ - -static int __init mode_skas0_cmd_param(char *str, int* add) - __attribute__((alias("skas0_cmd_param"))); - -__uml_setup("skas0", skas0_cmd_param, -"skas0\n" -" Disables SKAS3 and SKAS4 usage, so that SKAS0 is used\n\n"); - -__uml_setup("mode=skas0", mode_skas0_cmd_param, -"mode=skas0\n" -" Disables SKAS3 and SKAS4 usage, so that SKAS0 is used.\n\n"); - -/* Changed only during early boot */ static int force_sysemu_disabled = 0; static int __init nosysemu_cmd_param(char *str, int* add) @@ -376,121 +337,6 @@ void __init os_early_checks(void) stop_ptraced_child(pid, 1, 1); } -static int __init noprocmm_cmd_param(char *str, int* add) -{ - disable_proc_mm = 1; - return 0; -} - -__uml_setup("noprocmm", noprocmm_cmd_param, -"noprocmm\n" -" Turns off usage of /proc/mm, even if host supports it.\n" -" To support /proc/mm, the host needs to be patched using\n" -" the current skas3 patch.\n\n"); - -static int __init noptracefaultinfo_cmd_param(char *str, int* add) -{ - disable_ptrace_faultinfo = 1; - return 0; -} - -__uml_setup("noptracefaultinfo", noptracefaultinfo_cmd_param, -"noptracefaultinfo\n" -" Turns off usage of PTRACE_FAULTINFO, even if host supports\n" -" it. To support PTRACE_FAULTINFO, the host needs to be patched\n" -" using the current skas3 patch.\n\n"); - -static int __init noptraceldt_cmd_param(char *str, int* add) -{ - disable_ptrace_ldt = 1; - return 0; -} - -__uml_setup("noptraceldt", noptraceldt_cmd_param, -"noptraceldt\n" -" Turns off usage of PTRACE_LDT, even if host supports it.\n" -" To support PTRACE_LDT, the host needs to be patched using\n" -" the current skas3 patch.\n\n"); - -static inline void check_skas3_ptrace_faultinfo(void) -{ - struct ptrace_faultinfo fi; - int pid, n; - - non_fatal(" - PTRACE_FAULTINFO..."); - pid = start_ptraced_child(); - - n = ptrace(PTRACE_FAULTINFO, pid, 0, &fi); - if (n < 0) { - if (errno == EIO) - non_fatal("not found\n"); - else - perror("not found"); - } else if (disable_ptrace_faultinfo) - non_fatal("found but disabled on command line\n"); - else { - ptrace_faultinfo = 1; - non_fatal("found\n"); - } - - stop_ptraced_child(pid, 1, 1); -} - -static inline void check_skas3_ptrace_ldt(void) -{ -#ifdef PTRACE_LDT - int pid, n; - unsigned char ldtbuf[40]; - struct ptrace_ldt ldt_op = (struct ptrace_ldt) { - .func = 2, /* read default ldt */ - .ptr = ldtbuf, - .bytecount = sizeof(ldtbuf)}; - - non_fatal(" - PTRACE_LDT..."); - pid = start_ptraced_child(); - - n = ptrace(PTRACE_LDT, pid, 0, (unsigned long) &ldt_op); - if (n < 0) { - if (errno == EIO) - non_fatal("not found\n"); - else - perror("not found"); - } else if (disable_ptrace_ldt) - non_fatal("found, but use is disabled\n"); - else { - ptrace_ldt = 1; - non_fatal("found\n"); - } - - stop_ptraced_child(pid, 1, 1); -#endif -} - -static inline void check_skas3_proc_mm(void) -{ - non_fatal(" - /proc/mm..."); - if (access("/proc/mm", W_OK) < 0) - perror("not found"); - else if (disable_proc_mm) - non_fatal("found but disabled on command line\n"); - else { - proc_mm = 1; - non_fatal("found\n"); - } -} - -void can_do_skas(void) -{ - non_fatal("Checking for the skas3 patch in the host:\n"); - - check_skas3_proc_mm(); - check_skas3_ptrace_faultinfo(); - check_skas3_ptrace_ldt(); - - if (!proc_mm || !ptrace_faultinfo || !ptrace_ldt) - skas_needs_stub = 1; -} - int __init parse_iomem(char *str, int *add) { struct iomem_region *new; diff --git a/arch/um/sys-ia64/Makefile b/arch/um/sys-ia64/Makefile deleted file mode 100644 index d02f4c265232..000000000000 --- a/arch/um/sys-ia64/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -OBJ = built-in.o - -OBJS = - -all: $(OBJ) - -$(OBJ): $(OBJS) - rm -f $@ - $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ - -clean-files := $(OBJS) link.ld diff --git a/arch/um/sys-ia64/sysdep/ptrace.h b/arch/um/sys-ia64/sysdep/ptrace.h deleted file mode 100644 index 0f0f4e6fd334..000000000000 --- a/arch/um/sys-ia64/sysdep/ptrace.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_IA64_PTRACE_H -#define __SYSDEP_IA64_PTRACE_H - -struct sys_pt_regs { - int foo; -}; - -#define EMPTY_REGS { 0 } - -#endif - diff --git a/arch/um/sys-ia64/sysdep/sigcontext.h b/arch/um/sys-ia64/sysdep/sigcontext.h deleted file mode 100644 index 76b43161e779..000000000000 --- a/arch/um/sys-ia64/sysdep/sigcontext.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_IA64_SIGCONTEXT_H -#define __SYSDEP_IA64_SIGCONTEXT_H - -#endif - diff --git a/arch/um/sys-ia64/sysdep/skas_ptrace.h b/arch/um/sys-ia64/sysdep/skas_ptrace.h deleted file mode 100644 index 25a38e715702..000000000000 --- a/arch/um/sys-ia64/sysdep/skas_ptrace.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_IA64_SKAS_PTRACE_H -#define __SYSDEP_IA64_SKAS_PTRACE_H - -struct ptrace_faultinfo { - int is_write; - unsigned long addr; -}; - -struct ptrace_ldt { - int func; - void *ptr; - unsigned long bytecount; -}; - -#define PTRACE_LDT 54 - -#endif diff --git a/arch/um/sys-ia64/sysdep/syscalls.h b/arch/um/sys-ia64/sysdep/syscalls.h deleted file mode 100644 index 5f6700c41558..000000000000 --- a/arch/um/sys-ia64/sysdep/syscalls.h +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_IA64_SYSCALLS_H -#define __SYSDEP_IA64_SYSCALLS_H - -#endif - diff --git a/arch/um/sys-ppc/Makefile b/arch/um/sys-ppc/Makefile deleted file mode 100644 index 20d363bd7004..000000000000 --- a/arch/um/sys-ppc/Makefile +++ /dev/null @@ -1,65 +0,0 @@ -OBJ = built-in.o - -.S.o: - $(CC) $(KBUILD_AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o - -OBJS = ptrace.o sigcontext.o checksum.o miscthings.o misc.o \ - ptrace_user.o sysrq.o - -asflags-y := -DCONFIG_PPC32 -I. -I$(srctree)/arch/ppc/kernel - -all: $(OBJ) - -$(OBJ): $(OBJS) - rm -f $@ - $(LD) $(LINKFLAGS) --start-group $^ --end-group -o $@ - -ptrace_user.o: ptrace_user.c - $(CC) -D__KERNEL__ $(USER_CFLAGS) $(ccflags-y) -c -o $@ $< - -sigcontext.o: sigcontext.c - $(CC) $(USER_CFLAGS) $(ccflags-y) -c -o $@ $< - -checksum.S: - rm -f $@ - ln -s $(srctree)/arch/ppc/lib/$@ $@ - -mk_defs.c: - rm -f $@ - ln -s $(srctree)/arch/ppc/kernel/$@ $@ - -ppc_defs.head: - rm -f $@ - ln -s $(srctree)/arch/ppc/kernel/$@ $@ - -ppc_defs.h: mk_defs.c ppc_defs.head \ - $(srctree)/include/asm-ppc/mmu.h \ - $(srctree)/include/asm-ppc/processor.h \ - $(srctree)/include/asm-ppc/pgtable.h \ - $(srctree)/include/asm-ppc/ptrace.h -# $(CC) $(CFLAGS) -S mk_defs.c - cp ppc_defs.head ppc_defs.h -# for bk, this way we can write to the file even if it's not checked out - echo '#define THREAD 608' >> ppc_defs.h - echo '#define PT_REGS 8' >> ppc_defs.h - echo '#define CLONE_VM 256' >> ppc_defs.h -# chmod u+w ppc_defs.h -# grep '^#define' mk_defs.s >> ppc_defs.h -# rm mk_defs.s - -# the asm link is horrible, and breaks the other targets. This is also -# not going to work with parallel makes. - -checksum.o: checksum.S - rm -f asm - ln -s $(srctree)/include/asm-ppc asm - $(CC) $(asflags-y) $(KBUILD_AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o - rm -f asm - -misc.o: misc.S ppc_defs.h - rm -f asm - ln -s $(srctree)/include/asm-ppc asm - $(CC) $(asflags-y) $(KBUILD_AFLAGS) -D__ASSEMBLY__ -D__UM_PPC__ -c $< -o $*.o - rm -f asm - -clean-files := $(OBJS) ppc_defs.h checksum.S mk_defs.c diff --git a/arch/um/sys-ppc/asm/archparam.h b/arch/um/sys-ppc/asm/archparam.h deleted file mode 100644 index 4269d8a37b4f..000000000000 --- a/arch/um/sys-ppc/asm/archparam.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __UM_ARCHPARAM_PPC_H -#define __UM_ARCHPARAM_PPC_H - -/********* Bits for asm-um/string.h **********/ - -#define __HAVE_ARCH_STRRCHR - -#endif diff --git a/arch/um/sys-ppc/asm/elf.h b/arch/um/sys-ppc/asm/elf.h deleted file mode 100644 index 8aacaf56508d..000000000000 --- a/arch/um/sys-ppc/asm/elf.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef __UM_ELF_PPC_H -#define __UM_ELF_PPC_H - - -extern long elf_aux_hwcap; -#define ELF_HWCAP (elf_aux_hwcap) - -#define SET_PERSONALITY(ex) do ; while(0) - -#define ELF_EXEC_PAGESIZE 4096 - -#define elf_check_arch(x) (1) - -#ifdef CONFIG_64BIT -#define ELF_CLASS ELFCLASS64 -#else -#define ELF_CLASS ELFCLASS32 -#endif - -#define R_386_NONE 0 -#define R_386_32 1 -#define R_386_PC32 2 -#define R_386_GOT32 3 -#define R_386_PLT32 4 -#define R_386_COPY 5 -#define R_386_GLOB_DAT 6 -#define R_386_JMP_SLOT 7 -#define R_386_RELATIVE 8 -#define R_386_GOTOFF 9 -#define R_386_GOTPC 10 -#define R_386_NUM 11 - -#define ELF_PLATFORM (0) - -#define ELF_ET_DYN_BASE (0x08000000) - -/* the following stolen from asm-ppc/elf.h */ -#define ELF_NGREG 48 /* includes nip, msr, lr, etc. */ -#define ELF_NFPREG 33 /* includes fpscr */ -/* General registers */ -typedef unsigned long elf_greg_t; -typedef elf_greg_t elf_gregset_t[ELF_NGREG]; - -/* Floating point registers */ -typedef double elf_fpreg_t; -typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG]; - -#define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_PPC - -#endif diff --git a/arch/um/sys-ppc/asm/processor.h b/arch/um/sys-ppc/asm/processor.h deleted file mode 100644 index 959323151229..000000000000 --- a/arch/um/sys-ppc/asm/processor.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __UM_PROCESSOR_PPC_H -#define __UM_PROCESSOR_PPC_H - -#if defined(__ASSEMBLY__) - -#define CONFIG_PPC_MULTIPLATFORM -#include "arch/processor.h" - -#else - -#include "asm/processor-generic.h" - -#endif - -#endif diff --git a/arch/um/sys-ppc/misc.S b/arch/um/sys-ppc/misc.S deleted file mode 100644 index 1364b7da578c..000000000000 --- a/arch/um/sys-ppc/misc.S +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file contains miscellaneous low-level functions. - * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) - * - * Largely rewritten by Cort Dougan (cort@cs.nmt.edu) - * and Paul Mackerras. - * - * A couple of functions stolen from arch/ppc/kernel/misc.S for UML - * by Chris Emerson. - * - * 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 <asm/processor.h> -#include "ppc_asm.h" - -#if defined(CONFIG_4xx) || defined(CONFIG_8xx) -#define CACHE_LINE_SIZE 16 -#define LG_CACHE_LINE_SIZE 4 -#define MAX_COPY_PREFETCH 1 -#else -#define CACHE_LINE_SIZE 32 -#define LG_CACHE_LINE_SIZE 5 -#define MAX_COPY_PREFETCH 4 -#endif /* CONFIG_4xx || CONFIG_8xx */ - - .text - -/* - * Clear a page using the dcbz instruction, which doesn't cause any - * memory traffic (except to write out any cache lines which get - * displaced). This only works on cacheable memory. - */ -_GLOBAL(clear_page) - li r0,4096/CACHE_LINE_SIZE - mtctr r0 -#ifdef CONFIG_8xx - li r4, 0 -1: stw r4, 0(r3) - stw r4, 4(r3) - stw r4, 8(r3) - stw r4, 12(r3) -#else -1: dcbz 0,r3 -#endif - addi r3,r3,CACHE_LINE_SIZE - bdnz 1b - blr - -/* - * Copy a whole page. We use the dcbz instruction on the destination - * to reduce memory traffic (it eliminates the unnecessary reads of - * the destination into cache). This requires that the destination - * is cacheable. - */ -#define COPY_16_BYTES \ - lwz r6,4(r4); \ - lwz r7,8(r4); \ - lwz r8,12(r4); \ - lwzu r9,16(r4); \ - stw r6,4(r3); \ - stw r7,8(r3); \ - stw r8,12(r3); \ - stwu r9,16(r3) - -_GLOBAL(copy_page) - addi r3,r3,-4 - addi r4,r4,-4 - li r5,4 - -#ifndef CONFIG_8xx -#if MAX_COPY_PREFETCH > 1 - li r0,MAX_COPY_PREFETCH - li r11,4 - mtctr r0 -11: dcbt r11,r4 - addi r11,r11,CACHE_LINE_SIZE - bdnz 11b -#else /* MAX_COPY_PREFETCH == 1 */ - dcbt r5,r4 - li r11,CACHE_LINE_SIZE+4 -#endif /* MAX_COPY_PREFETCH */ -#endif /* CONFIG_8xx */ - - li r0,4096/CACHE_LINE_SIZE - mtctr r0 -1: -#ifndef CONFIG_8xx - dcbt r11,r4 - dcbz r5,r3 -#endif - COPY_16_BYTES -#if CACHE_LINE_SIZE >= 32 - COPY_16_BYTES -#if CACHE_LINE_SIZE >= 64 - COPY_16_BYTES - COPY_16_BYTES -#if CACHE_LINE_SIZE >= 128 - COPY_16_BYTES - COPY_16_BYTES - COPY_16_BYTES - COPY_16_BYTES -#endif -#endif -#endif - bdnz 1b - blr diff --git a/arch/um/sys-ppc/miscthings.c b/arch/um/sys-ppc/miscthings.c deleted file mode 100644 index 25908d26ce07..000000000000 --- a/arch/um/sys-ppc/miscthings.c +++ /dev/null @@ -1,42 +0,0 @@ -#include <linux/threads.h> -#include <linux/stddef.h> // for NULL -#include <linux/elf.h> // for AT_NULL - -/* The following function nicked from arch/ppc/kernel/process.c and - * adapted slightly */ -/* - * XXX ld.so expects the auxiliary table to start on - * a 16-byte boundary, so we have to find it and - * move it up. :-( - */ -void shove_aux_table(unsigned long sp) -{ - int argc; - char *p; - unsigned long e; - unsigned long aux_start, offset; - - argc = *(int *)sp; - sp += sizeof(int) + (argc + 1) * sizeof(char *); - /* skip over the environment pointers */ - do { - p = *(char **)sp; - sp += sizeof(char *); - } while (p != NULL); - aux_start = sp; - /* skip to the end of the auxiliary table */ - do { - e = *(unsigned long *)sp; - sp += 2 * sizeof(unsigned long); - } while (e != AT_NULL); - offset = ((aux_start + 15) & ~15) - aux_start; - if (offset != 0) { - do { - sp -= sizeof(unsigned long); - e = *(unsigned long *)sp; - *(unsigned long *)(sp + offset) = e; - } while (sp > aux_start); - } -} -/* END stuff taken from arch/ppc/kernel/process.c */ - diff --git a/arch/um/sys-ppc/ptrace.c b/arch/um/sys-ppc/ptrace.c deleted file mode 100644 index 8245df41b201..000000000000 --- a/arch/um/sys-ppc/ptrace.c +++ /dev/null @@ -1,58 +0,0 @@ -#include <linux/sched.h> -#include "asm/ptrace.h" - -int putreg(struct task_struct *child, unsigned long regno, - unsigned long value) -{ - child->thread.process_regs.regs[regno >> 2] = value; - return 0; -} - -int poke_user(struct task_struct *child, long addr, long data) -{ - if ((addr & 3) || addr < 0) - return -EIO; - - if (addr < MAX_REG_OFFSET) - return putreg(child, addr, data); - - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - if((addr == 4) || (addr == 5)) return -EIO; - child->thread.arch.debugregs[addr] = data; - return 0; - } - return -EIO; -} - -unsigned long getreg(struct task_struct *child, unsigned long regno) -{ - unsigned long retval = ~0UL; - - retval &= child->thread.process_regs.regs[regno >> 2]; - return retval; -} - -int peek_user(struct task_struct *child, long addr, long data) -{ - /* read the word at location addr in the USER area. */ - unsigned long tmp; - - if ((addr & 3) || addr < 0) - return -EIO; - - tmp = 0; /* Default return condition */ - if(addr < MAX_REG_OFFSET){ - tmp = getreg(child, addr); - } - else if((addr >= offsetof(struct user, u_debugreg[0])) && - (addr <= offsetof(struct user, u_debugreg[7]))){ - addr -= offsetof(struct user, u_debugreg[0]); - addr = addr >> 2; - tmp = child->thread.arch.debugregs[addr]; - } - return put_user(tmp, (unsigned long *) data); -} - diff --git a/arch/um/sys-ppc/ptrace_user.c b/arch/um/sys-ppc/ptrace_user.c deleted file mode 100644 index 4601b9296aa7..000000000000 --- a/arch/um/sys-ppc/ptrace_user.c +++ /dev/null @@ -1,29 +0,0 @@ -#include <errno.h> -#include <asm/ptrace.h> -#include <sysdep/ptrace.h> - -int ptrace_getregs(long pid, unsigned long *regs_out) -{ - int i; - for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { - errno = 0; - regs_out->regs[i] = ptrace(PTRACE_PEEKUSR, pid, i*4, 0); - if (errno) { - return -errno; - } - } - return 0; -} - -int ptrace_setregs(long pid, unsigned long *regs_in) -{ - int i; - for (i=0; i < sizeof(struct sys_pt_regs)/sizeof(PPC_REG); ++i) { - if (i != 34 /* FIXME: PT_ORIG_R3 */ && i <= PT_MQ) { - if (ptrace(PTRACE_POKEUSR, pid, i*4, regs_in->regs[i]) < 0) { - return -errno; - } - } - } - return 0; -} diff --git a/arch/um/sys-ppc/shared/sysdep/ptrace.h b/arch/um/sys-ppc/shared/sysdep/ptrace.h deleted file mode 100644 index efe0c1a3ea9c..000000000000 --- a/arch/um/sys-ppc/shared/sysdep/ptrace.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Licensed under the GPL - */ - -#ifndef __SYS_PTRACE_PPC_H -#define __SYS_PTRACE_PPC_H - -#include <linux/types.h> - -/* the following taken from <asm-ppc/ptrace.h> */ - -#ifdef CONFIG_PPC64 -#define PPC_REG unsigned long /*long*/ -#else -#define PPC_REG unsigned long -#endif -struct sys_pt_regs_s { - PPC_REG gpr[32]; - PPC_REG nip; - PPC_REG msr; - PPC_REG orig_gpr3; /* Used for restarting system calls */ - PPC_REG ctr; - PPC_REG link; - PPC_REG xer; - PPC_REG ccr; - PPC_REG mq; /* 601 only (not used at present) */ - /* Used on APUS to hold IPL value. */ - PPC_REG trap; /* Reason for being here */ - PPC_REG dar; /* Fault registers */ - PPC_REG dsisr; - PPC_REG result; /* Result of a system call */ -}; - -#define NUM_REGS (sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)) - -struct sys_pt_regs { - PPC_REG regs[sizeof(struct sys_pt_regs_s) / sizeof(PPC_REG)]; -}; - -#define UM_MAX_REG (PT_FPR0) -#define UM_MAX_REG_OFFSET (UM_MAX_REG * sizeof(PPC_REG)) - -#define EMPTY_REGS { { [ 0 ... NUM_REGS - 1] = 0 } } - -#define UM_REG(r, n) ((r)->regs[n]) - -#define UM_SYSCALL_RET(r) UM_REG(r, PT_R3) -#define UM_SP(r) UM_REG(r, PT_R1) -#define UM_IP(r) UM_REG(r, PT_NIP) -#define UM_ELF_ZERO(r) UM_REG(r, PT_FPSCR) -#define UM_SYSCALL_NR(r) UM_REG(r, PT_R0) -#define UM_SYSCALL_ARG1(r) UM_REG(r, PT_ORIG_R3) -#define UM_SYSCALL_ARG2(r) UM_REG(r, PT_R4) -#define UM_SYSCALL_ARG3(r) UM_REG(r, PT_R5) -#define UM_SYSCALL_ARG4(r) UM_REG(r, PT_R6) -#define UM_SYSCALL_ARG5(r) UM_REG(r, PT_R7) -#define UM_SYSCALL_ARG6(r) UM_REG(r, PT_R8) - -#define UM_SYSCALL_NR_OFFSET (PT_R0 * sizeof(PPC_REG)) -#define UM_SYSCALL_RET_OFFSET (PT_R3 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG1_OFFSET (PT_R3 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG2_OFFSET (PT_R4 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG3_OFFSET (PT_R5 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG4_OFFSET (PT_R6 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG5_OFFSET (PT_R7 * sizeof(PPC_REG)) -#define UM_SYSCALL_ARG6_OFFSET (PT_R8 * sizeof(PPC_REG)) -#define UM_SP_OFFSET (PT_R1 * sizeof(PPC_REG)) -#define UM_IP_OFFSET (PT_NIP * sizeof(PPC_REG)) -#define UM_ELF_ZERO_OFFSET (PT_R3 * sizeof(PPC_REG)) - -#define UM_SET_SYSCALL_RETURN(_regs, result) \ -do { \ - if (result < 0) { \ - (_regs)->regs[PT_CCR] |= 0x10000000; \ - UM_SYSCALL_RET((_regs)) = -result; \ - } else { \ - UM_SYSCALL_RET((_regs)) = result; \ - } \ -} while(0) - -extern void shove_aux_table(unsigned long sp); -#define UM_FIX_EXEC_STACK(sp) shove_aux_table(sp); - -/* These aren't actually defined. The undefs are just to make sure - * everyone's clear on the concept. - */ -#undef UML_HAVE_GETREGS -#undef UML_HAVE_GETFPREGS -#undef UML_HAVE_SETREGS -#undef UML_HAVE_SETFPREGS - -#endif - diff --git a/arch/um/sys-ppc/shared/sysdep/sigcontext.h b/arch/um/sys-ppc/shared/sysdep/sigcontext.h deleted file mode 100644 index b7286f0a1e00..000000000000 --- a/arch/um/sys-ppc/shared/sysdep/sigcontext.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYS_SIGCONTEXT_PPC_H -#define __SYS_SIGCONTEXT_PPC_H - -#define DSISR_WRITE 0x02000000 - -#define SC_FAULT_ADDR(sc) ({ \ - struct sigcontext *_sc = (sc); \ - long retval = -1; \ - switch (_sc->regs->trap) { \ - case 0x300: \ - /* data exception */ \ - retval = _sc->regs->dar; \ - break; \ - case 0x400: \ - /* instruction exception */ \ - retval = _sc->regs->nip; \ - break; \ - default: \ - panic("SC_FAULT_ADDR: unhandled trap type\n"); \ - } \ - retval; \ - }) - -#define SC_FAULT_WRITE(sc) ({ \ - struct sigcontext *_sc = (sc); \ - long retval = -1; \ - switch (_sc->regs->trap) { \ - case 0x300: \ - /* data exception */ \ - retval = !!(_sc->regs->dsisr & DSISR_WRITE); \ - break; \ - case 0x400: \ - /* instruction exception: not a write */ \ - retval = 0; \ - break; \ - default: \ - panic("SC_FAULT_ADDR: unhandled trap type\n"); \ - } \ - retval; \ - }) - -#define SC_IP(sc) ((sc)->regs->nip) -#define SC_SP(sc) ((sc)->regs->gpr[1]) -#define SEGV_IS_FIXABLE(sc) (1) - -#endif - diff --git a/arch/um/sys-ppc/shared/sysdep/skas_ptrace.h b/arch/um/sys-ppc/shared/sysdep/skas_ptrace.h deleted file mode 100644 index d9fbbac10de0..000000000000 --- a/arch/um/sys-ppc/shared/sysdep/skas_ptrace.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_PPC_SKAS_PTRACE_H -#define __SYSDEP_PPC_SKAS_PTRACE_H - -struct ptrace_faultinfo { - int is_write; - unsigned long addr; -}; - -struct ptrace_ldt { - int func; - void *ptr; - unsigned long bytecount; -}; - -#define PTRACE_LDT 54 - -#endif diff --git a/arch/um/sys-ppc/shared/sysdep/syscalls.h b/arch/um/sys-ppc/shared/sysdep/syscalls.h deleted file mode 100644 index 1ff81552251c..000000000000 --- a/arch/um/sys-ppc/shared/sysdep/syscalls.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -typedef long syscall_handler_t(unsigned long arg1, unsigned long arg2, - unsigned long arg3, unsigned long arg4, - unsigned long arg5, unsigned long arg6); - -#define EXECUTE_SYSCALL(syscall, regs) \ - (*sys_call_table[syscall])(UM_SYSCALL_ARG1(®s), \ - UM_SYSCALL_ARG2(®s), \ - UM_SYSCALL_ARG3(®s), \ - UM_SYSCALL_ARG4(®s), \ - UM_SYSCALL_ARG5(®s), \ - UM_SYSCALL_ARG6(®s)) - -extern syscall_handler_t sys_mincore; -extern syscall_handler_t sys_madvise; - -/* old_mmap needs the correct prototype since syscall_kern.c includes - * this file. - */ -int old_mmap(unsigned long addr, unsigned long len, - unsigned long prot, unsigned long flags, - unsigned long fd, unsigned long offset); - -#define ARCH_SYSCALLS \ - [ __NR_modify_ldt ] = sys_ni_syscall, \ - [ __NR_pciconfig_read ] = sys_ni_syscall, \ - [ __NR_pciconfig_write ] = sys_ni_syscall, \ - [ __NR_pciconfig_iobase ] = sys_ni_syscall, \ - [ __NR_pivot_root ] = sys_ni_syscall, \ - [ __NR_multiplexer ] = sys_ni_syscall, \ - [ __NR_mmap ] = old_mmap, \ - [ __NR_madvise ] = sys_madvise, \ - [ __NR_mincore ] = sys_mincore, \ - [ __NR_iopl ] = (syscall_handler_t *) sys_ni_syscall, \ - [ __NR_utimes ] = (syscall_handler_t *) sys_utimes, \ - [ __NR_fadvise64 ] = (syscall_handler_t *) sys_fadvise64, - -#define LAST_ARCH_SYSCALL __NR_fadvise64 - diff --git a/arch/um/sys-ppc/sigcontext.c b/arch/um/sys-ppc/sigcontext.c deleted file mode 100644 index aac6c83fe44e..000000000000 --- a/arch/um/sys-ppc/sigcontext.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "asm/ptrace.h" -#include "asm/sigcontext.h" -#include <sysdep/ptrace.h> - diff --git a/arch/um/sys-ppc/sysrq.c b/arch/um/sys-ppc/sysrq.c deleted file mode 100644 index 1ff1ad7f27da..000000000000 --- a/arch/um/sys-ppc/sysrq.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2001 Chris Emerson (cemerson@chiark.greenend.org.uk) - * Licensed under the GPL - */ - -#include <linux/kernel.h> -#include <linux/smp.h> -#include "asm/ptrace.h" -#include "sysrq.h" - -void show_regs(struct pt_regs_subarch *regs) -{ - printk("\n"); - show_regs_print_info(KERN_DEFAULT); - - printk("show_regs(): insert regs here.\n"); -#if 0 - printk("\n"); - printk("EIP: %04x:[<%08lx>] CPU: %d",0xffff & regs->xcs, regs->eip, - smp_processor_id()); - if (regs->xcs & 3) - printk(" ESP: %04x:%08lx",0xffff & regs->xss, regs->esp); - printk(" EFLAGS: %08lx\n", regs->eflags); - printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", - regs->eax, regs->ebx, regs->ecx, regs->edx); - printk("ESI: %08lx EDI: %08lx EBP: %08lx", - regs->esi, regs->edi, regs->ebp); - printk(" DS: %04x ES: %04x\n", - 0xffff & regs->xds, 0xffff & regs->xes); -#endif - - show_trace(current, ®s->gpr[1]); -} diff --git a/arch/unicore32/include/asm/thread_info.h b/arch/unicore32/include/asm/thread_info.h index 63e2839dfeb8..e79ad6d5b5b2 100644 --- a/arch/unicore32/include/asm/thread_info.h +++ b/arch/unicore32/include/asm/thread_info.h @@ -24,7 +24,6 @@ #ifndef __ASSEMBLY__ struct task_struct; -struct exec_domain; #include <asm/types.h> @@ -71,7 +70,6 @@ struct thread_info { /* <0 => bug */ mm_segment_t addr_limit; /* address limit */ struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ __u32 cpu; /* cpu */ struct cpu_context_save cpu_context; /* cpu context */ __u32 syscall; /* syscall number */ @@ -84,7 +82,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ .addr_limit = KERNEL_DS, \ diff --git a/arch/unicore32/kernel/asm-offsets.c b/arch/unicore32/kernel/asm-offsets.c index ffcbe7536ca7..80d50c4651e3 100644 --- a/arch/unicore32/kernel/asm-offsets.c +++ b/arch/unicore32/kernel/asm-offsets.c @@ -42,7 +42,6 @@ int main(void) DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); - DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context)); DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp)); diff --git a/arch/unicore32/kernel/signal.c b/arch/unicore32/kernel/signal.c index d329f85766cc..4ae51cf15ade 100644 --- a/arch/unicore32/kernel/signal.c +++ b/arch/unicore32/kernel/signal.c @@ -330,13 +330,6 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs, } /* - * translate the signal - */ - if (usig < 32 && thread->exec_domain - && thread->exec_domain->signal_invmap) - usig = thread->exec_domain->signal_invmap[usig]; - - /* * Set up the stack frame */ if (ksig->ka.sa.sa_flags & SA_SIGINFO) diff --git a/arch/x86/Makefile b/arch/x86/Makefile index 5ba2d9ce82dc..2fda005bb334 100644 --- a/arch/x86/Makefile +++ b/arch/x86/Makefile @@ -63,7 +63,7 @@ ifeq ($(CONFIG_X86_32),y) $(call cc-option,-fno-unit-at-a-time)) # CPU-specific tuning. Anything which can be shared with UML should go here. - include $(srctree)/arch/x86/Makefile_32.cpu + include arch/x86/Makefile_32.cpu KBUILD_CFLAGS += $(cflags-y) # temporary until string.h is fixed diff --git a/arch/x86/Makefile.um b/arch/x86/Makefile.um index 95eba554baf9..5b7e898ffd9a 100644 --- a/arch/x86/Makefile.um +++ b/arch/x86/Makefile.um @@ -18,7 +18,7 @@ LDS_EXTRA := -Ui386 export LDS_EXTRA # First of all, tune CFLAGS for the specific CPU. This actually sets cflags-y. -include $(srctree)/arch/x86/Makefile_32.cpu +include arch/x86/Makefile_32.cpu # prevent gcc from keeping the stack 16 byte aligned. Taken from i386. cflags-y += $(call cc-option,-mpreferred-stack-boundary=2) diff --git a/arch/x86/crypto/aesni-intel_glue.c b/arch/x86/crypto/aesni-intel_glue.c index 54f60ab41c63..112cefacf2af 100644 --- a/arch/x86/crypto/aesni-intel_glue.c +++ b/arch/x86/crypto/aesni-intel_glue.c @@ -797,7 +797,9 @@ static int rfc4106_init(struct crypto_tfm *tfm) PTR_ALIGN((u8 *)crypto_tfm_ctx(tfm), AESNI_ALIGN); struct crypto_aead *cryptd_child; struct aesni_rfc4106_gcm_ctx *child_ctx; - cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni", 0, 0); + cryptd_tfm = cryptd_alloc_aead("__driver-gcm-aes-aesni", + CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); if (IS_ERR(cryptd_tfm)) return PTR_ERR(cryptd_tfm); @@ -890,15 +892,12 @@ out_free_ablkcipher: return ret; } -static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, - unsigned int key_len) +static int common_rfc4106_set_key(struct crypto_aead *aead, const u8 *key, + unsigned int key_len) { int ret = 0; - struct crypto_tfm *tfm = crypto_aead_tfm(parent); - struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); - struct aesni_rfc4106_gcm_ctx *child_ctx = - aesni_rfc4106_gcm_ctx_get(cryptd_child); + struct crypto_tfm *tfm = crypto_aead_tfm(aead); + struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(aead); u8 *new_key_align, *new_key_mem = NULL; if (key_len < 4) { @@ -943,20 +942,31 @@ static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, goto exit; } ret = rfc4106_set_hash_subkey(ctx->hash_subkey, key, key_len); - memcpy(child_ctx, ctx, sizeof(*ctx)); exit: kfree(new_key_mem); return ret; } -/* This is the Integrity Check Value (aka the authentication tag length and can - * be 8, 12 or 16 bytes long. */ -static int rfc4106_set_authsize(struct crypto_aead *parent, - unsigned int authsize) +static int rfc4106_set_key(struct crypto_aead *parent, const u8 *key, + unsigned int key_len) { struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent); - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); + struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm); + struct aesni_rfc4106_gcm_ctx *c_ctx = aesni_rfc4106_gcm_ctx_get(child); + struct cryptd_aead *cryptd_tfm = ctx->cryptd_tfm; + int ret; + ret = crypto_aead_setkey(child, key, key_len); + if (!ret) { + memcpy(ctx, c_ctx, sizeof(*ctx)); + ctx->cryptd_tfm = cryptd_tfm; + } + return ret; +} + +static int common_rfc4106_set_authsize(struct crypto_aead *aead, + unsigned int authsize) +{ switch (authsize) { case 8: case 12: @@ -965,51 +975,23 @@ static int rfc4106_set_authsize(struct crypto_aead *parent, default: return -EINVAL; } - crypto_aead_crt(parent)->authsize = authsize; - crypto_aead_crt(cryptd_child)->authsize = authsize; + crypto_aead_crt(aead)->authsize = authsize; return 0; } -static int rfc4106_encrypt(struct aead_request *req) -{ - int ret; - struct crypto_aead *tfm = crypto_aead_reqtfm(req); - struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - - if (!irq_fpu_usable()) { - struct aead_request *cryptd_req = - (struct aead_request *) aead_request_ctx(req); - memcpy(cryptd_req, req, sizeof(*req)); - aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); - return crypto_aead_encrypt(cryptd_req); - } else { - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); - kernel_fpu_begin(); - ret = cryptd_child->base.crt_aead.encrypt(req); - kernel_fpu_end(); - return ret; - } -} - -static int rfc4106_decrypt(struct aead_request *req) +/* This is the Integrity Check Value (aka the authentication tag length and can + * be 8, 12 or 16 bytes long. */ +static int rfc4106_set_authsize(struct crypto_aead *parent, + unsigned int authsize) { + struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(parent); + struct crypto_aead *child = cryptd_aead_child(ctx->cryptd_tfm); int ret; - struct crypto_aead *tfm = crypto_aead_reqtfm(req); - struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); - if (!irq_fpu_usable()) { - struct aead_request *cryptd_req = - (struct aead_request *) aead_request_ctx(req); - memcpy(cryptd_req, req, sizeof(*req)); - aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); - return crypto_aead_decrypt(cryptd_req); - } else { - struct crypto_aead *cryptd_child = cryptd_aead_child(ctx->cryptd_tfm); - kernel_fpu_begin(); - ret = cryptd_child->base.crt_aead.decrypt(req); - kernel_fpu_end(); - return ret; - } + ret = crypto_aead_setauthsize(child, authsize); + if (!ret) + crypto_aead_crt(parent)->authsize = authsize; + return ret; } static int __driver_rfc4106_encrypt(struct aead_request *req) @@ -1185,6 +1167,78 @@ static int __driver_rfc4106_decrypt(struct aead_request *req) } return retval; } + +static int rfc4106_encrypt(struct aead_request *req) +{ + int ret; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); + + if (!irq_fpu_usable()) { + struct aead_request *cryptd_req = + (struct aead_request *) aead_request_ctx(req); + + memcpy(cryptd_req, req, sizeof(*req)); + aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); + ret = crypto_aead_encrypt(cryptd_req); + } else { + kernel_fpu_begin(); + ret = __driver_rfc4106_encrypt(req); + kernel_fpu_end(); + } + return ret; +} + +static int rfc4106_decrypt(struct aead_request *req) +{ + int ret; + struct crypto_aead *tfm = crypto_aead_reqtfm(req); + struct aesni_rfc4106_gcm_ctx *ctx = aesni_rfc4106_gcm_ctx_get(tfm); + + if (!irq_fpu_usable()) { + struct aead_request *cryptd_req = + (struct aead_request *) aead_request_ctx(req); + + memcpy(cryptd_req, req, sizeof(*req)); + aead_request_set_tfm(cryptd_req, &ctx->cryptd_tfm->base); + ret = crypto_aead_decrypt(cryptd_req); + } else { + kernel_fpu_begin(); + ret = __driver_rfc4106_decrypt(req); + kernel_fpu_end(); + } + return ret; +} + +static int helper_rfc4106_encrypt(struct aead_request *req) +{ + int ret; + + if (unlikely(!irq_fpu_usable())) { + WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context"); + ret = -EINVAL; + } else { + kernel_fpu_begin(); + ret = __driver_rfc4106_encrypt(req); + kernel_fpu_end(); + } + return ret; +} + +static int helper_rfc4106_decrypt(struct aead_request *req) +{ + int ret; + + if (unlikely(!irq_fpu_usable())) { + WARN_ONCE(1, "__gcm-aes-aesni alg used in invalid context"); + ret = -EINVAL; + } else { + kernel_fpu_begin(); + ret = __driver_rfc4106_decrypt(req); + kernel_fpu_end(); + } + return ret; +} #endif static struct crypto_alg aesni_algs[] = { { @@ -1210,7 +1264,7 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__aes-aesni", .cra_driver_name = "__driver-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_CIPHER, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1, @@ -1229,7 +1283,8 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__ecb-aes-aesni", .cra_driver_name = "__driver-ecb-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1, @@ -1249,7 +1304,8 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__cbc-aes-aesni", .cra_driver_name = "__driver-cbc-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1, @@ -1313,7 +1369,8 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__ctr-aes-aesni", .cra_driver_name = "__driver-ctr-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct crypto_aes_ctx) + AESNI_ALIGN - 1, @@ -1357,7 +1414,7 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__gcm-aes-aesni", .cra_driver_name = "__driver-gcm-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_AEAD, + .cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct aesni_rfc4106_gcm_ctx) + AESNI_ALIGN, @@ -1366,8 +1423,12 @@ static struct crypto_alg aesni_algs[] = { { .cra_module = THIS_MODULE, .cra_u = { .aead = { - .encrypt = __driver_rfc4106_encrypt, - .decrypt = __driver_rfc4106_decrypt, + .setkey = common_rfc4106_set_key, + .setauthsize = common_rfc4106_set_authsize, + .encrypt = helper_rfc4106_encrypt, + .decrypt = helper_rfc4106_decrypt, + .ivsize = 8, + .maxauthsize = 16, }, }, }, { @@ -1423,7 +1484,8 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__lrw-aes-aesni", .cra_driver_name = "__driver-lrw-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aesni_lrw_ctx), .cra_alignmask = 0, @@ -1444,7 +1506,8 @@ static struct crypto_alg aesni_algs[] = { { .cra_name = "__xts-aes-aesni", .cra_driver_name = "__driver-xts-aes-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = AES_BLOCK_SIZE, .cra_ctxsize = sizeof(struct aesni_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/camellia_aesni_avx2_glue.c b/arch/x86/crypto/camellia_aesni_avx2_glue.c index 9a07fafe3831..baf0ac21ace5 100644 --- a/arch/x86/crypto/camellia_aesni_avx2_glue.c +++ b/arch/x86/crypto/camellia_aesni_avx2_glue.c @@ -343,7 +343,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__ecb-camellia-aesni-avx2", .cra_driver_name = "__driver-ecb-camellia-aesni-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -362,7 +363,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__cbc-camellia-aesni-avx2", .cra_driver_name = "__driver-cbc-camellia-aesni-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -381,7 +383,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__ctr-camellia-aesni-avx2", .cra_driver_name = "__driver-ctr-camellia-aesni-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -401,7 +404,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__lrw-camellia-aesni-avx2", .cra_driver_name = "__driver-lrw-camellia-aesni-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_lrw_ctx), .cra_alignmask = 0, @@ -424,7 +428,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__xts-camellia-aesni-avx2", .cra_driver_name = "__driver-xts-camellia-aesni-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/camellia_aesni_avx_glue.c b/arch/x86/crypto/camellia_aesni_avx_glue.c index ed38d959add6..78818a1e73e3 100644 --- a/arch/x86/crypto/camellia_aesni_avx_glue.c +++ b/arch/x86/crypto/camellia_aesni_avx_glue.c @@ -335,7 +335,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__ecb-camellia-aesni", .cra_driver_name = "__driver-ecb-camellia-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -354,7 +355,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__cbc-camellia-aesni", .cra_driver_name = "__driver-cbc-camellia-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -373,7 +375,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__ctr-camellia-aesni", .cra_driver_name = "__driver-ctr-camellia-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct camellia_ctx), .cra_alignmask = 0, @@ -393,7 +396,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__lrw-camellia-aesni", .cra_driver_name = "__driver-lrw-camellia-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_lrw_ctx), .cra_alignmask = 0, @@ -416,7 +420,8 @@ static struct crypto_alg cmll_algs[10] = { { .cra_name = "__xts-camellia-aesni", .cra_driver_name = "__driver-xts-camellia-aesni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAMELLIA_BLOCK_SIZE, .cra_ctxsize = sizeof(struct camellia_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/cast5_avx_glue.c b/arch/x86/crypto/cast5_avx_glue.c index 60ada677a928..236c80974457 100644 --- a/arch/x86/crypto/cast5_avx_glue.c +++ b/arch/x86/crypto/cast5_avx_glue.c @@ -341,7 +341,8 @@ static struct crypto_alg cast5_algs[6] = { { .cra_name = "__ecb-cast5-avx", .cra_driver_name = "__driver-ecb-cast5-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST5_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast5_ctx), .cra_alignmask = 0, @@ -360,7 +361,8 @@ static struct crypto_alg cast5_algs[6] = { { .cra_name = "__cbc-cast5-avx", .cra_driver_name = "__driver-cbc-cast5-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST5_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast5_ctx), .cra_alignmask = 0, @@ -379,7 +381,8 @@ static struct crypto_alg cast5_algs[6] = { { .cra_name = "__ctr-cast5-avx", .cra_driver_name = "__driver-ctr-cast5-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct cast5_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/cast6_avx_glue.c b/arch/x86/crypto/cast6_avx_glue.c index 0160f68a57ff..f448810ca4ac 100644 --- a/arch/x86/crypto/cast6_avx_glue.c +++ b/arch/x86/crypto/cast6_avx_glue.c @@ -372,7 +372,8 @@ static struct crypto_alg cast6_algs[10] = { { .cra_name = "__ecb-cast6-avx", .cra_driver_name = "__driver-ecb-cast6-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST6_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast6_ctx), .cra_alignmask = 0, @@ -391,7 +392,8 @@ static struct crypto_alg cast6_algs[10] = { { .cra_name = "__cbc-cast6-avx", .cra_driver_name = "__driver-cbc-cast6-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST6_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast6_ctx), .cra_alignmask = 0, @@ -410,7 +412,8 @@ static struct crypto_alg cast6_algs[10] = { { .cra_name = "__ctr-cast6-avx", .cra_driver_name = "__driver-ctr-cast6-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct cast6_ctx), .cra_alignmask = 0, @@ -430,7 +433,8 @@ static struct crypto_alg cast6_algs[10] = { { .cra_name = "__lrw-cast6-avx", .cra_driver_name = "__driver-lrw-cast6-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST6_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast6_lrw_ctx), .cra_alignmask = 0, @@ -453,7 +457,8 @@ static struct crypto_alg cast6_algs[10] = { { .cra_name = "__xts-cast6-avx", .cra_driver_name = "__driver-xts-cast6-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = CAST6_BLOCK_SIZE, .cra_ctxsize = sizeof(struct cast6_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/ghash-clmulni-intel_glue.c b/arch/x86/crypto/ghash-clmulni-intel_glue.c index 8253d85aa165..2079baf06bdd 100644 --- a/arch/x86/crypto/ghash-clmulni-intel_glue.c +++ b/arch/x86/crypto/ghash-clmulni-intel_glue.c @@ -154,7 +154,8 @@ static struct shash_alg ghash_alg = { .cra_name = "__ghash", .cra_driver_name = "__ghash-pclmulqdqni", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_SHASH, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | + CRYPTO_ALG_INTERNAL, .cra_blocksize = GHASH_BLOCK_SIZE, .cra_ctxsize = sizeof(struct ghash_ctx), .cra_module = THIS_MODULE, @@ -261,7 +262,9 @@ static int ghash_async_init_tfm(struct crypto_tfm *tfm) struct cryptd_ahash *cryptd_tfm; struct ghash_async_ctx *ctx = crypto_tfm_ctx(tfm); - cryptd_tfm = cryptd_alloc_ahash("__ghash-pclmulqdqni", 0, 0); + cryptd_tfm = cryptd_alloc_ahash("__ghash-pclmulqdqni", + CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); if (IS_ERR(cryptd_tfm)) return PTR_ERR(cryptd_tfm); ctx->cryptd_tfm = cryptd_tfm; diff --git a/arch/x86/crypto/glue_helper.c b/arch/x86/crypto/glue_helper.c index 432f1d76ceb8..6a85598931b5 100644 --- a/arch/x86/crypto/glue_helper.c +++ b/arch/x86/crypto/glue_helper.c @@ -232,7 +232,6 @@ static void glue_ctr_crypt_final_128bit(const common_glue_ctr_func_t fn_ctr, le128_to_be128((be128 *)walk->iv, &ctrblk); } -EXPORT_SYMBOL_GPL(glue_ctr_crypt_final_128bit); static unsigned int __glue_ctr_crypt_128bit(const struct common_glue_ctx *gctx, struct blkcipher_desc *desc, diff --git a/arch/x86/crypto/serpent_avx2_glue.c b/arch/x86/crypto/serpent_avx2_glue.c index 437e47a4d302..2f63dc89e7a9 100644 --- a/arch/x86/crypto/serpent_avx2_glue.c +++ b/arch/x86/crypto/serpent_avx2_glue.c @@ -309,7 +309,8 @@ static struct crypto_alg srp_algs[10] = { { .cra_name = "__ecb-serpent-avx2", .cra_driver_name = "__driver-ecb-serpent-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -329,7 +330,8 @@ static struct crypto_alg srp_algs[10] = { { .cra_name = "__cbc-serpent-avx2", .cra_driver_name = "__driver-cbc-serpent-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -349,7 +351,8 @@ static struct crypto_alg srp_algs[10] = { { .cra_name = "__ctr-serpent-avx2", .cra_driver_name = "__driver-ctr-serpent-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -370,7 +373,8 @@ static struct crypto_alg srp_algs[10] = { { .cra_name = "__lrw-serpent-avx2", .cra_driver_name = "__driver-lrw-serpent-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_lrw_ctx), .cra_alignmask = 0, @@ -394,7 +398,8 @@ static struct crypto_alg srp_algs[10] = { { .cra_name = "__xts-serpent-avx2", .cra_driver_name = "__driver-xts-serpent-avx2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/serpent_avx_glue.c b/arch/x86/crypto/serpent_avx_glue.c index 7e217398b4eb..c8d478af8456 100644 --- a/arch/x86/crypto/serpent_avx_glue.c +++ b/arch/x86/crypto/serpent_avx_glue.c @@ -378,7 +378,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__ecb-serpent-avx", .cra_driver_name = "__driver-ecb-serpent-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -397,7 +398,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__cbc-serpent-avx", .cra_driver_name = "__driver-cbc-serpent-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -416,7 +418,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__ctr-serpent-avx", .cra_driver_name = "__driver-ctr-serpent-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -436,7 +439,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__lrw-serpent-avx", .cra_driver_name = "__driver-lrw-serpent-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_lrw_ctx), .cra_alignmask = 0, @@ -459,7 +463,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__xts-serpent-avx", .cra_driver_name = "__driver-xts-serpent-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/serpent_sse2_glue.c b/arch/x86/crypto/serpent_sse2_glue.c index bf025adaea01..3643dd508f45 100644 --- a/arch/x86/crypto/serpent_sse2_glue.c +++ b/arch/x86/crypto/serpent_sse2_glue.c @@ -387,7 +387,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__ecb-serpent-sse2", .cra_driver_name = "__driver-ecb-serpent-sse2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -406,7 +407,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__cbc-serpent-sse2", .cra_driver_name = "__driver-cbc-serpent-sse2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -425,7 +427,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__ctr-serpent-sse2", .cra_driver_name = "__driver-ctr-serpent-sse2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct serpent_ctx), .cra_alignmask = 0, @@ -445,7 +448,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__lrw-serpent-sse2", .cra_driver_name = "__driver-lrw-serpent-sse2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_lrw_ctx), .cra_alignmask = 0, @@ -468,7 +472,8 @@ static struct crypto_alg serpent_algs[10] = { { .cra_name = "__xts-serpent-sse2", .cra_driver_name = "__driver-xts-serpent-sse2", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SERPENT_BLOCK_SIZE, .cra_ctxsize = sizeof(struct serpent_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/crypto/sha-mb/sha1_mb.c b/arch/x86/crypto/sha-mb/sha1_mb.c index fd9f6b035b16..e510b1c5d690 100644 --- a/arch/x86/crypto/sha-mb/sha1_mb.c +++ b/arch/x86/crypto/sha-mb/sha1_mb.c @@ -694,7 +694,8 @@ static struct shash_alg sha1_mb_shash_alg = { * use ASYNC flag as some buffers in multi-buffer * algo may not have completed before hashing thread sleep */ - .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_ASYNC, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_ASYNC | + CRYPTO_ALG_INTERNAL, .cra_blocksize = SHA1_BLOCK_SIZE, .cra_module = THIS_MODULE, .cra_list = LIST_HEAD_INIT(sha1_mb_shash_alg.base.cra_list), @@ -770,7 +771,9 @@ static int sha1_mb_async_init_tfm(struct crypto_tfm *tfm) struct sha1_mb_ctx *ctx = crypto_tfm_ctx(tfm); struct mcryptd_hash_ctx *mctx; - mcryptd_tfm = mcryptd_alloc_ahash("__intel_sha1-mb", 0, 0); + mcryptd_tfm = mcryptd_alloc_ahash("__intel_sha1-mb", + CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); if (IS_ERR(mcryptd_tfm)) return PTR_ERR(mcryptd_tfm); mctx = crypto_ahash_ctx(&mcryptd_tfm->base); @@ -828,7 +831,7 @@ static unsigned long sha1_mb_flusher(struct mcryptd_alg_cstate *cstate) while (!list_empty(&cstate->work_list)) { rctx = list_entry(cstate->work_list.next, struct mcryptd_hash_request_ctx, waiter); - if time_before(cur_time, rctx->tag.expire) + if (time_before(cur_time, rctx->tag.expire)) break; kernel_fpu_begin(); sha_ctx = (struct sha1_hash_ctx *) sha1_ctx_mgr_flush(cstate->mgr); diff --git a/arch/x86/crypto/sha-mb/sha1_mb_mgr_init_avx2.c b/arch/x86/crypto/sha-mb/sha1_mb_mgr_init_avx2.c index 4ca7e166a2aa..822acb5b464c 100644 --- a/arch/x86/crypto/sha-mb/sha1_mb_mgr_init_avx2.c +++ b/arch/x86/crypto/sha-mb/sha1_mb_mgr_init_avx2.c @@ -56,7 +56,7 @@ void sha1_mb_mgr_init_avx2(struct sha1_mb_mgr *state) { unsigned int j; - state->unused_lanes = 0xF76543210; + state->unused_lanes = 0xF76543210ULL; for (j = 0; j < 8; j++) { state->lens[j] = 0xFFFFFFFF; state->ldata[j].job_in_lane = NULL; diff --git a/arch/x86/crypto/sha1_ssse3_glue.c b/arch/x86/crypto/sha1_ssse3_glue.c index 6c20fe04a738..33d1b9dc14cc 100644 --- a/arch/x86/crypto/sha1_ssse3_glue.c +++ b/arch/x86/crypto/sha1_ssse3_glue.c @@ -28,7 +28,7 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> -#include <asm/byteorder.h> +#include <crypto/sha1_base.h> #include <asm/i387.h> #include <asm/xcr.h> #include <asm/xsave.h> @@ -44,132 +44,51 @@ asmlinkage void sha1_transform_avx(u32 *digest, const char *data, #define SHA1_AVX2_BLOCK_OPTSIZE 4 /* optimal 4*64 bytes of SHA1 blocks */ asmlinkage void sha1_transform_avx2(u32 *digest, const char *data, - unsigned int rounds); + unsigned int rounds); #endif -static asmlinkage void (*sha1_transform_asm)(u32 *, const char *, unsigned int); - - -static int sha1_ssse3_init(struct shash_desc *desc) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - - *sctx = (struct sha1_state){ - .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, - }; - - return 0; -} - -static int __sha1_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len, unsigned int partial) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int done = 0; - - sctx->count += len; - - if (partial) { - done = SHA1_BLOCK_SIZE - partial; - memcpy(sctx->buffer + partial, data, done); - sha1_transform_asm(sctx->state, sctx->buffer, 1); - } - - if (len - done >= SHA1_BLOCK_SIZE) { - const unsigned int rounds = (len - done) / SHA1_BLOCK_SIZE; - - sha1_transform_asm(sctx->state, data + done, rounds); - done += rounds * SHA1_BLOCK_SIZE; - } - - memcpy(sctx->buffer, data + done, len - done); - - return 0; -} +static void (*sha1_transform_asm)(u32 *, const char *, unsigned int); static int sha1_ssse3_update(struct shash_desc *desc, const u8 *data, unsigned int len) { struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; - int res; - /* Handle the fast case right here */ - if (partial + len < SHA1_BLOCK_SIZE) { - sctx->count += len; - memcpy(sctx->buffer + partial, data, len); + if (!irq_fpu_usable() || + (sctx->count % SHA1_BLOCK_SIZE) + len < SHA1_BLOCK_SIZE) + return crypto_sha1_update(desc, data, len); - return 0; - } + /* make sure casting to sha1_block_fn() is safe */ + BUILD_BUG_ON(offsetof(struct sha1_state, state) != 0); - if (!irq_fpu_usable()) { - res = crypto_sha1_update(desc, data, len); - } else { - kernel_fpu_begin(); - res = __sha1_ssse3_update(desc, data, len, partial); - kernel_fpu_end(); - } - - return res; -} - - -/* Add padding and return the message digest. */ -static int sha1_ssse3_final(struct shash_desc *desc, u8 *out) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int i, index, padlen; - __be32 *dst = (__be32 *)out; - __be64 bits; - static const u8 padding[SHA1_BLOCK_SIZE] = { 0x80, }; - - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64 and append length */ - index = sctx->count % SHA1_BLOCK_SIZE; - padlen = (index < 56) ? (56 - index) : ((SHA1_BLOCK_SIZE+56) - index); - if (!irq_fpu_usable()) { - crypto_sha1_update(desc, padding, padlen); - crypto_sha1_update(desc, (const u8 *)&bits, sizeof(bits)); - } else { - kernel_fpu_begin(); - /* We need to fill a whole block for __sha1_ssse3_update() */ - if (padlen <= 56) { - sctx->count += padlen; - memcpy(sctx->buffer + index, padding, padlen); - } else { - __sha1_ssse3_update(desc, padding, padlen, index); - } - __sha1_ssse3_update(desc, (const u8 *)&bits, sizeof(bits), 56); - kernel_fpu_end(); - } - - /* Store state in digest */ - for (i = 0; i < 5; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof(*sctx)); + kernel_fpu_begin(); + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_transform_asm); + kernel_fpu_end(); return 0; } -static int sha1_ssse3_export(struct shash_desc *desc, void *out) +static int sha1_ssse3_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); + if (!irq_fpu_usable()) + return crypto_sha1_finup(desc, data, len, out); - memcpy(out, sctx, sizeof(*sctx)); + kernel_fpu_begin(); + if (len) + sha1_base_do_update(desc, data, len, + (sha1_block_fn *)sha1_transform_asm); + sha1_base_do_finalize(desc, (sha1_block_fn *)sha1_transform_asm); + kernel_fpu_end(); - return 0; + return sha1_base_finish(desc, out); } -static int sha1_ssse3_import(struct shash_desc *desc, const void *in) +/* Add padding and return the message digest. */ +static int sha1_ssse3_final(struct shash_desc *desc, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - - return 0; + return sha1_ssse3_finup(desc, NULL, 0, out); } #ifdef CONFIG_AS_AVX2 @@ -186,13 +105,11 @@ static void sha1_apply_transform_avx2(u32 *digest, const char *data, static struct shash_alg alg = { .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_ssse3_init, + .init = sha1_base_init, .update = sha1_ssse3_update, .final = sha1_ssse3_final, - .export = sha1_ssse3_export, - .import = sha1_ssse3_import, + .finup = sha1_ssse3_finup, .descsize = sizeof(struct sha1_state), - .statesize = sizeof(struct sha1_state), .base = { .cra_name = "sha1", .cra_driver_name= "sha1-ssse3", diff --git a/arch/x86/crypto/sha256-avx-asm.S b/arch/x86/crypto/sha256-avx-asm.S index 642f15687a0a..92b3b5d75ba9 100644 --- a/arch/x86/crypto/sha256-avx-asm.S +++ b/arch/x86/crypto/sha256-avx-asm.S @@ -96,10 +96,10 @@ SHUF_DC00 = %xmm12 # shuffle xDxC -> DC00 BYTE_FLIP_MASK = %xmm13 NUM_BLKS = %rdx # 3rd arg -CTX = %rsi # 2nd arg -INP = %rdi # 1st arg +INP = %rsi # 2nd arg +CTX = %rdi # 1st arg -SRND = %rdi # clobbers INP +SRND = %rsi # clobbers INP c = %ecx d = %r8d e = %edx @@ -342,8 +342,8 @@ a = TMP_ ######################################################################## ## void sha256_transform_avx(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to input data -## arg 2 : pointer to digest +## arg 1 : pointer to digest +## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## .text diff --git a/arch/x86/crypto/sha256-avx2-asm.S b/arch/x86/crypto/sha256-avx2-asm.S index 9e86944c539d..570ec5ec62d7 100644 --- a/arch/x86/crypto/sha256-avx2-asm.S +++ b/arch/x86/crypto/sha256-avx2-asm.S @@ -91,12 +91,12 @@ BYTE_FLIP_MASK = %ymm13 X_BYTE_FLIP_MASK = %xmm13 # XMM version of BYTE_FLIP_MASK NUM_BLKS = %rdx # 3rd arg -CTX = %rsi # 2nd arg -INP = %rdi # 1st arg +INP = %rsi # 2nd arg +CTX = %rdi # 1st arg c = %ecx d = %r8d e = %edx # clobbers NUM_BLKS -y3 = %edi # clobbers INP +y3 = %esi # clobbers INP TBL = %rbp @@ -523,8 +523,8 @@ STACK_SIZE = _RSP + _RSP_SIZE ######################################################################## ## void sha256_transform_rorx(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to input data -## arg 2 : pointer to digest +## arg 1 : pointer to digest +## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## .text diff --git a/arch/x86/crypto/sha256-ssse3-asm.S b/arch/x86/crypto/sha256-ssse3-asm.S index f833b74d902b..2cedc44e8121 100644 --- a/arch/x86/crypto/sha256-ssse3-asm.S +++ b/arch/x86/crypto/sha256-ssse3-asm.S @@ -88,10 +88,10 @@ SHUF_DC00 = %xmm11 # shuffle xDxC -> DC00 BYTE_FLIP_MASK = %xmm12 NUM_BLKS = %rdx # 3rd arg -CTX = %rsi # 2nd arg -INP = %rdi # 1st arg +INP = %rsi # 2nd arg +CTX = %rdi # 1st arg -SRND = %rdi # clobbers INP +SRND = %rsi # clobbers INP c = %ecx d = %r8d e = %edx @@ -348,8 +348,8 @@ a = TMP_ ######################################################################## ## void sha256_transform_ssse3(void *input_data, UINT32 digest[8], UINT64 num_blks) -## arg 1 : pointer to input data -## arg 2 : pointer to digest +## arg 1 : pointer to digest +## arg 2 : pointer to input data ## arg 3 : Num blocks ######################################################################## .text diff --git a/arch/x86/crypto/sha256_ssse3_glue.c b/arch/x86/crypto/sha256_ssse3_glue.c index 8fad72f4dfd2..ccc338881ee8 100644 --- a/arch/x86/crypto/sha256_ssse3_glue.c +++ b/arch/x86/crypto/sha256_ssse3_glue.c @@ -36,195 +36,74 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> -#include <asm/byteorder.h> +#include <crypto/sha256_base.h> #include <asm/i387.h> #include <asm/xcr.h> #include <asm/xsave.h> #include <linux/string.h> -asmlinkage void sha256_transform_ssse3(const char *data, u32 *digest, - u64 rounds); +asmlinkage void sha256_transform_ssse3(u32 *digest, const char *data, + u64 rounds); #ifdef CONFIG_AS_AVX -asmlinkage void sha256_transform_avx(const char *data, u32 *digest, +asmlinkage void sha256_transform_avx(u32 *digest, const char *data, u64 rounds); #endif #ifdef CONFIG_AS_AVX2 -asmlinkage void sha256_transform_rorx(const char *data, u32 *digest, - u64 rounds); +asmlinkage void sha256_transform_rorx(u32 *digest, const char *data, + u64 rounds); #endif -static asmlinkage void (*sha256_transform_asm)(const char *, u32 *, u64); - - -static int sha256_ssse3_init(struct shash_desc *desc) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - sctx->state[0] = SHA256_H0; - sctx->state[1] = SHA256_H1; - sctx->state[2] = SHA256_H2; - sctx->state[3] = SHA256_H3; - sctx->state[4] = SHA256_H4; - sctx->state[5] = SHA256_H5; - sctx->state[6] = SHA256_H6; - sctx->state[7] = SHA256_H7; - sctx->count = 0; - - return 0; -} - -static int __sha256_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len, unsigned int partial) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - unsigned int done = 0; - - sctx->count += len; - - if (partial) { - done = SHA256_BLOCK_SIZE - partial; - memcpy(sctx->buf + partial, data, done); - sha256_transform_asm(sctx->buf, sctx->state, 1); - } - - if (len - done >= SHA256_BLOCK_SIZE) { - const unsigned int rounds = (len - done) / SHA256_BLOCK_SIZE; - - sha256_transform_asm(data + done, sctx->state, (u64) rounds); - - done += rounds * SHA256_BLOCK_SIZE; - } - - memcpy(sctx->buf, data + done, len - done); - - return 0; -} +static void (*sha256_transform_asm)(u32 *, const char *, u64); static int sha256_ssse3_update(struct shash_desc *desc, const u8 *data, unsigned int len) { struct sha256_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; - int res; - /* Handle the fast case right here */ - if (partial + len < SHA256_BLOCK_SIZE) { - sctx->count += len; - memcpy(sctx->buf + partial, data, len); + if (!irq_fpu_usable() || + (sctx->count % SHA256_BLOCK_SIZE) + len < SHA256_BLOCK_SIZE) + return crypto_sha256_update(desc, data, len); - return 0; - } - - if (!irq_fpu_usable()) { - res = crypto_sha256_update(desc, data, len); - } else { - kernel_fpu_begin(); - res = __sha256_ssse3_update(desc, data, len, partial); - kernel_fpu_end(); - } - - return res; -} + /* make sure casting to sha256_block_fn() is safe */ + BUILD_BUG_ON(offsetof(struct sha256_state, state) != 0); - -/* Add padding and return the message digest. */ -static int sha256_ssse3_final(struct shash_desc *desc, u8 *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - unsigned int i, index, padlen; - __be32 *dst = (__be32 *)out; - __be64 bits; - static const u8 padding[SHA256_BLOCK_SIZE] = { 0x80, }; - - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64 and append length */ - index = sctx->count % SHA256_BLOCK_SIZE; - padlen = (index < 56) ? (56 - index) : ((SHA256_BLOCK_SIZE+56)-index); - - if (!irq_fpu_usable()) { - crypto_sha256_update(desc, padding, padlen); - crypto_sha256_update(desc, (const u8 *)&bits, sizeof(bits)); - } else { - kernel_fpu_begin(); - /* We need to fill a whole block for __sha256_ssse3_update() */ - if (padlen <= 56) { - sctx->count += padlen; - memcpy(sctx->buf + index, padding, padlen); - } else { - __sha256_ssse3_update(desc, padding, padlen, index); - } - __sha256_ssse3_update(desc, (const u8 *)&bits, - sizeof(bits), 56); - kernel_fpu_end(); - } - - /* Store state in digest */ - for (i = 0; i < 8; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof(*sctx)); + kernel_fpu_begin(); + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_transform_asm); + kernel_fpu_end(); return 0; } -static int sha256_ssse3_export(struct shash_desc *desc, void *out) +static int sha256_ssse3_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha256_state *sctx = shash_desc_ctx(desc); + if (!irq_fpu_usable()) + return crypto_sha256_finup(desc, data, len, out); - memcpy(out, sctx, sizeof(*sctx)); + kernel_fpu_begin(); + if (len) + sha256_base_do_update(desc, data, len, + (sha256_block_fn *)sha256_transform_asm); + sha256_base_do_finalize(desc, (sha256_block_fn *)sha256_transform_asm); + kernel_fpu_end(); - return 0; + return sha256_base_finish(desc, out); } -static int sha256_ssse3_import(struct shash_desc *desc, const void *in) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - - return 0; -} - -static int sha224_ssse3_init(struct shash_desc *desc) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - sctx->state[0] = SHA224_H0; - sctx->state[1] = SHA224_H1; - sctx->state[2] = SHA224_H2; - sctx->state[3] = SHA224_H3; - sctx->state[4] = SHA224_H4; - sctx->state[5] = SHA224_H5; - sctx->state[6] = SHA224_H6; - sctx->state[7] = SHA224_H7; - sctx->count = 0; - - return 0; -} - -static int sha224_ssse3_final(struct shash_desc *desc, u8 *hash) +/* Add padding and return the message digest. */ +static int sha256_ssse3_final(struct shash_desc *desc, u8 *out) { - u8 D[SHA256_DIGEST_SIZE]; - - sha256_ssse3_final(desc, D); - - memcpy(hash, D, SHA224_DIGEST_SIZE); - memzero_explicit(D, SHA256_DIGEST_SIZE); - - return 0; + return sha256_ssse3_finup(desc, NULL, 0, out); } static struct shash_alg algs[] = { { .digestsize = SHA256_DIGEST_SIZE, - .init = sha256_ssse3_init, + .init = sha256_base_init, .update = sha256_ssse3_update, .final = sha256_ssse3_final, - .export = sha256_ssse3_export, - .import = sha256_ssse3_import, + .finup = sha256_ssse3_finup, .descsize = sizeof(struct sha256_state), - .statesize = sizeof(struct sha256_state), .base = { .cra_name = "sha256", .cra_driver_name = "sha256-ssse3", @@ -235,13 +114,11 @@ static struct shash_alg algs[] = { { } }, { .digestsize = SHA224_DIGEST_SIZE, - .init = sha224_ssse3_init, + .init = sha224_base_init, .update = sha256_ssse3_update, - .final = sha224_ssse3_final, - .export = sha256_ssse3_export, - .import = sha256_ssse3_import, + .final = sha256_ssse3_final, + .finup = sha256_ssse3_finup, .descsize = sizeof(struct sha256_state), - .statesize = sizeof(struct sha256_state), .base = { .cra_name = "sha224", .cra_driver_name = "sha224-ssse3", diff --git a/arch/x86/crypto/sha512-avx-asm.S b/arch/x86/crypto/sha512-avx-asm.S index 974dde9bc6cd..565274d6a641 100644 --- a/arch/x86/crypto/sha512-avx-asm.S +++ b/arch/x86/crypto/sha512-avx-asm.S @@ -54,9 +54,9 @@ # Virtual Registers # ARG1 -msg = %rdi +digest = %rdi # ARG2 -digest = %rsi +msg = %rsi # ARG3 msglen = %rdx T1 = %rcx @@ -271,7 +271,7 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_avx(const void* M, void* D, u64 L) +# void sha512_transform_avx(void* D, const void* M, u64 L) # Purpose: Updates the SHA512 digest stored at D with the message stored in M. # The size of the message pointed to by M must be an integer multiple of SHA512 # message blocks. diff --git a/arch/x86/crypto/sha512-avx2-asm.S b/arch/x86/crypto/sha512-avx2-asm.S index 568b96105f5c..a4771dcd1fcf 100644 --- a/arch/x86/crypto/sha512-avx2-asm.S +++ b/arch/x86/crypto/sha512-avx2-asm.S @@ -70,9 +70,9 @@ XFER = YTMP0 BYTE_FLIP_MASK = %ymm9 # 1st arg -INP = %rdi +CTX = %rdi # 2nd arg -CTX = %rsi +INP = %rsi # 3rd arg NUM_BLKS = %rdx @@ -562,7 +562,7 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_rorx(const void* M, void* D, uint64_t L)# +# void sha512_transform_rorx(void* D, const void* M, uint64_t L)# # Purpose: Updates the SHA512 digest stored at D with the message stored in M. # The size of the message pointed to by M must be an integer multiple of SHA512 # message blocks. diff --git a/arch/x86/crypto/sha512-ssse3-asm.S b/arch/x86/crypto/sha512-ssse3-asm.S index fb56855d51f5..e610e29cbc81 100644 --- a/arch/x86/crypto/sha512-ssse3-asm.S +++ b/arch/x86/crypto/sha512-ssse3-asm.S @@ -53,9 +53,9 @@ # Virtual Registers # ARG1 -msg = %rdi +digest = %rdi # ARG2 -digest = %rsi +msg = %rsi # ARG3 msglen = %rdx T1 = %rcx @@ -269,7 +269,7 @@ frame_size = frame_GPRSAVE + GPRSAVE_SIZE .endm ######################################################################## -# void sha512_transform_ssse3(const void* M, void* D, u64 L)# +# void sha512_transform_ssse3(void* D, const void* M, u64 L)# # Purpose: Updates the SHA512 digest stored at D with the message stored in M. # The size of the message pointed to by M must be an integer multiple of SHA512 # message blocks. diff --git a/arch/x86/crypto/sha512_ssse3_glue.c b/arch/x86/crypto/sha512_ssse3_glue.c index 0b6af26832bf..d9fa4c1e063f 100644 --- a/arch/x86/crypto/sha512_ssse3_glue.c +++ b/arch/x86/crypto/sha512_ssse3_glue.c @@ -34,205 +34,75 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> -#include <asm/byteorder.h> +#include <crypto/sha512_base.h> #include <asm/i387.h> #include <asm/xcr.h> #include <asm/xsave.h> #include <linux/string.h> -asmlinkage void sha512_transform_ssse3(const char *data, u64 *digest, - u64 rounds); +asmlinkage void sha512_transform_ssse3(u64 *digest, const char *data, + u64 rounds); #ifdef CONFIG_AS_AVX -asmlinkage void sha512_transform_avx(const char *data, u64 *digest, +asmlinkage void sha512_transform_avx(u64 *digest, const char *data, u64 rounds); #endif #ifdef CONFIG_AS_AVX2 -asmlinkage void sha512_transform_rorx(const char *data, u64 *digest, - u64 rounds); +asmlinkage void sha512_transform_rorx(u64 *digest, const char *data, + u64 rounds); #endif -static asmlinkage void (*sha512_transform_asm)(const char *, u64 *, u64); - - -static int sha512_ssse3_init(struct shash_desc *desc) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - - sctx->state[0] = SHA512_H0; - sctx->state[1] = SHA512_H1; - sctx->state[2] = SHA512_H2; - sctx->state[3] = SHA512_H3; - sctx->state[4] = SHA512_H4; - sctx->state[5] = SHA512_H5; - sctx->state[6] = SHA512_H6; - sctx->state[7] = SHA512_H7; - sctx->count[0] = sctx->count[1] = 0; - - return 0; -} +static void (*sha512_transform_asm)(u64 *, const char *, u64); -static int __sha512_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len, unsigned int partial) +static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data, + unsigned int len) { struct sha512_state *sctx = shash_desc_ctx(desc); - unsigned int done = 0; - - sctx->count[0] += len; - if (sctx->count[0] < len) - sctx->count[1]++; - if (partial) { - done = SHA512_BLOCK_SIZE - partial; - memcpy(sctx->buf + partial, data, done); - sha512_transform_asm(sctx->buf, sctx->state, 1); - } - - if (len - done >= SHA512_BLOCK_SIZE) { - const unsigned int rounds = (len - done) / SHA512_BLOCK_SIZE; + if (!irq_fpu_usable() || + (sctx->count[0] % SHA512_BLOCK_SIZE) + len < SHA512_BLOCK_SIZE) + return crypto_sha512_update(desc, data, len); - sha512_transform_asm(data + done, sctx->state, (u64) rounds); - - done += rounds * SHA512_BLOCK_SIZE; - } + /* make sure casting to sha512_block_fn() is safe */ + BUILD_BUG_ON(offsetof(struct sha512_state, state) != 0); - memcpy(sctx->buf, data + done, len - done); + kernel_fpu_begin(); + sha512_base_do_update(desc, data, len, + (sha512_block_fn *)sha512_transform_asm); + kernel_fpu_end(); return 0; } -static int sha512_ssse3_update(struct shash_desc *desc, const u8 *data, - unsigned int len) +static int sha512_ssse3_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha512_state *sctx = shash_desc_ctx(desc); - unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE; - int res; - - /* Handle the fast case right here */ - if (partial + len < SHA512_BLOCK_SIZE) { - sctx->count[0] += len; - if (sctx->count[0] < len) - sctx->count[1]++; - memcpy(sctx->buf + partial, data, len); - - return 0; - } + if (!irq_fpu_usable()) + return crypto_sha512_finup(desc, data, len, out); - if (!irq_fpu_usable()) { - res = crypto_sha512_update(desc, data, len); - } else { - kernel_fpu_begin(); - res = __sha512_ssse3_update(desc, data, len, partial); - kernel_fpu_end(); - } + kernel_fpu_begin(); + if (len) + sha512_base_do_update(desc, data, len, + (sha512_block_fn *)sha512_transform_asm); + sha512_base_do_finalize(desc, (sha512_block_fn *)sha512_transform_asm); + kernel_fpu_end(); - return res; + return sha512_base_finish(desc, out); } - /* Add padding and return the message digest. */ static int sha512_ssse3_final(struct shash_desc *desc, u8 *out) { - struct sha512_state *sctx = shash_desc_ctx(desc); - unsigned int i, index, padlen; - __be64 *dst = (__be64 *)out; - __be64 bits[2]; - static const u8 padding[SHA512_BLOCK_SIZE] = { 0x80, }; - - /* save number of bits */ - bits[1] = cpu_to_be64(sctx->count[0] << 3); - bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61); - - /* Pad out to 112 mod 128 and append length */ - index = sctx->count[0] & 0x7f; - padlen = (index < 112) ? (112 - index) : ((128+112) - index); - - if (!irq_fpu_usable()) { - crypto_sha512_update(desc, padding, padlen); - crypto_sha512_update(desc, (const u8 *)&bits, sizeof(bits)); - } else { - kernel_fpu_begin(); - /* We need to fill a whole block for __sha512_ssse3_update() */ - if (padlen <= 112) { - sctx->count[0] += padlen; - if (sctx->count[0] < padlen) - sctx->count[1]++; - memcpy(sctx->buf + index, padding, padlen); - } else { - __sha512_ssse3_update(desc, padding, padlen, index); - } - __sha512_ssse3_update(desc, (const u8 *)&bits, - sizeof(bits), 112); - kernel_fpu_end(); - } - - /* Store state in digest */ - for (i = 0; i < 8; i++) - dst[i] = cpu_to_be64(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof(*sctx)); - - return 0; -} - -static int sha512_ssse3_export(struct shash_desc *desc, void *out) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - - memcpy(out, sctx, sizeof(*sctx)); - - return 0; -} - -static int sha512_ssse3_import(struct shash_desc *desc, const void *in) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - - return 0; -} - -static int sha384_ssse3_init(struct shash_desc *desc) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - - sctx->state[0] = SHA384_H0; - sctx->state[1] = SHA384_H1; - sctx->state[2] = SHA384_H2; - sctx->state[3] = SHA384_H3; - sctx->state[4] = SHA384_H4; - sctx->state[5] = SHA384_H5; - sctx->state[6] = SHA384_H6; - sctx->state[7] = SHA384_H7; - - sctx->count[0] = sctx->count[1] = 0; - - return 0; -} - -static int sha384_ssse3_final(struct shash_desc *desc, u8 *hash) -{ - u8 D[SHA512_DIGEST_SIZE]; - - sha512_ssse3_final(desc, D); - - memcpy(hash, D, SHA384_DIGEST_SIZE); - memzero_explicit(D, SHA512_DIGEST_SIZE); - - return 0; + return sha512_ssse3_finup(desc, NULL, 0, out); } static struct shash_alg algs[] = { { .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_ssse3_init, + .init = sha512_base_init, .update = sha512_ssse3_update, .final = sha512_ssse3_final, - .export = sha512_ssse3_export, - .import = sha512_ssse3_import, + .finup = sha512_ssse3_finup, .descsize = sizeof(struct sha512_state), - .statesize = sizeof(struct sha512_state), .base = { .cra_name = "sha512", .cra_driver_name = "sha512-ssse3", @@ -243,13 +113,11 @@ static struct shash_alg algs[] = { { } }, { .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_ssse3_init, + .init = sha384_base_init, .update = sha512_ssse3_update, - .final = sha384_ssse3_final, - .export = sha512_ssse3_export, - .import = sha512_ssse3_import, + .final = sha512_ssse3_final, + .finup = sha512_ssse3_finup, .descsize = sizeof(struct sha512_state), - .statesize = sizeof(struct sha512_state), .base = { .cra_name = "sha384", .cra_driver_name = "sha384-ssse3", diff --git a/arch/x86/crypto/twofish_avx_glue.c b/arch/x86/crypto/twofish_avx_glue.c index 1ac531ea9bcc..b5e2d5651851 100644 --- a/arch/x86/crypto/twofish_avx_glue.c +++ b/arch/x86/crypto/twofish_avx_glue.c @@ -340,7 +340,8 @@ static struct crypto_alg twofish_algs[10] = { { .cra_name = "__ecb-twofish-avx", .cra_driver_name = "__driver-ecb-twofish-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = TF_BLOCK_SIZE, .cra_ctxsize = sizeof(struct twofish_ctx), .cra_alignmask = 0, @@ -359,7 +360,8 @@ static struct crypto_alg twofish_algs[10] = { { .cra_name = "__cbc-twofish-avx", .cra_driver_name = "__driver-cbc-twofish-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = TF_BLOCK_SIZE, .cra_ctxsize = sizeof(struct twofish_ctx), .cra_alignmask = 0, @@ -378,7 +380,8 @@ static struct crypto_alg twofish_algs[10] = { { .cra_name = "__ctr-twofish-avx", .cra_driver_name = "__driver-ctr-twofish-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = 1, .cra_ctxsize = sizeof(struct twofish_ctx), .cra_alignmask = 0, @@ -398,7 +401,8 @@ static struct crypto_alg twofish_algs[10] = { { .cra_name = "__lrw-twofish-avx", .cra_driver_name = "__driver-lrw-twofish-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = TF_BLOCK_SIZE, .cra_ctxsize = sizeof(struct twofish_lrw_ctx), .cra_alignmask = 0, @@ -421,7 +425,8 @@ static struct crypto_alg twofish_algs[10] = { { .cra_name = "__xts-twofish-avx", .cra_driver_name = "__driver-xts-twofish-avx", .cra_priority = 0, - .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | + CRYPTO_ALG_INTERNAL, .cra_blocksize = TF_BLOCK_SIZE, .cra_ctxsize = sizeof(struct twofish_xts_ctx), .cra_alignmask = 0, diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index ea2dbe82cba3..b4bdec3e9523 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -46,13 +46,11 @@ */ #ifndef __ASSEMBLY__ struct task_struct; -struct exec_domain; #include <asm/processor.h> #include <linux/atomic.h> struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ __u32 flags; /* low level flags */ __u32 status; /* thread synchronous flags */ __u32 cpu; /* current CPU */ @@ -66,7 +64,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .saved_preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 3e581865c8e2..f9804080ccb3 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -593,24 +593,10 @@ badframe: return 0; } -/* - * OK, we're invoking a handler: - */ -static int signr_convert(int sig) -{ -#ifdef CONFIG_X86_32 - struct thread_info *info = current_thread_info(); - - if (info->exec_domain && info->exec_domain->signal_invmap && sig < 32) - return info->exec_domain->signal_invmap[sig]; -#endif /* CONFIG_X86_32 */ - return sig; -} - static int setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) { - int usig = signr_convert(ksig->sig); + int usig = ksig->sig; sigset_t *set = sigmask_to_save(); compat_sigset_t *cset = (compat_sigset_t *) set; diff --git a/arch/x86/um/Makefile b/arch/x86/um/Makefile index eafa324eb7a5..acb384d24669 100644 --- a/arch/x86/um/Makefile +++ b/arch/x86/um/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_BINFMT_ELF) += elfcore.o subarch-y = ../lib/string_32.o ../lib/atomic64_32.o ../lib/atomic64_cx8_32.o subarch-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += ../lib/rwsem.o -subarch-$(CONFIG_HIGHMEM) += ../mm/highmem_32.o else diff --git a/arch/x86/um/asm/barrier.h b/arch/x86/um/asm/barrier.h index 8ffd2146fa6a..7e8a1a650435 100644 --- a/arch/x86/um/asm/barrier.h +++ b/arch/x86/um/asm/barrier.h @@ -36,22 +36,11 @@ #endif /* CONFIG_X86_PPRO_FENCE */ #define dma_wmb() barrier() -#ifdef CONFIG_SMP - -#define smp_mb() mb() -#define smp_rmb() dma_rmb() -#define smp_wmb() barrier() -#define set_mb(var, value) do { (void)xchg(&var, value); } while (0) - -#else /* CONFIG_SMP */ - #define smp_mb() barrier() #define smp_rmb() barrier() #define smp_wmb() barrier() #define set_mb(var, value) do { var = value; barrier(); } while (0) -#endif /* CONFIG_SMP */ - #define read_barrier_depends() do { } while (0) #define smp_read_barrier_depends() do { } while (0) diff --git a/arch/x86/um/asm/elf.h b/arch/x86/um/asm/elf.h index 25a1022dd793..0a656b727b1a 100644 --- a/arch/x86/um/asm/elf.h +++ b/arch/x86/um/asm/elf.h @@ -210,7 +210,7 @@ extern int elf_core_copy_fpregs(struct task_struct *t, elf_fpregset_t *fpu); #define ELF_EXEC_PAGESIZE 4096 -#define ELF_ET_DYN_BASE (2 * TASK_SIZE / 3) +#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) extern long elf_aux_hwcap; #define ELF_HWCAP (elf_aux_hwcap) diff --git a/arch/x86/um/ldt.c b/arch/x86/um/ldt.c index 8e08176f0bcb..5c0b711d2433 100644 --- a/arch/x86/um/ldt.c +++ b/arch/x86/um/ldt.c @@ -8,9 +8,7 @@ #include <linux/slab.h> #include <asm/unistd.h> #include <os.h> -#include <proc_mm.h> #include <skas.h> -#include <skas_ptrace.h> #include <sysdep/tls.h> extern int modify_ldt(int func, void *ptr, unsigned long bytecount); @@ -19,105 +17,20 @@ static long write_ldt_entry(struct mm_id *mm_idp, int func, struct user_desc *desc, void **addr, int done) { long res; - - if (proc_mm) { - /* - * This is a special handling for the case, that the mm to - * modify isn't current->active_mm. - * If this is called directly by modify_ldt, - * (current->active_mm->context.skas.u == mm_idp) - * will be true. So no call to __switch_mm(mm_idp) is done. - * If this is called in case of init_new_ldt or PTRACE_LDT, - * mm_idp won't belong to current->active_mm, but child->mm. - * So we need to switch child's mm into our userspace, then - * later switch back. - * - * Note: I'm unsure: should interrupts be disabled here? - */ - if (!current->active_mm || current->active_mm == &init_mm || - mm_idp != ¤t->active_mm->context.id) - __switch_mm(mm_idp); - } - - if (ptrace_ldt) { - struct ptrace_ldt ldt_op = (struct ptrace_ldt) { - .func = func, - .ptr = desc, - .bytecount = sizeof(*desc)}; - u32 cpu; - int pid; - - if (!proc_mm) - pid = mm_idp->u.pid; - else { - cpu = get_cpu(); - pid = userspace_pid[cpu]; - } - - res = os_ptrace_ldt(pid, 0, (unsigned long) &ldt_op); - - if (proc_mm) - put_cpu(); - } - else { - void *stub_addr; - res = syscall_stub_data(mm_idp, (unsigned long *)desc, - (sizeof(*desc) + sizeof(long) - 1) & - ~(sizeof(long) - 1), - addr, &stub_addr); - if (!res) { - unsigned long args[] = { func, - (unsigned long)stub_addr, - sizeof(*desc), - 0, 0, 0 }; - res = run_syscall_stub(mm_idp, __NR_modify_ldt, args, - 0, addr, done); - } + void *stub_addr; + res = syscall_stub_data(mm_idp, (unsigned long *)desc, + (sizeof(*desc) + sizeof(long) - 1) & + ~(sizeof(long) - 1), + addr, &stub_addr); + if (!res) { + unsigned long args[] = { func, + (unsigned long)stub_addr, + sizeof(*desc), + 0, 0, 0 }; + res = run_syscall_stub(mm_idp, __NR_modify_ldt, args, + 0, addr, done); } - if (proc_mm) { - /* - * This is the second part of special handling, that makes - * PTRACE_LDT possible to implement. - */ - if (current->active_mm && current->active_mm != &init_mm && - mm_idp != ¤t->active_mm->context.id) - __switch_mm(¤t->active_mm->context.id); - } - - return res; -} - -static long read_ldt_from_host(void __user * ptr, unsigned long bytecount) -{ - int res, n; - struct ptrace_ldt ptrace_ldt = (struct ptrace_ldt) { - .func = 0, - .bytecount = bytecount, - .ptr = kmalloc(bytecount, GFP_KERNEL)}; - u32 cpu; - - if (ptrace_ldt.ptr == NULL) - return -ENOMEM; - - /* - * This is called from sys_modify_ldt only, so userspace_pid gives - * us the right number - */ - - cpu = get_cpu(); - res = os_ptrace_ldt(userspace_pid[cpu], 0, (unsigned long) &ptrace_ldt); - put_cpu(); - if (res < 0) - goto out; - - n = copy_to_user(ptr, ptrace_ldt.ptr, res); - if (n != 0) - res = -EFAULT; - - out: - kfree(ptrace_ldt.ptr); - return res; } @@ -145,9 +58,6 @@ static int read_ldt(void __user * ptr, unsigned long bytecount) bytecount = LDT_ENTRY_SIZE*LDT_ENTRIES; err = bytecount; - if (ptrace_ldt) - return read_ldt_from_host(ptr, bytecount); - mutex_lock(&ldt->lock); if (ldt->entry_count <= LDT_DIRECT_ENTRIES) { size = LDT_ENTRY_SIZE*LDT_DIRECT_ENTRIES; @@ -229,17 +139,11 @@ static int write_ldt(void __user * ptr, unsigned long bytecount, int func) goto out; } - if (!ptrace_ldt) - mutex_lock(&ldt->lock); + mutex_lock(&ldt->lock); err = write_ldt_entry(mm_idp, func, &ldt_info, &addr, 1); if (err) goto out_unlock; - else if (ptrace_ldt) { - /* With PTRACE_LDT available, this is used as a flag only */ - ldt->entry_count = 1; - goto out; - } if (ldt_info.entry_number >= ldt->entry_count && ldt_info.entry_number >= LDT_DIRECT_ENTRIES) { @@ -393,91 +297,56 @@ long init_new_ldt(struct mm_context *new_mm, struct mm_context *from_mm) int i; long page, err=0; void *addr = NULL; - struct proc_mm_op copy; - if (!ptrace_ldt) - mutex_init(&new_mm->arch.ldt.lock); + mutex_init(&new_mm->arch.ldt.lock); if (!from_mm) { memset(&desc, 0, sizeof(desc)); /* - * We have to initialize a clean ldt. + * Now we try to retrieve info about the ldt, we + * inherited from the host. All ldt-entries found + * will be reset in the following loop */ - if (proc_mm) { - /* - * If the new mm was created using proc_mm, host's - * default-ldt currently is assigned, which normally - * contains the call-gates for lcall7 and lcall27. - * To remove these gates, we simply write an empty - * entry as number 0 to the host. - */ - err = write_ldt_entry(&new_mm->id, 1, &desc, &addr, 1); - } - else{ - /* - * Now we try to retrieve info about the ldt, we - * inherited from the host. All ldt-entries found - * will be reset in the following loop - */ - ldt_get_host_info(); - for (num_p=host_ldt_entries; *num_p != -1; num_p++) { - desc.entry_number = *num_p; - err = write_ldt_entry(&new_mm->id, 1, &desc, - &addr, *(num_p + 1) == -1); - if (err) - break; - } + ldt_get_host_info(); + for (num_p=host_ldt_entries; *num_p != -1; num_p++) { + desc.entry_number = *num_p; + err = write_ldt_entry(&new_mm->id, 1, &desc, + &addr, *(num_p + 1) == -1); + if (err) + break; } new_mm->arch.ldt.entry_count = 0; goto out; } - if (proc_mm) { - /* - * We have a valid from_mm, so we now have to copy the LDT of - * from_mm to new_mm, because using proc_mm an new mm with - * an empty/default LDT was created in new_mm() - */ - copy = ((struct proc_mm_op) { .op = MM_COPY_SEGMENTS, - .u = - { .copy_segments = - from_mm->id.u.mm_fd } } ); - i = os_write_file(new_mm->id.u.mm_fd, ©, sizeof(copy)); - if (i != sizeof(copy)) - printk(KERN_ERR "new_mm : /proc/mm copy_segments " - "failed, err = %d\n", -i); - } - - if (!ptrace_ldt) { - /* - * Our local LDT is used to supply the data for - * modify_ldt(READLDT), if PTRACE_LDT isn't available, - * i.e., we have to use the stub for modify_ldt, which - * can't handle the big read buffer of up to 64kB. - */ - mutex_lock(&from_mm->arch.ldt.lock); - if (from_mm->arch.ldt.entry_count <= LDT_DIRECT_ENTRIES) - memcpy(new_mm->arch.ldt.u.entries, from_mm->arch.ldt.u.entries, - sizeof(new_mm->arch.ldt.u.entries)); - else { - i = from_mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE; - while (i-->0) { - page = __get_free_page(GFP_KERNEL|__GFP_ZERO); - if (!page) { - err = -ENOMEM; - break; - } - new_mm->arch.ldt.u.pages[i] = - (struct ldt_entry *) page; - memcpy(new_mm->arch.ldt.u.pages[i], - from_mm->arch.ldt.u.pages[i], PAGE_SIZE); + /* + * Our local LDT is used to supply the data for + * modify_ldt(READLDT), if PTRACE_LDT isn't available, + * i.e., we have to use the stub for modify_ldt, which + * can't handle the big read buffer of up to 64kB. + */ + mutex_lock(&from_mm->arch.ldt.lock); + if (from_mm->arch.ldt.entry_count <= LDT_DIRECT_ENTRIES) + memcpy(new_mm->arch.ldt.u.entries, from_mm->arch.ldt.u.entries, + sizeof(new_mm->arch.ldt.u.entries)); + else { + i = from_mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE; + while (i-->0) { + page = __get_free_page(GFP_KERNEL|__GFP_ZERO); + if (!page) { + err = -ENOMEM; + break; } + new_mm->arch.ldt.u.pages[i] = + (struct ldt_entry *) page; + memcpy(new_mm->arch.ldt.u.pages[i], + from_mm->arch.ldt.u.pages[i], PAGE_SIZE); } - new_mm->arch.ldt.entry_count = from_mm->arch.ldt.entry_count; - mutex_unlock(&from_mm->arch.ldt.lock); } + new_mm->arch.ldt.entry_count = from_mm->arch.ldt.entry_count; + mutex_unlock(&from_mm->arch.ldt.lock); out: return err; @@ -488,7 +357,7 @@ void free_ldt(struct mm_context *mm) { int i; - if (!ptrace_ldt && mm->arch.ldt.entry_count > LDT_DIRECT_ENTRIES) { + if (mm->arch.ldt.entry_count > LDT_DIRECT_ENTRIES) { i = mm->arch.ldt.entry_count / LDT_ENTRIES_PER_PAGE; while (i-- > 0) free_page((long) mm->arch.ldt.u.pages[i]); diff --git a/arch/x86/um/shared/sysdep/faultinfo_32.h b/arch/x86/um/shared/sysdep/faultinfo_32.h index a26086b8a800..b6f2437ec29c 100644 --- a/arch/x86/um/shared/sysdep/faultinfo_32.h +++ b/arch/x86/um/shared/sysdep/faultinfo_32.h @@ -27,9 +27,6 @@ struct faultinfo { /* This is Page Fault */ #define SEGV_IS_FIXABLE(fi) ((fi)->trap_no == 14) -/* SKAS3 has no trap_no on i386, but get_skas_faultinfo() sets it to 0. */ -#define SEGV_MAYBE_FIXABLE(fi) ((fi)->trap_no == 0 && ptrace_faultinfo) - #define PTRACE_FULL_FAULTINFO 0 #endif diff --git a/arch/x86/um/shared/sysdep/faultinfo_64.h b/arch/x86/um/shared/sysdep/faultinfo_64.h index f811cbe15d62..ee88f88974ea 100644 --- a/arch/x86/um/shared/sysdep/faultinfo_64.h +++ b/arch/x86/um/shared/sysdep/faultinfo_64.h @@ -27,9 +27,6 @@ struct faultinfo { /* This is Page Fault */ #define SEGV_IS_FIXABLE(fi) ((fi)->trap_no == 14) -/* No broken SKAS API, which doesn't pass trap_no, here. */ -#define SEGV_MAYBE_FIXABLE(fi) 0 - #define PTRACE_FULL_FAULTINFO 1 #endif diff --git a/arch/x86/um/shared/sysdep/skas_ptrace.h b/arch/x86/um/shared/sysdep/skas_ptrace.h deleted file mode 100644 index 453febe98993..000000000000 --- a/arch/x86/um/shared/sysdep/skas_ptrace.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) - * Licensed under the GPL - */ - -#ifndef __SYSDEP_X86_SKAS_PTRACE_H -#define __SYSDEP_X86_SKAS_PTRACE_H - -struct ptrace_faultinfo { - int is_write; - unsigned long addr; -}; - -struct ptrace_ldt { - int func; - void *ptr; - unsigned long bytecount; -}; - -#define PTRACE_LDT 54 - -#endif diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c index 0c8c32bfd792..592491d1d70d 100644 --- a/arch/x86/um/signal.c +++ b/arch/x86/um/signal.c @@ -549,13 +549,6 @@ int setup_signal_stack_si(unsigned long stack_top, struct ksignal *ksig, if (err) return err; - /* Set up registers for signal handler */ - { - struct exec_domain *ed = current_thread_info()->exec_domain; - if (unlikely(ed && ed->signal_invmap && sig < 32)) - sig = ed->signal_invmap[sig]; - } - PT_REGS_SP(regs) = (unsigned long) frame; PT_REGS_DI(regs) = sig; /* In case the signal handler was declared without prototypes */ diff --git a/arch/xtensa/include/asm/thread_info.h b/arch/xtensa/include/asm/thread_info.h index a9b5d3ba196c..9ad12c617184 100644 --- a/arch/xtensa/include/asm/thread_info.h +++ b/arch/xtensa/include/asm/thread_info.h @@ -44,7 +44,6 @@ typedef struct xtregs_coprocessor { struct thread_info { struct task_struct *task; /* main task structure */ - struct exec_domain *exec_domain; /* execution domain */ unsigned long flags; /* low level flags */ unsigned long status; /* thread-synchronous flags */ __u32 cpu; /* current CPU */ @@ -61,17 +60,6 @@ struct thread_info { xtregs_user_t xtregs_user; }; -#else /* !__ASSEMBLY__ */ - -/* offsets into the thread_info struct for assembly code access */ -#define TI_TASK 0x00000000 -#define TI_EXEC_DOMAIN 0x00000004 -#define TI_FLAGS 0x00000008 -#define TI_STATUS 0x0000000C -#define TI_CPU 0x00000010 -#define TI_PRE_COUNT 0x00000014 -#define TI_ADDR_LIMIT 0x00000018 - #endif /* @@ -83,7 +71,6 @@ struct thread_info { #define INIT_THREAD_INFO(tsk) \ { \ .task = &tsk, \ - .exec_domain = &default_exec_domain, \ .flags = 0, \ .cpu = 0, \ .preempt_count = INIT_PREEMPT_COUNT, \ diff --git a/arch/xtensa/kernel/asm-offsets.c b/arch/xtensa/kernel/asm-offsets.c index 1915c7c889ba..b123ace3b67c 100644 --- a/arch/xtensa/kernel/asm-offsets.c +++ b/arch/xtensa/kernel/asm-offsets.c @@ -77,6 +77,14 @@ int main(void) DEFINE(TASK_THREAD_INFO, offsetof (struct task_struct, stack)); DEFINE(TASK_STRUCT_SIZE, sizeof (struct task_struct)); + /* offsets in thread_info struct */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_STSTUS, thread_info, status); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PRE_COUNT, thread_info, preempt_count); + OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit); + /* struct thread_info (offset from start_struct) */ DEFINE(THREAD_RA, offsetof (struct task_struct, thread.ra)); DEFINE(THREAD_SP, offsetof (struct task_struct, thread.sp)); diff --git a/arch/xtensa/kernel/signal.c b/arch/xtensa/kernel/signal.c index 6b3790445cbe..e87adaa07ff3 100644 --- a/arch/xtensa/kernel/signal.c +++ b/arch/xtensa/kernel/signal.c @@ -336,7 +336,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, { struct rt_sigframe *frame; int err = 0, sig = ksig->sig; - int signal; unsigned long sp, ra, tp; sp = regs->areg[1]; @@ -354,12 +353,6 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, return -EFAULT; } - signal = current_thread_info()->exec_domain - && current_thread_info()->exec_domain->signal_invmap - && sig < 32 - ? current_thread_info()->exec_domain->signal_invmap[sig] - : sig; - if (ksig->ka.sa.sa_flags & SA_SIGINFO) { err |= copy_siginfo_to_user(&frame->info, &ksig->info); } @@ -400,14 +393,14 @@ static int setup_frame(struct ksignal *ksig, sigset_t *set, * Note: PS.CALLINC is set to one by start_thread */ regs->areg[4] = (((unsigned long) ra) & 0x3fffffff) | 0x40000000; - regs->areg[6] = (unsigned long) signal; + regs->areg[6] = (unsigned long) sig; regs->areg[7] = (unsigned long) &frame->info; regs->areg[8] = (unsigned long) &frame->uc; regs->threadptr = tp; #if DEBUG_SIG printk("SIG rt deliver (%s:%d): signal=%d sp=%p pc=%08x\n", - current->comm, current->pid, signal, frame, regs->pc); + current->comm, current->pid, sig, frame, regs->pc); #endif return 0; diff --git a/crypto/Kconfig b/crypto/Kconfig index 50f4da44a304..8aaf298a80e1 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -436,6 +436,14 @@ config CRYPTO_MD5_OCTEON MD5 message digest algorithm (RFC1321) implemented using OCTEON crypto instructions, when available. +config CRYPTO_MD5_PPC + tristate "MD5 digest algorithm (PPC)" + depends on PPC + select CRYPTO_HASH + help + MD5 message digest algorithm (RFC1321) implemented + in PPC assembler. + config CRYPTO_MD5_SPARC64 tristate "MD5 digest algorithm (SPARC64)" depends on SPARC64 @@ -546,34 +554,23 @@ config CRYPTO_SHA512_SSSE3 Extensions version 1 (AVX1), or Advanced Vector Extensions version 2 (AVX2) instructions, when available. -config CRYPTO_SHA1_SPARC64 - tristate "SHA1 digest algorithm (SPARC64)" - depends on SPARC64 - select CRYPTO_SHA1 - select CRYPTO_HASH - help - SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented - using sparc64 crypto instructions, when available. - -config CRYPTO_SHA1_ARM - tristate "SHA1 digest algorithm (ARM-asm)" - depends on ARM +config CRYPTO_SHA1_OCTEON + tristate "SHA1 digest algorithm (OCTEON)" + depends on CPU_CAVIUM_OCTEON select CRYPTO_SHA1 select CRYPTO_HASH help SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented - using optimized ARM assembler. + using OCTEON crypto instructions, when available. -config CRYPTO_SHA1_ARM_NEON - tristate "SHA1 digest algorithm (ARM NEON)" - depends on ARM && KERNEL_MODE_NEON - select CRYPTO_SHA1_ARM +config CRYPTO_SHA1_SPARC64 + tristate "SHA1 digest algorithm (SPARC64)" + depends on SPARC64 select CRYPTO_SHA1 select CRYPTO_HASH help SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2) implemented - using optimized ARM NEON assembly, when NEON instructions are - available. + using sparc64 crypto instructions, when available. config CRYPTO_SHA1_PPC tristate "SHA1 digest algorithm (powerpc)" @@ -582,6 +579,13 @@ config CRYPTO_SHA1_PPC This is the powerpc hardware accelerated implementation of the SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2). +config CRYPTO_SHA1_PPC_SPE + tristate "SHA1 digest algorithm (PPC SPE)" + depends on PPC && SPE + help + SHA-1 secure hash standard (DFIPS 180-4) implemented + using powerpc SPE SIMD instruction set. + config CRYPTO_SHA1_MB tristate "SHA1 digest algorithm (x86_64 Multi-Buffer, Experimental)" depends on X86 && 64BIT @@ -610,6 +614,24 @@ config CRYPTO_SHA256 This code also includes SHA-224, a 224 bit hash with 112 bits of security against collision attacks. +config CRYPTO_SHA256_PPC_SPE + tristate "SHA224 and SHA256 digest algorithm (PPC SPE)" + depends on PPC && SPE + select CRYPTO_SHA256 + select CRYPTO_HASH + help + SHA224 and SHA256 secure hash standard (DFIPS 180-2) + implemented using powerpc SPE SIMD instruction set. + +config CRYPTO_SHA256_OCTEON + tristate "SHA224 and SHA256 digest algorithm (OCTEON)" + depends on CPU_CAVIUM_OCTEON + select CRYPTO_SHA256 + select CRYPTO_HASH + help + SHA-256 secure hash standard (DFIPS 180-2) implemented + using OCTEON crypto instructions, when available. + config CRYPTO_SHA256_SPARC64 tristate "SHA224 and SHA256 digest algorithm (SPARC64)" depends on SPARC64 @@ -631,29 +653,23 @@ config CRYPTO_SHA512 This code also includes SHA-384, a 384 bit hash with 192 bits of security against collision attacks. -config CRYPTO_SHA512_SPARC64 - tristate "SHA384 and SHA512 digest algorithm (SPARC64)" - depends on SPARC64 +config CRYPTO_SHA512_OCTEON + tristate "SHA384 and SHA512 digest algorithms (OCTEON)" + depends on CPU_CAVIUM_OCTEON select CRYPTO_SHA512 select CRYPTO_HASH help SHA-512 secure hash standard (DFIPS 180-2) implemented - using sparc64 crypto instructions, when available. + using OCTEON crypto instructions, when available. -config CRYPTO_SHA512_ARM_NEON - tristate "SHA384 and SHA512 digest algorithm (ARM NEON)" - depends on ARM && KERNEL_MODE_NEON +config CRYPTO_SHA512_SPARC64 + tristate "SHA384 and SHA512 digest algorithm (SPARC64)" + depends on SPARC64 select CRYPTO_SHA512 select CRYPTO_HASH help SHA-512 secure hash standard (DFIPS 180-2) implemented - using ARM NEON instructions, when available. - - This version of SHA implements a 512 bit hash with 256 bits of - security against collision attacks. - - This code also includes SHA-384, a 384 bit hash with 192 bits - of security against collision attacks. + using sparc64 crypto instructions, when available. config CRYPTO_TGR192 tristate "Tiger digest algorithms" @@ -817,45 +833,18 @@ config CRYPTO_AES_SPARC64 for some popular block cipher mode is supported too, including ECB and CBC. -config CRYPTO_AES_ARM - tristate "AES cipher algorithms (ARM-asm)" - depends on ARM - select CRYPTO_ALGAPI - select CRYPTO_AES - help - Use optimized AES assembler routines for ARM platforms. - - 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_AES_ARM_BS - tristate "Bit sliced AES using NEON instructions" - depends on ARM && KERNEL_MODE_NEON - select CRYPTO_ALGAPI - select CRYPTO_AES_ARM - select CRYPTO_ABLK_HELPER +config CRYPTO_AES_PPC_SPE + tristate "AES cipher algorithms (PPC SPE)" + depends on PPC && SPE help - Use a faster and more secure NEON based implementation of AES in CBC, - CTR and XTS modes - - Bit sliced AES gives around 45% speedup on Cortex-A15 for CTR mode - and for XTS mode encryption, CBC and XTS mode decryption speedup is - around 25%. (CBC encryption speed is not affected by this driver.) - This implementation does not rely on any lookup tables so it is - believed to be invulnerable to cache timing attacks. + AES cipher algorithms (FIPS-197). Additionally the acceleration + for popular block cipher modes ECB, CBC, CTR and XTS is supported. + This module should only be used for low power (router) devices + without hardware AES acceleration (e.g. caam crypto). It reduces the + size of the AES tables from 16KB to 8KB + 256 bytes and mitigates + timining attacks. Nevertheless it might be not as secure as other + architecture specific assembler implementations that work on 1KB + tables or 256 bytes S-boxes. config CRYPTO_ANUBIS tristate "Anubis cipher algorithm" @@ -1199,7 +1188,7 @@ config CRYPTO_SERPENT_SSE2_X86_64 Keys are allowed to be from 0 to 256 bits in length, in steps of 8 bits. - This module provides Serpent cipher algorithm that processes eigth + This module provides Serpent cipher algorithm that processes eight blocks parallel using SSE2 instruction set. See also: @@ -1523,6 +1512,15 @@ config CRYPTO_USER_API_RNG This option enables the user-spaces interface for random number generator algorithms. +config CRYPTO_USER_API_AEAD + tristate "User-space interface for AEAD cipher algorithms" + depends on NET + select CRYPTO_AEAD + select CRYPTO_USER_API + help + This option enables the user-spaces interface for AEAD + cipher algorithms. + config CRYPTO_HASH_INFO bool diff --git a/crypto/Makefile b/crypto/Makefile index ba19465f9ad3..97b7d3ac87e7 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_CRYPTO_USER_API) += af_alg.o obj-$(CONFIG_CRYPTO_USER_API_HASH) += algif_hash.o obj-$(CONFIG_CRYPTO_USER_API_SKCIPHER) += algif_skcipher.o obj-$(CONFIG_CRYPTO_USER_API_RNG) += algif_rng.o +obj-$(CONFIG_CRYPTO_USER_API_AEAD) += algif_aead.o # # generic algorithms and the async_tx api diff --git a/crypto/ablk_helper.c b/crypto/ablk_helper.c index ffe7278d4bd8..e1fcf53bb931 100644 --- a/crypto/ablk_helper.c +++ b/crypto/ablk_helper.c @@ -124,7 +124,8 @@ int ablk_init_common(struct crypto_tfm *tfm, const char *drv_name) struct async_helper_ctx *ctx = crypto_tfm_ctx(tfm); struct cryptd_ablkcipher *cryptd_tfm; - cryptd_tfm = cryptd_alloc_ablkcipher(drv_name, 0, 0); + cryptd_tfm = cryptd_alloc_ablkcipher(drv_name, CRYPTO_ALG_INTERNAL, + CRYPTO_ALG_INTERNAL); if (IS_ERR(cryptd_tfm)) return PTR_ERR(cryptd_tfm); diff --git a/crypto/algapi.c b/crypto/algapi.c index 83b04e0884b1..2d0a1c64ce39 100644 --- a/crypto/algapi.c +++ b/crypto/algapi.c @@ -64,6 +64,8 @@ static int crypto_check_alg(struct crypto_alg *alg) if (alg->cra_priority < 0) return -EINVAL; + atomic_set(&alg->cra_refcnt, 1); + return crypto_set_driver_name(alg); } @@ -99,10 +101,9 @@ static struct list_head *crypto_more_spawns(struct crypto_alg *alg, return &n->list == stack ? top : &n->inst->alg.cra_users; } -static void crypto_remove_spawn(struct crypto_spawn *spawn, - struct list_head *list) +static void crypto_remove_instance(struct crypto_instance *inst, + struct list_head *list) { - struct crypto_instance *inst = spawn->inst; struct crypto_template *tmpl = inst->tmpl; if (crypto_is_dead(&inst->alg)) @@ -167,7 +168,7 @@ void crypto_remove_spawns(struct crypto_alg *alg, struct list_head *list, if (spawn->alg) list_move(&spawn->list, &spawn->alg->cra_users); else - crypto_remove_spawn(spawn, list); + crypto_remove_instance(spawn->inst, list); } } EXPORT_SYMBOL_GPL(crypto_remove_spawns); @@ -188,7 +189,6 @@ static struct crypto_larval *__crypto_register_alg(struct crypto_alg *alg) ret = -EEXIST; - atomic_set(&alg->cra_refcnt, 1); list_for_each_entry(q, &crypto_alg_list, cra_list) { if (q == alg) goto err; @@ -523,7 +523,10 @@ int crypto_register_instance(struct crypto_template *tmpl, err = crypto_check_alg(&inst->alg); if (err) - goto err; + return err; + + if (unlikely(!crypto_mod_get(&inst->alg))) + return -EAGAIN; inst->alg.cra_module = tmpl->module; inst->alg.cra_flags |= CRYPTO_ALG_INSTANCE; @@ -545,37 +548,30 @@ unlock: goto err; crypto_wait_for_test(larval); + + /* Remove instance if test failed */ + if (!(inst->alg.cra_flags & CRYPTO_ALG_TESTED)) + crypto_unregister_instance(inst); err = 0; err: + crypto_mod_put(&inst->alg); return err; } EXPORT_SYMBOL_GPL(crypto_register_instance); -int crypto_unregister_instance(struct crypto_alg *alg) +int crypto_unregister_instance(struct crypto_instance *inst) { - int err; - struct crypto_instance *inst = (void *)alg; - struct crypto_template *tmpl = inst->tmpl; - LIST_HEAD(users); - - if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) - return -EINVAL; - - BUG_ON(atomic_read(&alg->cra_refcnt) != 1); + LIST_HEAD(list); down_write(&crypto_alg_sem); - hlist_del_init(&inst->list); - err = crypto_remove_alg(alg, &users); + crypto_remove_spawns(&inst->alg, &list, NULL); + crypto_remove_instance(inst, &list); up_write(&crypto_alg_sem); - if (err) - return err; - - tmpl->free(inst); - crypto_remove_final(&users); + crypto_remove_final(&list); return 0; } diff --git a/crypto/algif_aead.c b/crypto/algif_aead.c new file mode 100644 index 000000000000..00a6fe166fed --- /dev/null +++ b/crypto/algif_aead.c @@ -0,0 +1,664 @@ +/* + * algif_aead: User-space interface for AEAD algorithms + * + * Copyright (C) 2014, Stephan Mueller <smueller@chronox.de> + * + * This file provides the user-space API for AEAD ciphers. + * + * This file is derived from algif_skcipher.c. + * + * 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 <crypto/scatterwalk.h> +#include <crypto/if_alg.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/net.h> +#include <net/sock.h> + +struct aead_sg_list { + unsigned int cur; + struct scatterlist sg[ALG_MAX_PAGES]; +}; + +struct aead_ctx { + struct aead_sg_list tsgl; + /* + * RSGL_MAX_ENTRIES is an artificial limit where user space at maximum + * can cause the kernel to allocate RSGL_MAX_ENTRIES * ALG_MAX_PAGES + * bytes + */ +#define RSGL_MAX_ENTRIES ALG_MAX_PAGES + struct af_alg_sgl rsgl[RSGL_MAX_ENTRIES]; + + void *iv; + + struct af_alg_completion completion; + + unsigned long used; + + unsigned int len; + bool more; + bool merge; + bool enc; + + size_t aead_assoclen; + struct aead_request aead_req; +}; + +static inline int aead_sndbuf(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + + return max_t(int, max_t(int, sk->sk_sndbuf & PAGE_MASK, PAGE_SIZE) - + ctx->used, 0); +} + +static inline bool aead_writable(struct sock *sk) +{ + return PAGE_SIZE <= aead_sndbuf(sk); +} + +static inline bool aead_sufficient_data(struct aead_ctx *ctx) +{ + unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req)); + + return (ctx->used >= (ctx->aead_assoclen + (ctx->enc ? 0 : as))); +} + +static void aead_put_sgl(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + struct aead_sg_list *sgl = &ctx->tsgl; + struct scatterlist *sg = sgl->sg; + unsigned int i; + + for (i = 0; i < sgl->cur; i++) { + if (!sg_page(sg + i)) + continue; + + put_page(sg_page(sg + i)); + sg_assign_page(sg + i, NULL); + } + sgl->cur = 0; + ctx->used = 0; + ctx->more = 0; + ctx->merge = 0; +} + +static void aead_wmem_wakeup(struct sock *sk) +{ + struct socket_wq *wq; + + if (!aead_writable(sk)) + return; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_sync_poll(&wq->wait, POLLIN | + POLLRDNORM | + POLLRDBAND); + sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN); + rcu_read_unlock(); +} + +static int aead_wait_for_data(struct sock *sk, unsigned flags) +{ + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + long timeout; + DEFINE_WAIT(wait); + int err = -ERESTARTSYS; + + if (flags & MSG_DONTWAIT) + return -EAGAIN; + + set_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + + for (;;) { + if (signal_pending(current)) + break; + prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + timeout = MAX_SCHEDULE_TIMEOUT; + if (sk_wait_event(sk, &timeout, !ctx->more)) { + err = 0; + break; + } + } + finish_wait(sk_sleep(sk), &wait); + + clear_bit(SOCK_ASYNC_WAITDATA, &sk->sk_socket->flags); + + return err; +} + +static void aead_data_wakeup(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + struct socket_wq *wq; + + if (ctx->more) + return; + if (!ctx->used) + return; + + rcu_read_lock(); + wq = rcu_dereference(sk->sk_wq); + if (wq_has_sleeper(wq)) + wake_up_interruptible_sync_poll(&wq->wait, POLLOUT | + POLLRDNORM | + POLLRDBAND); + sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT); + rcu_read_unlock(); +} + +static int aead_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + unsigned ivsize = + crypto_aead_ivsize(crypto_aead_reqtfm(&ctx->aead_req)); + struct aead_sg_list *sgl = &ctx->tsgl; + struct af_alg_control con = {}; + long copied = 0; + bool enc = 0; + bool init = 0; + int err = -EINVAL; + + if (msg->msg_controllen) { + err = af_alg_cmsg_send(msg, &con); + if (err) + return err; + + init = 1; + switch (con.op) { + case ALG_OP_ENCRYPT: + enc = 1; + break; + case ALG_OP_DECRYPT: + enc = 0; + break; + default: + return -EINVAL; + } + + if (con.iv && con.iv->ivlen != ivsize) + return -EINVAL; + } + + lock_sock(sk); + if (!ctx->more && ctx->used) + goto unlock; + + if (init) { + ctx->enc = enc; + if (con.iv) + memcpy(ctx->iv, con.iv->iv, ivsize); + + ctx->aead_assoclen = con.aead_assoclen; + } + + while (size) { + unsigned long len = size; + struct scatterlist *sg = NULL; + + /* use the existing memory in an allocated page */ + if (ctx->merge) { + sg = sgl->sg + sgl->cur - 1; + len = min_t(unsigned long, len, + PAGE_SIZE - sg->offset - sg->length); + err = memcpy_from_msg(page_address(sg_page(sg)) + + sg->offset + sg->length, + msg, len); + if (err) + goto unlock; + + sg->length += len; + ctx->merge = (sg->offset + sg->length) & + (PAGE_SIZE - 1); + + ctx->used += len; + copied += len; + size -= len; + continue; + } + + if (!aead_writable(sk)) { + /* user space sent too much data */ + aead_put_sgl(sk); + err = -EMSGSIZE; + goto unlock; + } + + /* allocate a new page */ + len = min_t(unsigned long, size, aead_sndbuf(sk)); + while (len) { + int plen = 0; + + if (sgl->cur >= ALG_MAX_PAGES) { + aead_put_sgl(sk); + err = -E2BIG; + goto unlock; + } + + sg = sgl->sg + sgl->cur; + plen = min_t(int, len, PAGE_SIZE); + + sg_assign_page(sg, alloc_page(GFP_KERNEL)); + err = -ENOMEM; + if (!sg_page(sg)) + goto unlock; + + err = memcpy_from_msg(page_address(sg_page(sg)), + msg, plen); + if (err) { + __free_page(sg_page(sg)); + sg_assign_page(sg, NULL); + goto unlock; + } + + sg->offset = 0; + sg->length = plen; + len -= plen; + ctx->used += plen; + copied += plen; + sgl->cur++; + size -= plen; + ctx->merge = plen & (PAGE_SIZE - 1); + } + } + + err = 0; + + ctx->more = msg->msg_flags & MSG_MORE; + if (!ctx->more && !aead_sufficient_data(ctx)) { + aead_put_sgl(sk); + err = -EMSGSIZE; + } + +unlock: + aead_data_wakeup(sk); + release_sock(sk); + + return err ?: copied; +} + +static ssize_t aead_sendpage(struct socket *sock, struct page *page, + int offset, size_t size, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + struct aead_sg_list *sgl = &ctx->tsgl; + int err = -EINVAL; + + if (flags & MSG_SENDPAGE_NOTLAST) + flags |= MSG_MORE; + + if (sgl->cur >= ALG_MAX_PAGES) + return -E2BIG; + + lock_sock(sk); + if (!ctx->more && ctx->used) + goto unlock; + + if (!size) + goto done; + + if (!aead_writable(sk)) { + /* user space sent too much data */ + aead_put_sgl(sk); + err = -EMSGSIZE; + goto unlock; + } + + ctx->merge = 0; + + get_page(page); + sg_set_page(sgl->sg + sgl->cur, page, size, offset); + sgl->cur++; + ctx->used += size; + + err = 0; + +done: + ctx->more = flags & MSG_MORE; + if (!ctx->more && !aead_sufficient_data(ctx)) { + aead_put_sgl(sk); + err = -EMSGSIZE; + } + +unlock: + aead_data_wakeup(sk); + release_sock(sk); + + return err ?: size; +} + +static int aead_recvmsg(struct socket *sock, struct msghdr *msg, size_t ignored, int flags) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + unsigned bs = crypto_aead_blocksize(crypto_aead_reqtfm(&ctx->aead_req)); + unsigned as = crypto_aead_authsize(crypto_aead_reqtfm(&ctx->aead_req)); + struct aead_sg_list *sgl = &ctx->tsgl; + struct scatterlist *sg = NULL; + struct scatterlist assoc[ALG_MAX_PAGES]; + size_t assoclen = 0; + unsigned int i = 0; + int err = -EINVAL; + unsigned long used = 0; + size_t outlen = 0; + size_t usedpages = 0; + unsigned int cnt = 0; + + /* Limit number of IOV blocks to be accessed below */ + if (msg->msg_iter.nr_segs > RSGL_MAX_ENTRIES) + return -ENOMSG; + + lock_sock(sk); + + /* + * AEAD memory structure: For encryption, the tag is appended to the + * ciphertext which implies that the memory allocated for the ciphertext + * must be increased by the tag length. For decryption, the tag + * is expected to be concatenated to the ciphertext. The plaintext + * therefore has a memory size of the ciphertext minus the tag length. + * + * The memory structure for cipher operation has the following + * structure: + * AEAD encryption input: assoc data || plaintext + * AEAD encryption output: cipherntext || auth tag + * AEAD decryption input: assoc data || ciphertext || auth tag + * AEAD decryption output: plaintext + */ + + if (ctx->more) { + err = aead_wait_for_data(sk, flags); + if (err) + goto unlock; + } + + used = ctx->used; + + /* + * Make sure sufficient data is present -- note, the same check is + * is also present in sendmsg/sendpage. The checks in sendpage/sendmsg + * shall provide an information to the data sender that something is + * wrong, but they are irrelevant to maintain the kernel integrity. + * We need this check here too in case user space decides to not honor + * the error message in sendmsg/sendpage and still call recvmsg. This + * check here protects the kernel integrity. + */ + if (!aead_sufficient_data(ctx)) + goto unlock; + + /* + * The cipher operation input data is reduced by the associated data + * length as this data is processed separately later on. + */ + used -= ctx->aead_assoclen; + + if (ctx->enc) { + /* round up output buffer to multiple of block size */ + outlen = ((used + bs - 1) / bs * bs); + /* add the size needed for the auth tag to be created */ + outlen += as; + } else { + /* output data size is input without the authentication tag */ + outlen = used - as; + /* round up output buffer to multiple of block size */ + outlen = ((outlen + bs - 1) / bs * bs); + } + + /* convert iovecs of output buffers into scatterlists */ + while (iov_iter_count(&msg->msg_iter)) { + size_t seglen = min_t(size_t, iov_iter_count(&msg->msg_iter), + (outlen - usedpages)); + + /* make one iovec available as scatterlist */ + err = af_alg_make_sg(&ctx->rsgl[cnt], &msg->msg_iter, + seglen); + if (err < 0) + goto unlock; + usedpages += err; + /* chain the new scatterlist with initial list */ + if (cnt) + scatterwalk_crypto_chain(ctx->rsgl[0].sg, + ctx->rsgl[cnt].sg, 1, + sg_nents(ctx->rsgl[cnt-1].sg)); + /* we do not need more iovecs as we have sufficient memory */ + if (outlen <= usedpages) + break; + iov_iter_advance(&msg->msg_iter, err); + cnt++; + } + + err = -EINVAL; + /* ensure output buffer is sufficiently large */ + if (usedpages < outlen) + goto unlock; + + sg_init_table(assoc, ALG_MAX_PAGES); + assoclen = ctx->aead_assoclen; + /* + * Split scatterlist into two: first part becomes AD, second part + * is plaintext / ciphertext. The first part is assigned to assoc + * scatterlist. When this loop finishes, sg points to the start of the + * plaintext / ciphertext. + */ + for (i = 0; i < ctx->tsgl.cur; i++) { + sg = sgl->sg + i; + if (sg->length <= assoclen) { + /* AD is larger than one page */ + sg_set_page(assoc + i, sg_page(sg), + sg->length, sg->offset); + assoclen -= sg->length; + if (i >= ctx->tsgl.cur) + goto unlock; + } else if (!assoclen) { + /* current page is to start of plaintext / ciphertext */ + if (i) + /* AD terminates at page boundary */ + sg_mark_end(assoc + i - 1); + else + /* AD size is zero */ + sg_mark_end(assoc); + break; + } else { + /* AD does not terminate at page boundary */ + sg_set_page(assoc + i, sg_page(sg), + assoclen, sg->offset); + sg_mark_end(assoc + i); + /* plaintext / ciphertext starts after AD */ + sg->length -= assoclen; + sg->offset += assoclen; + break; + } + } + + aead_request_set_assoc(&ctx->aead_req, assoc, ctx->aead_assoclen); + aead_request_set_crypt(&ctx->aead_req, sg, ctx->rsgl[0].sg, used, + ctx->iv); + + err = af_alg_wait_for_completion(ctx->enc ? + crypto_aead_encrypt(&ctx->aead_req) : + crypto_aead_decrypt(&ctx->aead_req), + &ctx->completion); + + if (err) { + /* EBADMSG implies a valid cipher operation took place */ + if (err == -EBADMSG) + aead_put_sgl(sk); + goto unlock; + } + + aead_put_sgl(sk); + + err = 0; + +unlock: + for (i = 0; i < cnt; i++) + af_alg_free_sg(&ctx->rsgl[i]); + + aead_wmem_wakeup(sk); + release_sock(sk); + + return err ? err : outlen; +} + +static unsigned int aead_poll(struct file *file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + unsigned int mask; + + sock_poll_wait(file, sk_sleep(sk), wait); + mask = 0; + + if (!ctx->more) + mask |= POLLIN | POLLRDNORM; + + if (aead_writable(sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + + return mask; +} + +static struct proto_ops algif_aead_ops = { + .family = PF_ALG, + + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .getname = sock_no_getname, + .ioctl = sock_no_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .getsockopt = sock_no_getsockopt, + .mmap = sock_no_mmap, + .bind = sock_no_bind, + .accept = sock_no_accept, + .setsockopt = sock_no_setsockopt, + + .release = af_alg_release, + .sendmsg = aead_sendmsg, + .sendpage = aead_sendpage, + .recvmsg = aead_recvmsg, + .poll = aead_poll, +}; + +static void *aead_bind(const char *name, u32 type, u32 mask) +{ + return crypto_alloc_aead(name, type, mask); +} + +static void aead_release(void *private) +{ + crypto_free_aead(private); +} + +static int aead_setauthsize(void *private, unsigned int authsize) +{ + return crypto_aead_setauthsize(private, authsize); +} + +static int aead_setkey(void *private, const u8 *key, unsigned int keylen) +{ + return crypto_aead_setkey(private, key, keylen); +} + +static void aead_sock_destruct(struct sock *sk) +{ + struct alg_sock *ask = alg_sk(sk); + struct aead_ctx *ctx = ask->private; + unsigned int ivlen = crypto_aead_ivsize( + crypto_aead_reqtfm(&ctx->aead_req)); + + aead_put_sgl(sk); + sock_kzfree_s(sk, ctx->iv, ivlen); + sock_kfree_s(sk, ctx, ctx->len); + af_alg_release_parent(sk); +} + +static int aead_accept_parent(void *private, struct sock *sk) +{ + struct aead_ctx *ctx; + struct alg_sock *ask = alg_sk(sk); + unsigned int len = sizeof(*ctx) + crypto_aead_reqsize(private); + unsigned int ivlen = crypto_aead_ivsize(private); + + ctx = sock_kmalloc(sk, len, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + memset(ctx, 0, len); + + ctx->iv = sock_kmalloc(sk, ivlen, GFP_KERNEL); + if (!ctx->iv) { + sock_kfree_s(sk, ctx, len); + return -ENOMEM; + } + memset(ctx->iv, 0, ivlen); + + ctx->len = len; + ctx->used = 0; + ctx->more = 0; + ctx->merge = 0; + ctx->enc = 0; + ctx->tsgl.cur = 0; + ctx->aead_assoclen = 0; + af_alg_init_completion(&ctx->completion); + sg_init_table(ctx->tsgl.sg, ALG_MAX_PAGES); + + ask->private = ctx; + + aead_request_set_tfm(&ctx->aead_req, private); + aead_request_set_callback(&ctx->aead_req, CRYPTO_TFM_REQ_MAY_BACKLOG, + af_alg_complete, &ctx->completion); + + sk->sk_destruct = aead_sock_destruct; + + return 0; +} + +static const struct af_alg_type algif_type_aead = { + .bind = aead_bind, + .release = aead_release, + .setkey = aead_setkey, + .setauthsize = aead_setauthsize, + .accept = aead_accept_parent, + .ops = &algif_aead_ops, + .name = "aead", + .owner = THIS_MODULE +}; + +static int __init algif_aead_init(void) +{ + return af_alg_register_type(&algif_type_aead); +} + +static void __exit algif_aead_exit(void) +{ + int err = af_alg_unregister_type(&algif_type_aead); + BUG_ON(err); +} + +module_init(algif_aead_init); +module_exit(algif_aead_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Stephan Mueller <smueller@chronox.de>"); +MODULE_DESCRIPTION("AEAD kernel crypto API user space interface"); diff --git a/crypto/algif_rng.c b/crypto/algif_rng.c index 3acba0a7cd55..8109aaad2726 100644 --- a/crypto/algif_rng.c +++ b/crypto/algif_rng.c @@ -87,7 +87,7 @@ static int rng_recvmsg(struct socket *sock, struct msghdr *msg, size_t len, return genlen; err = memcpy_to_msg(msg, result, len); - memzero_explicit(result, genlen); + memzero_explicit(result, len); return err ? err : len; } diff --git a/crypto/ansi_cprng.c b/crypto/ansi_cprng.c index 6f5bebc9bf01..765fe7609348 100644 --- a/crypto/ansi_cprng.c +++ b/crypto/ansi_cprng.c @@ -210,7 +210,11 @@ static int get_prng_bytes(char *buf, size_t nbytes, struct prng_context *ctx, byte_count = DEFAULT_BLK_SZ; } - err = byte_count; + /* + * Return 0 in case of success as mandated by the kernel + * crypto API interface definition. + */ + err = 0; dbgprint(KERN_CRIT "getting %d random bytes for context %p\n", byte_count, ctx); diff --git a/crypto/api.c b/crypto/api.c index 2a81e98a0021..afe4610afc4b 100644 --- a/crypto/api.c +++ b/crypto/api.c @@ -257,6 +257,16 @@ struct crypto_alg *crypto_alg_mod_lookup(const char *name, u32 type, u32 mask) mask |= CRYPTO_ALG_TESTED; } + /* + * If the internal flag is set for a cipher, require a caller to + * to invoke the cipher with the internal flag to use that cipher. + * Also, if a caller wants to allocate a cipher that may or may + * not be an internal cipher, use type | CRYPTO_ALG_INTERNAL and + * !(mask & CRYPTO_ALG_INTERNAL). + */ + if (!((type | mask) & CRYPTO_ALG_INTERNAL)) + mask |= CRYPTO_ALG_INTERNAL; + larval = crypto_larval_lookup(name, type, mask); if (IS_ERR(larval) || !crypto_is_larval(larval)) return larval; diff --git a/crypto/cryptd.c b/crypto/cryptd.c index 650afac10fd7..b0602ba03111 100644 --- a/crypto/cryptd.c +++ b/crypto/cryptd.c @@ -168,6 +168,20 @@ static inline struct cryptd_queue *cryptd_get_queue(struct crypto_tfm *tfm) return ictx->queue; } +static inline void cryptd_check_internal(struct rtattr **tb, u32 *type, + u32 *mask) +{ + struct crypto_attr_type *algt; + + algt = crypto_get_attr_type(tb); + if (IS_ERR(algt)) + return; + if ((algt->type & CRYPTO_ALG_INTERNAL)) + *type |= CRYPTO_ALG_INTERNAL; + if ((algt->mask & CRYPTO_ALG_INTERNAL)) + *mask |= CRYPTO_ALG_INTERNAL; +} + static int cryptd_blkcipher_setkey(struct crypto_ablkcipher *parent, const u8 *key, unsigned int keylen) { @@ -321,10 +335,13 @@ static int cryptd_create_blkcipher(struct crypto_template *tmpl, struct cryptd_instance_ctx *ctx; struct crypto_instance *inst; struct crypto_alg *alg; + u32 type = CRYPTO_ALG_TYPE_BLKCIPHER; + u32 mask = CRYPTO_ALG_TYPE_MASK; int err; - alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_BLKCIPHER, - CRYPTO_ALG_TYPE_MASK); + cryptd_check_internal(tb, &type, &mask); + + alg = crypto_get_attr_alg(tb, type, mask); if (IS_ERR(alg)) return PTR_ERR(alg); @@ -341,7 +358,10 @@ static int cryptd_create_blkcipher(struct crypto_template *tmpl, if (err) goto out_free_inst; - inst->alg.cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC; + type = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC; + if (alg->cra_flags & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_INTERNAL; + inst->alg.cra_flags = type; inst->alg.cra_type = &crypto_ablkcipher_type; inst->alg.cra_ablkcipher.ivsize = alg->cra_blkcipher.ivsize; @@ -577,9 +597,13 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, struct ahash_instance *inst; struct shash_alg *salg; struct crypto_alg *alg; + u32 type = 0; + u32 mask = 0; int err; - salg = shash_attr_alg(tb[1], 0, 0); + cryptd_check_internal(tb, &type, &mask); + + salg = shash_attr_alg(tb[1], type, mask); if (IS_ERR(salg)) return PTR_ERR(salg); @@ -598,7 +622,10 @@ static int cryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, if (err) goto out_free_inst; - inst->alg.halg.base.cra_flags = CRYPTO_ALG_ASYNC; + type = CRYPTO_ALG_ASYNC; + if (alg->cra_flags & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_INTERNAL; + inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; inst->alg.halg.base.cra_ctxsize = sizeof(struct cryptd_hash_ctx); @@ -719,10 +746,13 @@ static int cryptd_create_aead(struct crypto_template *tmpl, struct aead_instance_ctx *ctx; struct crypto_instance *inst; struct crypto_alg *alg; + u32 type = CRYPTO_ALG_TYPE_AEAD; + u32 mask = CRYPTO_ALG_TYPE_MASK; int err; - alg = crypto_get_attr_alg(tb, CRYPTO_ALG_TYPE_AEAD, - CRYPTO_ALG_TYPE_MASK); + cryptd_check_internal(tb, &type, &mask); + + alg = crypto_get_attr_alg(tb, type, mask); if (IS_ERR(alg)) return PTR_ERR(alg); @@ -739,7 +769,10 @@ static int cryptd_create_aead(struct crypto_template *tmpl, if (err) goto out_free_inst; - inst->alg.cra_flags = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC; + type = CRYPTO_ALG_TYPE_AEAD | CRYPTO_ALG_ASYNC; + if (alg->cra_flags & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_INTERNAL; + inst->alg.cra_flags = type; inst->alg.cra_type = alg->cra_type; inst->alg.cra_ctxsize = sizeof(struct cryptd_aead_ctx); inst->alg.cra_init = cryptd_aead_init_tfm; diff --git a/crypto/crypto_user.c b/crypto/crypto_user.c index c5148a35ae0a..41dfe762b7fb 100644 --- a/crypto/crypto_user.c +++ b/crypto/crypto_user.c @@ -62,10 +62,14 @@ static struct crypto_alg *crypto_alg_match(struct crypto_user_alg *p, int exact) else if (!exact) match = !strcmp(q->cra_name, p->cru_name); - if (match) { - alg = q; - break; - } + if (!match) + continue; + + if (unlikely(!crypto_mod_get(q))) + continue; + + alg = q; + break; } up_read(&crypto_alg_sem); @@ -205,9 +209,10 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, if (!alg) return -ENOENT; + err = -ENOMEM; skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); if (!skb) - return -ENOMEM; + goto drop_alg; info.in_skb = in_skb; info.out_skb = skb; @@ -215,6 +220,10 @@ static int crypto_report(struct sk_buff *in_skb, struct nlmsghdr *in_nlh, info.nlmsg_flags = 0; err = crypto_report_alg(alg, &info); + +drop_alg: + crypto_mod_put(alg); + if (err) return err; @@ -284,6 +293,7 @@ static int crypto_update_alg(struct sk_buff *skb, struct nlmsghdr *nlh, up_write(&crypto_alg_sem); + crypto_mod_put(alg); crypto_remove_final(&list); return 0; @@ -294,6 +304,7 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh, { struct crypto_alg *alg; struct crypto_user_alg *p = nlmsg_data(nlh); + int err; if (!netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; @@ -310,13 +321,19 @@ static int crypto_del_alg(struct sk_buff *skb, struct nlmsghdr *nlh, * if we try to unregister. Unregistering such an algorithm without * removing the module is not possible, so we restrict to crypto * instances that are build from templates. */ + err = -EINVAL; if (!(alg->cra_flags & CRYPTO_ALG_INSTANCE)) - return -EINVAL; + goto drop_alg; - if (atomic_read(&alg->cra_refcnt) != 1) - return -EBUSY; + err = -EBUSY; + if (atomic_read(&alg->cra_refcnt) > 2) + goto drop_alg; - return crypto_unregister_instance(alg); + err = crypto_unregister_instance((struct crypto_instance *)alg); + +drop_alg: + crypto_mod_put(alg); + return err; } static struct crypto_alg *crypto_user_skcipher_alg(const char *name, u32 type, @@ -395,8 +412,10 @@ static int crypto_add_alg(struct sk_buff *skb, struct nlmsghdr *nlh, return -EINVAL; alg = crypto_alg_match(p, exact); - if (alg) + if (alg) { + crypto_mod_put(alg); return -EEXIST; + } if (strlen(p->cru_driver_name)) name = p->cru_driver_name; diff --git a/crypto/drbg.c b/crypto/drbg.c index d8ff16e5c322..b69409cb7e6a 100644 --- a/crypto/drbg.c +++ b/crypto/drbg.c @@ -119,19 +119,19 @@ static const struct drbg_core drbg_cores[] = { .statelen = 32, /* 256 bits as defined in 10.2.1 */ .blocklen_bytes = 16, .cra_name = "ctr_aes128", - .backend_cra_name = "ecb(aes)", + .backend_cra_name = "aes", }, { .flags = DRBG_CTR | DRBG_STRENGTH192, .statelen = 40, /* 320 bits as defined in 10.2.1 */ .blocklen_bytes = 16, .cra_name = "ctr_aes192", - .backend_cra_name = "ecb(aes)", + .backend_cra_name = "aes", }, { .flags = DRBG_CTR | DRBG_STRENGTH256, .statelen = 48, /* 384 bits as defined in 10.2.1 */ .blocklen_bytes = 16, .cra_name = "ctr_aes256", - .backend_cra_name = "ecb(aes)", + .backend_cra_name = "aes", }, #endif /* CONFIG_CRYPTO_DRBG_CTR */ #ifdef CONFIG_CRYPTO_DRBG_HASH @@ -308,9 +308,6 @@ static int drbg_ctr_bcc(struct drbg_state *drbg, drbg_string_fill(&data, out, drbg_blocklen(drbg)); - /* 10.4.3 step 1 */ - memset(out, 0, drbg_blocklen(drbg)); - /* 10.4.3 step 2 / 4 */ list_for_each_entry(curr, in, list) { const unsigned char *pos = curr->buf; @@ -406,7 +403,6 @@ static int drbg_ctr_df(struct drbg_state *drbg, memset(pad, 0, drbg_blocklen(drbg)); memset(iv, 0, drbg_blocklen(drbg)); - memset(temp, 0, drbg_statelen(drbg)); /* 10.4.2 step 1 is implicit as we work byte-wise */ @@ -523,7 +519,6 @@ static int drbg_ctr_update(struct drbg_state *drbg, struct list_head *seed, unsigned int len = 0; struct drbg_string cipherin; - memset(temp, 0, drbg_statelen(drbg) + drbg_blocklen(drbg)); if (3 > reseed) memset(df_data, 0, drbg_statelen(drbg)); @@ -585,8 +580,6 @@ static int drbg_ctr_generate(struct drbg_state *drbg, int ret = 0; struct drbg_string data; - memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); - /* 10.2.1.5.2 step 2 */ if (addtl && !list_empty(addtl)) { ret = drbg_ctr_update(drbg, addtl, 2); @@ -761,7 +754,6 @@ static struct drbg_state_ops drbg_hmac_ops = { .generate = drbg_hmac_generate, .crypto_init = drbg_init_hash_kernel, .crypto_fini = drbg_fini_hash_kernel, - }; #endif /* CONFIG_CRYPTO_DRBG_HMAC */ @@ -838,8 +830,6 @@ static int drbg_hash_df(struct drbg_state *drbg, unsigned char *tmp = drbg->scratchpad + drbg_statelen(drbg); struct drbg_string data; - memset(tmp, 0, drbg_blocklen(drbg)); - /* 10.4.1 step 3 */ input[0] = 1; drbg_cpu_to_be32((outlen * 8), &input[1]); @@ -879,7 +869,6 @@ static int drbg_hash_update(struct drbg_state *drbg, struct list_head *seed, unsigned char *V = drbg->scratchpad; unsigned char prefix = DRBG_PREFIX1; - memset(drbg->scratchpad, 0, drbg_statelen(drbg)); if (!seed) return -EINVAL; @@ -921,9 +910,6 @@ static int drbg_hash_process_addtl(struct drbg_state *drbg, LIST_HEAD(datalist); unsigned char prefix = DRBG_PREFIX2; - /* this is value w as per documentation */ - memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); - /* 10.1.1.4 step 2 */ if (!addtl || list_empty(addtl)) return 0; @@ -959,9 +945,6 @@ static int drbg_hash_hashgen(struct drbg_state *drbg, struct drbg_string data; LIST_HEAD(datalist); - memset(src, 0, drbg_statelen(drbg)); - memset(dst, 0, drbg_blocklen(drbg)); - /* 10.1.1.4 step hashgen 2 */ memcpy(src, drbg->V, drbg_statelen(drbg)); @@ -1018,7 +1001,6 @@ static int drbg_hash_generate(struct drbg_state *drbg, len = drbg_hash_hashgen(drbg, buf, buflen); /* this is the value H as documented in 10.1.1.4 */ - memset(drbg->scratchpad, 0, drbg_blocklen(drbg)); /* 10.1.1.4 step 4 */ drbg_string_fill(&data1, &prefix, 1); list_add_tail(&data1.list, &datalist); @@ -1298,7 +1280,7 @@ static void drbg_restore_shadow(struct drbg_state *drbg, * as defined in SP800-90A. The additional input is mixed into * the state in addition to the pulled entropy. * - * return: generated number of bytes + * return: 0 when all bytes are generated; < 0 in case of an error */ static int drbg_generate(struct drbg_state *drbg, unsigned char *buf, unsigned int buflen, @@ -1437,6 +1419,11 @@ static int drbg_generate(struct drbg_state *drbg, } #endif + /* + * All operations were successful, return 0 as mandated by + * the kernel crypto API interface. + */ + len = 0; err: shadow->d_ops->crypto_fini(shadow); drbg_restore_shadow(drbg, &shadow); @@ -1644,24 +1631,24 @@ static int drbg_kcapi_hash(struct drbg_state *drbg, const unsigned char *key, static int drbg_init_sym_kernel(struct drbg_state *drbg) { int ret = 0; - struct crypto_blkcipher *tfm; + struct crypto_cipher *tfm; - tfm = crypto_alloc_blkcipher(drbg->core->backend_cra_name, 0, 0); + tfm = crypto_alloc_cipher(drbg->core->backend_cra_name, 0, 0); if (IS_ERR(tfm)) { pr_info("DRBG: could not allocate cipher TFM handle\n"); return PTR_ERR(tfm); } - BUG_ON(drbg_blocklen(drbg) != crypto_blkcipher_blocksize(tfm)); + BUG_ON(drbg_blocklen(drbg) != crypto_cipher_blocksize(tfm)); drbg->priv_data = tfm; return ret; } static int drbg_fini_sym_kernel(struct drbg_state *drbg) { - struct crypto_blkcipher *tfm = - (struct crypto_blkcipher *)drbg->priv_data; + struct crypto_cipher *tfm = + (struct crypto_cipher *)drbg->priv_data; if (tfm) - crypto_free_blkcipher(tfm); + crypto_free_cipher(tfm); drbg->priv_data = NULL; return 0; } @@ -1669,21 +1656,14 @@ static int drbg_fini_sym_kernel(struct drbg_state *drbg) static int drbg_kcapi_sym(struct drbg_state *drbg, const unsigned char *key, unsigned char *outval, const struct drbg_string *in) { - int ret = 0; - struct scatterlist sg_in, sg_out; - struct blkcipher_desc desc; - struct crypto_blkcipher *tfm = - (struct crypto_blkcipher *)drbg->priv_data; - - desc.tfm = tfm; - desc.flags = 0; - crypto_blkcipher_setkey(tfm, key, (drbg_keylen(drbg))); - /* there is only component in *in */ - sg_init_one(&sg_in, in->buf, in->len); - sg_init_one(&sg_out, outval, drbg_blocklen(drbg)); - ret = crypto_blkcipher_encrypt(&desc, &sg_out, &sg_in, in->len); + struct crypto_cipher *tfm = + (struct crypto_cipher *)drbg->priv_data; - return ret; + crypto_cipher_setkey(tfm, key, (drbg_keylen(drbg))); + /* there is only component in *in */ + BUG_ON(in->len < drbg_blocklen(drbg)); + crypto_cipher_encrypt_one(tfm, outval, in->buf); + return 0; } #endif /* CONFIG_CRYPTO_DRBG_CTR */ diff --git a/crypto/mcryptd.c b/crypto/mcryptd.c index a8e870444ea9..fe5b495a434d 100644 --- a/crypto/mcryptd.c +++ b/crypto/mcryptd.c @@ -258,6 +258,20 @@ out_free_inst: goto out; } +static inline void mcryptd_check_internal(struct rtattr **tb, u32 *type, + u32 *mask) +{ + struct crypto_attr_type *algt; + + algt = crypto_get_attr_type(tb); + if (IS_ERR(algt)) + return; + if ((algt->type & CRYPTO_ALG_INTERNAL)) + *type |= CRYPTO_ALG_INTERNAL; + if ((algt->mask & CRYPTO_ALG_INTERNAL)) + *mask |= CRYPTO_ALG_INTERNAL; +} + static int mcryptd_hash_init_tfm(struct crypto_tfm *tfm) { struct crypto_instance *inst = crypto_tfm_alg_instance(tfm); @@ -480,9 +494,13 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, struct ahash_instance *inst; struct shash_alg *salg; struct crypto_alg *alg; + u32 type = 0; + u32 mask = 0; int err; - salg = shash_attr_alg(tb[1], 0, 0); + mcryptd_check_internal(tb, &type, &mask); + + salg = shash_attr_alg(tb[1], type, mask); if (IS_ERR(salg)) return PTR_ERR(salg); @@ -502,7 +520,10 @@ static int mcryptd_create_hash(struct crypto_template *tmpl, struct rtattr **tb, if (err) goto out_free_inst; - inst->alg.halg.base.cra_flags = CRYPTO_ALG_ASYNC; + type = CRYPTO_ALG_ASYNC; + if (alg->cra_flags & CRYPTO_ALG_INTERNAL) + type |= CRYPTO_ALG_INTERNAL; + inst->alg.halg.base.cra_flags = type; inst->alg.halg.digestsize = salg->digestsize; inst->alg.halg.base.cra_ctxsize = sizeof(struct mcryptd_hash_ctx); diff --git a/crypto/proc.c b/crypto/proc.c index 4a0a7aad2204..4ffe73b51612 100644 --- a/crypto/proc.c +++ b/crypto/proc.c @@ -89,6 +89,9 @@ static int c_show(struct seq_file *m, void *p) seq_printf(m, "selftest : %s\n", (alg->cra_flags & CRYPTO_ALG_TESTED) ? "passed" : "unknown"); + seq_printf(m, "internal : %s\n", + (alg->cra_flags & CRYPTO_ALG_INTERNAL) ? + "yes" : "no"); if (alg->cra_flags & CRYPTO_ALG_LARVAL) { seq_printf(m, "type : larval\n"); diff --git a/crypto/sha1_generic.c b/crypto/sha1_generic.c index a3e50c37eb6f..39e3acc438d9 100644 --- a/crypto/sha1_generic.c +++ b/crypto/sha1_generic.c @@ -23,111 +23,49 @@ #include <linux/cryptohash.h> #include <linux/types.h> #include <crypto/sha.h> +#include <crypto/sha1_base.h> #include <asm/byteorder.h> -static int sha1_init(struct shash_desc *desc) +static void sha1_generic_block_fn(struct sha1_state *sst, u8 const *src, + int blocks) { - struct sha1_state *sctx = shash_desc_ctx(desc); + u32 temp[SHA_WORKSPACE_WORDS]; - *sctx = (struct sha1_state){ - .state = { SHA1_H0, SHA1_H1, SHA1_H2, SHA1_H3, SHA1_H4 }, - }; - - return 0; + while (blocks--) { + sha_transform(sst->state, src, temp); + src += SHA1_BLOCK_SIZE; + } + memzero_explicit(temp, sizeof(temp)); } int crypto_sha1_update(struct shash_desc *desc, const u8 *data, - unsigned int len) + unsigned int len) { - struct sha1_state *sctx = shash_desc_ctx(desc); - unsigned int partial, done; - const u8 *src; - - partial = sctx->count % SHA1_BLOCK_SIZE; - sctx->count += len; - done = 0; - src = data; - - if ((partial + len) >= SHA1_BLOCK_SIZE) { - u32 temp[SHA_WORKSPACE_WORDS]; - - if (partial) { - done = -partial; - memcpy(sctx->buffer + partial, data, - done + SHA1_BLOCK_SIZE); - src = sctx->buffer; - } - - do { - sha_transform(sctx->state, src, temp); - done += SHA1_BLOCK_SIZE; - src = data + done; - } while (done + SHA1_BLOCK_SIZE <= len); - - memzero_explicit(temp, sizeof(temp)); - partial = 0; - } - memcpy(sctx->buffer + partial, src, len - done); - - return 0; + return sha1_base_do_update(desc, data, len, sha1_generic_block_fn); } EXPORT_SYMBOL(crypto_sha1_update); - -/* Add padding and return the message digest. */ static int sha1_final(struct shash_desc *desc, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - u32 i, index, padlen; - __be64 bits; - static const u8 padding[64] = { 0x80, }; - - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64 */ - index = sctx->count & 0x3f; - padlen = (index < 56) ? (56 - index) : ((64+56) - index); - crypto_sha1_update(desc, padding, padlen); - - /* Append length */ - crypto_sha1_update(desc, (const u8 *)&bits, sizeof(bits)); - - /* Store state in digest */ - for (i = 0; i < 5; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Wipe context */ - memset(sctx, 0, sizeof *sctx); - - return 0; + sha1_base_do_finalize(desc, sha1_generic_block_fn); + return sha1_base_finish(desc, out); } -static int sha1_export(struct shash_desc *desc, void *out) +int crypto_sha1_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *out) { - struct sha1_state *sctx = shash_desc_ctx(desc); - - memcpy(out, sctx, sizeof(*sctx)); - return 0; -} - -static int sha1_import(struct shash_desc *desc, const void *in) -{ - struct sha1_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - return 0; + sha1_base_do_update(desc, data, len, sha1_generic_block_fn); + return sha1_final(desc, out); } +EXPORT_SYMBOL(crypto_sha1_finup); static struct shash_alg alg = { .digestsize = SHA1_DIGEST_SIZE, - .init = sha1_init, + .init = sha1_base_init, .update = crypto_sha1_update, .final = sha1_final, - .export = sha1_export, - .import = sha1_import, + .finup = crypto_sha1_finup, .descsize = sizeof(struct sha1_state), - .statesize = sizeof(struct sha1_state), .base = { .cra_name = "sha1", .cra_driver_name= "sha1-generic", diff --git a/crypto/sha256_generic.c b/crypto/sha256_generic.c index b001ff5c2efc..78431163ed3c 100644 --- a/crypto/sha256_generic.c +++ b/crypto/sha256_generic.c @@ -23,6 +23,7 @@ #include <linux/mm.h> #include <linux/types.h> #include <crypto/sha.h> +#include <crypto/sha256_base.h> #include <asm/byteorder.h> #include <asm/unaligned.h> @@ -214,138 +215,43 @@ static void sha256_transform(u32 *state, const u8 *input) memzero_explicit(W, 64 * sizeof(u32)); } -static int sha224_init(struct shash_desc *desc) +static void sha256_generic_block_fn(struct sha256_state *sst, u8 const *src, + int blocks) { - struct sha256_state *sctx = shash_desc_ctx(desc); - sctx->state[0] = SHA224_H0; - sctx->state[1] = SHA224_H1; - sctx->state[2] = SHA224_H2; - sctx->state[3] = SHA224_H3; - sctx->state[4] = SHA224_H4; - sctx->state[5] = SHA224_H5; - sctx->state[6] = SHA224_H6; - sctx->state[7] = SHA224_H7; - sctx->count = 0; - - return 0; -} - -static int sha256_init(struct shash_desc *desc) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - sctx->state[0] = SHA256_H0; - sctx->state[1] = SHA256_H1; - sctx->state[2] = SHA256_H2; - sctx->state[3] = SHA256_H3; - sctx->state[4] = SHA256_H4; - sctx->state[5] = SHA256_H5; - sctx->state[6] = SHA256_H6; - sctx->state[7] = SHA256_H7; - sctx->count = 0; - - return 0; + while (blocks--) { + sha256_transform(sst->state, src); + src += SHA256_BLOCK_SIZE; + } } int crypto_sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - struct sha256_state *sctx = shash_desc_ctx(desc); - unsigned int partial, done; - const u8 *src; - - partial = sctx->count & 0x3f; - sctx->count += len; - done = 0; - src = data; - - if ((partial + len) > 63) { - if (partial) { - done = -partial; - memcpy(sctx->buf + partial, data, done + 64); - src = sctx->buf; - } - - do { - sha256_transform(sctx->state, src); - done += 64; - src = data + done; - } while (done + 63 < len); - - partial = 0; - } - memcpy(sctx->buf + partial, src, len - done); - - return 0; + return sha256_base_do_update(desc, data, len, sha256_generic_block_fn); } EXPORT_SYMBOL(crypto_sha256_update); static int sha256_final(struct shash_desc *desc, u8 *out) { - struct sha256_state *sctx = shash_desc_ctx(desc); - __be32 *dst = (__be32 *)out; - __be64 bits; - unsigned int index, pad_len; - int i; - static const u8 padding[64] = { 0x80, }; - - /* Save number of bits */ - bits = cpu_to_be64(sctx->count << 3); - - /* Pad out to 56 mod 64. */ - index = sctx->count & 0x3f; - pad_len = (index < 56) ? (56 - index) : ((64+56) - index); - crypto_sha256_update(desc, padding, pad_len); - - /* Append length (before padding) */ - crypto_sha256_update(desc, (const u8 *)&bits, sizeof(bits)); - - /* Store state in digest */ - for (i = 0; i < 8; i++) - dst[i] = cpu_to_be32(sctx->state[i]); - - /* Zeroize sensitive information. */ - memset(sctx, 0, sizeof(*sctx)); - - return 0; + sha256_base_do_finalize(desc, sha256_generic_block_fn); + return sha256_base_finish(desc, out); } -static int sha224_final(struct shash_desc *desc, u8 *hash) +int crypto_sha256_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash) { - u8 D[SHA256_DIGEST_SIZE]; - - sha256_final(desc, D); - - memcpy(hash, D, SHA224_DIGEST_SIZE); - memzero_explicit(D, SHA256_DIGEST_SIZE); - - return 0; -} - -static int sha256_export(struct shash_desc *desc, void *out) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - memcpy(out, sctx, sizeof(*sctx)); - return 0; -} - -static int sha256_import(struct shash_desc *desc, const void *in) -{ - struct sha256_state *sctx = shash_desc_ctx(desc); - - memcpy(sctx, in, sizeof(*sctx)); - return 0; + sha256_base_do_update(desc, data, len, sha256_generic_block_fn); + return sha256_final(desc, hash); } +EXPORT_SYMBOL(crypto_sha256_finup); static struct shash_alg sha256_algs[2] = { { .digestsize = SHA256_DIGEST_SIZE, - .init = sha256_init, + .init = sha256_base_init, .update = crypto_sha256_update, .final = sha256_final, - .export = sha256_export, - .import = sha256_import, + .finup = crypto_sha256_finup, .descsize = sizeof(struct sha256_state), - .statesize = sizeof(struct sha256_state), .base = { .cra_name = "sha256", .cra_driver_name= "sha256-generic", @@ -355,9 +261,10 @@ static struct shash_alg sha256_algs[2] = { { } }, { .digestsize = SHA224_DIGEST_SIZE, - .init = sha224_init, + .init = sha224_base_init, .update = crypto_sha256_update, - .final = sha224_final, + .final = sha256_final, + .finup = crypto_sha256_finup, .descsize = sizeof(struct sha256_state), .base = { .cra_name = "sha224", diff --git a/crypto/sha512_generic.c b/crypto/sha512_generic.c index 1c3c3767e079..eba965d18bfc 100644 --- a/crypto/sha512_generic.c +++ b/crypto/sha512_generic.c @@ -18,6 +18,7 @@ #include <linux/crypto.h> #include <linux/types.h> #include <crypto/sha.h> +#include <crypto/sha512_base.h> #include <linux/percpu.h> #include <asm/byteorder.h> #include <asm/unaligned.h> @@ -130,125 +131,42 @@ sha512_transform(u64 *state, const u8 *input) a = b = c = d = e = f = g = h = t1 = t2 = 0; } -static int -sha512_init(struct shash_desc *desc) +static void sha512_generic_block_fn(struct sha512_state *sst, u8 const *src, + int blocks) { - struct sha512_state *sctx = shash_desc_ctx(desc); - sctx->state[0] = SHA512_H0; - sctx->state[1] = SHA512_H1; - sctx->state[2] = SHA512_H2; - sctx->state[3] = SHA512_H3; - sctx->state[4] = SHA512_H4; - sctx->state[5] = SHA512_H5; - sctx->state[6] = SHA512_H6; - sctx->state[7] = SHA512_H7; - sctx->count[0] = sctx->count[1] = 0; - - return 0; -} - -static int -sha384_init(struct shash_desc *desc) -{ - struct sha512_state *sctx = shash_desc_ctx(desc); - sctx->state[0] = SHA384_H0; - sctx->state[1] = SHA384_H1; - sctx->state[2] = SHA384_H2; - sctx->state[3] = SHA384_H3; - sctx->state[4] = SHA384_H4; - sctx->state[5] = SHA384_H5; - sctx->state[6] = SHA384_H6; - sctx->state[7] = SHA384_H7; - sctx->count[0] = sctx->count[1] = 0; - - return 0; + while (blocks--) { + sha512_transform(sst->state, src); + src += SHA512_BLOCK_SIZE; + } } int crypto_sha512_update(struct shash_desc *desc, const u8 *data, unsigned int len) { - struct sha512_state *sctx = shash_desc_ctx(desc); - - unsigned int i, index, part_len; - - /* Compute number of bytes mod 128 */ - index = sctx->count[0] & 0x7f; - - /* Update number of bytes */ - if ((sctx->count[0] += len) < len) - sctx->count[1]++; - - part_len = 128 - index; - - /* Transform as many times as possible. */ - if (len >= part_len) { - memcpy(&sctx->buf[index], data, part_len); - sha512_transform(sctx->state, sctx->buf); - - for (i = part_len; i + 127 < len; i+=128) - sha512_transform(sctx->state, &data[i]); - - index = 0; - } else { - i = 0; - } - - /* Buffer remaining input */ - memcpy(&sctx->buf[index], &data[i], len - i); - - return 0; + return sha512_base_do_update(desc, data, len, sha512_generic_block_fn); } EXPORT_SYMBOL(crypto_sha512_update); -static int -sha512_final(struct shash_desc *desc, u8 *hash) +static int sha512_final(struct shash_desc *desc, u8 *hash) { - struct sha512_state *sctx = shash_desc_ctx(desc); - static u8 padding[128] = { 0x80, }; - __be64 *dst = (__be64 *)hash; - __be64 bits[2]; - unsigned int index, pad_len; - int i; - - /* Save number of bits */ - bits[1] = cpu_to_be64(sctx->count[0] << 3); - bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61); - - /* Pad out to 112 mod 128. */ - index = sctx->count[0] & 0x7f; - pad_len = (index < 112) ? (112 - index) : ((128+112) - index); - crypto_sha512_update(desc, padding, pad_len); - - /* Append length (before padding) */ - crypto_sha512_update(desc, (const u8 *)bits, sizeof(bits)); - - /* Store state in digest */ - for (i = 0; i < 8; i++) - dst[i] = cpu_to_be64(sctx->state[i]); - - /* Zeroize sensitive information. */ - memset(sctx, 0, sizeof(struct sha512_state)); - - return 0; + sha512_base_do_finalize(desc, sha512_generic_block_fn); + return sha512_base_finish(desc, hash); } -static int sha384_final(struct shash_desc *desc, u8 *hash) +int crypto_sha512_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash) { - u8 D[64]; - - sha512_final(desc, D); - - memcpy(hash, D, 48); - memzero_explicit(D, 64); - - return 0; + sha512_base_do_update(desc, data, len, sha512_generic_block_fn); + return sha512_final(desc, hash); } +EXPORT_SYMBOL(crypto_sha512_finup); static struct shash_alg sha512_algs[2] = { { .digestsize = SHA512_DIGEST_SIZE, - .init = sha512_init, + .init = sha512_base_init, .update = crypto_sha512_update, .final = sha512_final, + .finup = crypto_sha512_finup, .descsize = sizeof(struct sha512_state), .base = { .cra_name = "sha512", @@ -259,9 +177,10 @@ static struct shash_alg sha512_algs[2] = { { } }, { .digestsize = SHA384_DIGEST_SIZE, - .init = sha384_init, + .init = sha384_base_init, .update = crypto_sha512_update, - .final = sha384_final, + .final = sha512_final, + .finup = crypto_sha512_finup, .descsize = sizeof(struct sha512_state), .base = { .cra_name = "sha384", diff --git a/crypto/tcrypt.c b/crypto/tcrypt.c index 4b9e23fa4204..1a2800107fc8 100644 --- a/crypto/tcrypt.c +++ b/crypto/tcrypt.c @@ -1155,9 +1155,9 @@ static void test_acipher_speed(const char *algo, int enc, unsigned int secs, goto out_free_req; } - sg_init_table(sg, TVMEMSIZE); - k = *keysize + *b_size; + sg_init_table(sg, DIV_ROUND_UP(k, PAGE_SIZE)); + if (k > PAGE_SIZE) { sg_set_buf(sg, tvmem[0] + *keysize, PAGE_SIZE - *keysize); diff --git a/crypto/testmgr.c b/crypto/testmgr.c index f4ed6d4205e7..f9bce3d7ee7f 100644 --- a/crypto/testmgr.c +++ b/crypto/testmgr.c @@ -1474,11 +1474,11 @@ static int test_cprng(struct crypto_rng *tfm, struct cprng_testvec *template, for (j = 0; j < template[i].loops; j++) { err = crypto_rng_get_bytes(tfm, result, template[i].rlen); - if (err != template[i].rlen) { + if (err < 0) { printk(KERN_ERR "alg: cprng: Failed to obtain " "the correct amount of random data for " - "%s (requested %d, got %d)\n", algo, - template[i].rlen, err); + "%s (requested %d)\n", algo, + template[i].rlen); goto out; } } @@ -1505,7 +1505,7 @@ static int alg_test_aead(const struct alg_test_desc *desc, const char *driver, struct crypto_aead *tfm; int err = 0; - tfm = crypto_alloc_aead(driver, type, mask); + tfm = crypto_alloc_aead(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(tfm)) { printk(KERN_ERR "alg: aead: Failed to load transform for %s: " "%ld\n", driver, PTR_ERR(tfm)); @@ -1534,7 +1534,7 @@ static int alg_test_cipher(const struct alg_test_desc *desc, struct crypto_cipher *tfm; int err = 0; - tfm = crypto_alloc_cipher(driver, type, mask); + tfm = crypto_alloc_cipher(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(tfm)) { printk(KERN_ERR "alg: cipher: Failed to load transform for " "%s: %ld\n", driver, PTR_ERR(tfm)); @@ -1563,7 +1563,7 @@ static int alg_test_skcipher(const struct alg_test_desc *desc, struct crypto_ablkcipher *tfm; int err = 0; - tfm = crypto_alloc_ablkcipher(driver, type, mask); + tfm = crypto_alloc_ablkcipher(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(tfm)) { printk(KERN_ERR "alg: skcipher: Failed to load transform for " "%s: %ld\n", driver, PTR_ERR(tfm)); @@ -1636,7 +1636,7 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver, struct crypto_ahash *tfm; int err; - tfm = crypto_alloc_ahash(driver, type, mask); + tfm = crypto_alloc_ahash(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(tfm)) { printk(KERN_ERR "alg: hash: Failed to load transform for %s: " "%ld\n", driver, PTR_ERR(tfm)); @@ -1664,7 +1664,7 @@ static int alg_test_crc32c(const struct alg_test_desc *desc, if (err) goto out; - tfm = crypto_alloc_shash(driver, type, mask); + tfm = crypto_alloc_shash(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(tfm)) { printk(KERN_ERR "alg: crc32c: Failed to load transform for %s: " "%ld\n", driver, PTR_ERR(tfm)); @@ -1706,7 +1706,7 @@ static int alg_test_cprng(const struct alg_test_desc *desc, const char *driver, struct crypto_rng *rng; int err; - rng = crypto_alloc_rng(driver, type, mask); + rng = crypto_alloc_rng(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(rng)) { printk(KERN_ERR "alg: cprng: Failed to load transform for %s: " "%ld\n", driver, PTR_ERR(rng)); @@ -1733,7 +1733,7 @@ static int drbg_cavs_test(struct drbg_testvec *test, int pr, if (!buf) return -ENOMEM; - drng = crypto_alloc_rng(driver, type, mask); + drng = crypto_alloc_rng(driver, type | CRYPTO_ALG_INTERNAL, mask); if (IS_ERR(drng)) { printk(KERN_ERR "alg: drbg: could not allocate DRNG handle for " "%s\n", driver); @@ -1759,7 +1759,7 @@ static int drbg_cavs_test(struct drbg_testvec *test, int pr, ret = crypto_drbg_get_bytes_addtl(drng, buf, test->expectedlen, &addtl); } - if (ret <= 0) { + if (ret < 0) { printk(KERN_ERR "alg: drbg: could not obtain random data for " "driver %s\n", driver); goto outbuf; @@ -1774,7 +1774,7 @@ static int drbg_cavs_test(struct drbg_testvec *test, int pr, ret = crypto_drbg_get_bytes_addtl(drng, buf, test->expectedlen, &addtl); } - if (ret <= 0) { + if (ret < 0) { printk(KERN_ERR "alg: drbg: could not obtain random data for " "driver %s\n", driver); goto outbuf; diff --git a/drivers/block/loop.c b/drivers/block/loop.c index d1f168b73634..c4fd1e45ce1e 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -75,6 +75,7 @@ #include <linux/sysfs.h> #include <linux/miscdevice.h> #include <linux/falloc.h> +#include <linux/uio.h> #include "loop.h" #include <asm/uaccess.h> @@ -229,13 +230,14 @@ lo_do_transfer(struct loop_device *lo, int cmd, static int __do_lo_send_write(struct file *file, u8 *buf, const int len, loff_t pos) { + struct kvec kvec = {.iov_base = buf, .iov_len = len}; + struct iov_iter from; ssize_t bw; - mm_segment_t old_fs = get_fs(); + + iov_iter_kvec(&from, ITER_KVEC | WRITE, &kvec, 1, len); file_start_write(file); - set_fs(get_ds()); - bw = file->f_op->write(file, buf, len, &pos); - set_fs(old_fs); + bw = vfs_iter_write(file, &from, &pos); file_end_write(file); if (likely(bw == len)) return 0; @@ -767,7 +769,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, goto out_putf; if (!(file->f_mode & FMODE_WRITE) || !(mode & FMODE_WRITE) || - !file->f_op->write) + !file->f_op->write_iter) lo_flags |= LO_FLAGS_READ_ONLY; lo_blocksize = S_ISBLK(inode->i_mode) ? diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index de57b38809c7..f48cf11c655e 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -101,6 +101,19 @@ config HW_RANDOM_BCM2835 If unsure, say Y. +config HW_RANDOM_IPROC_RNG200 + tristate "Broadcom iProc RNG200 support" + depends on ARCH_BCM_IPROC + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the RNG200 + hardware found on the Broadcom iProc SoCs. + + To compile this driver as a module, choose M here: the + module will be called iproc-rng200 + + If unsure, say Y. + config HW_RANDOM_GEODE tristate "AMD Geode HW Random Number Generator support" depends on X86_32 && PCI diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 0b4cd57f4e24..055bb01510ad 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -28,5 +28,6 @@ obj-$(CONFIG_HW_RANDOM_POWERNV) += powernv-rng.o obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o obj-$(CONFIG_HW_RANDOM_BCM2835) += bcm2835-rng.o +obj-$(CONFIG_HW_RANDOM_IPROC_RNG200) += iproc-rng200.o obj-$(CONFIG_HW_RANDOM_MSM) += msm-rng.o obj-$(CONFIG_HW_RANDOM_XGENE) += xgene-rng.o diff --git a/drivers/char/hw_random/bcm63xx-rng.c b/drivers/char/hw_random/bcm63xx-rng.c index ba6a65ac023b..d1494ecd9e11 100644 --- a/drivers/char/hw_random/bcm63xx-rng.c +++ b/drivers/char/hw_random/bcm63xx-rng.c @@ -13,24 +13,37 @@ #include <linux/platform_device.h> #include <linux/hw_random.h> -#include <bcm63xx_io.h> -#include <bcm63xx_regs.h> +#define RNG_CTRL 0x00 +#define RNG_EN (1 << 0) + +#define RNG_STAT 0x04 +#define RNG_AVAIL_MASK (0xff000000) + +#define RNG_DATA 0x08 +#define RNG_THRES 0x0c +#define RNG_MASK 0x10 struct bcm63xx_rng_priv { + struct hwrng rng; struct clk *clk; void __iomem *regs; }; -#define to_rng_priv(rng) ((struct bcm63xx_rng_priv *)rng->priv) +#define to_rng_priv(rng) container_of(rng, struct bcm63xx_rng_priv, rng) static int bcm63xx_rng_init(struct hwrng *rng) { struct bcm63xx_rng_priv *priv = to_rng_priv(rng); u32 val; + int error; + + error = clk_prepare_enable(priv->clk); + if (error) + return error; - val = bcm_readl(priv->regs + RNG_CTRL); + val = __raw_readl(priv->regs + RNG_CTRL); val |= RNG_EN; - bcm_writel(val, priv->regs + RNG_CTRL); + __raw_writel(val, priv->regs + RNG_CTRL); return 0; } @@ -40,23 +53,25 @@ static void bcm63xx_rng_cleanup(struct hwrng *rng) struct bcm63xx_rng_priv *priv = to_rng_priv(rng); u32 val; - val = bcm_readl(priv->regs + RNG_CTRL); + val = __raw_readl(priv->regs + RNG_CTRL); val &= ~RNG_EN; - bcm_writel(val, priv->regs + RNG_CTRL); + __raw_writel(val, priv->regs + RNG_CTRL); + + clk_didsable_unprepare(prov->clk); } static int bcm63xx_rng_data_present(struct hwrng *rng, int wait) { struct bcm63xx_rng_priv *priv = to_rng_priv(rng); - return bcm_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; + return __raw_readl(priv->regs + RNG_STAT) & RNG_AVAIL_MASK; } static int bcm63xx_rng_data_read(struct hwrng *rng, u32 *data) { struct bcm63xx_rng_priv *priv = to_rng_priv(rng); - *data = bcm_readl(priv->regs + RNG_DATA); + *data = __raw_readl(priv->regs + RNG_DATA); return 4; } @@ -72,94 +87,53 @@ static int bcm63xx_rng_probe(struct platform_device *pdev) r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) { dev_err(&pdev->dev, "no iomem resource\n"); - ret = -ENXIO; - goto out; + return -ENXIO; } - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "no memory for private structure\n"); - ret = -ENOMEM; - goto out; + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->rng.name = pdev->name; + priv->rng.init = bcm63xx_rng_init; + priv->rng.cleanup = bcm63xx_rng_cleanup; + prov->rng.data_present = bcm63xx_rng_data_present; + priv->rng.data_read = bcm63xx_rng_data_read; + + priv->clk = devm_clk_get(&pdev->dev, "ipsec"); + if (IS_ERR(priv->clk)) { + error = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "no clock for device: %d\n", error); + return error; } - rng = kzalloc(sizeof(*rng), GFP_KERNEL); - if (!rng) { - dev_err(&pdev->dev, "no memory for rng structure\n"); - ret = -ENOMEM; - goto out_free_priv; - } - - platform_set_drvdata(pdev, rng); - rng->priv = (unsigned long)priv; - rng->name = pdev->name; - rng->init = bcm63xx_rng_init; - rng->cleanup = bcm63xx_rng_cleanup; - rng->data_present = bcm63xx_rng_data_present; - rng->data_read = bcm63xx_rng_data_read; - - clk = clk_get(&pdev->dev, "ipsec"); - if (IS_ERR(clk)) { - dev_err(&pdev->dev, "no clock for device\n"); - ret = PTR_ERR(clk); - goto out_free_rng; - } - - priv->clk = clk; - if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r), pdev->name)) { dev_err(&pdev->dev, "request mem failed"); - ret = -ENOMEM; - goto out_free_rng; + return -EBUSY; } priv->regs = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); if (!priv->regs) { dev_err(&pdev->dev, "ioremap failed"); - ret = -ENOMEM; - goto out_free_rng; + return -ENOMEM; } - clk_enable(clk); - - ret = hwrng_register(rng); - if (ret) { - dev_err(&pdev->dev, "failed to register rng device\n"); - goto out_clk_disable; + error = devm_hwrng_register(&pdev->dev, &priv->rng); + if (error) { + dev_err(&pdev->dev, "failed to register rng device: %d\n", + error); + return error; } dev_info(&pdev->dev, "registered RNG driver\n"); return 0; - -out_clk_disable: - clk_disable(clk); -out_free_rng: - kfree(rng); -out_free_priv: - kfree(priv); -out: - return ret; -} - -static int bcm63xx_rng_remove(struct platform_device *pdev) -{ - struct hwrng *rng = platform_get_drvdata(pdev); - struct bcm63xx_rng_priv *priv = to_rng_priv(rng); - - hwrng_unregister(rng); - clk_disable(priv->clk); - kfree(priv); - kfree(rng); - - return 0; } static struct platform_driver bcm63xx_rng_driver = { .probe = bcm63xx_rng_probe, - .remove = bcm63xx_rng_remove, .driver = { .name = "bcm63xx-rng", }, diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 32a8a867f7f8..571ef61f8ea9 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -179,7 +179,8 @@ skip_init: add_early_randomness(rng); current_quality = rng->quality ? : default_quality; - current_quality &= 1023; + if (current_quality > 1024) + current_quality = 1024; if (current_quality == 0 && hwrng_fill) kthread_stop(hwrng_fill); @@ -536,6 +537,48 @@ void hwrng_unregister(struct hwrng *rng) } EXPORT_SYMBOL_GPL(hwrng_unregister); +static void devm_hwrng_release(struct device *dev, void *res) +{ + hwrng_unregister(*(struct hwrng **)res); +} + +static int devm_hwrng_match(struct device *dev, void *res, void *data) +{ + struct hwrng **r = res; + + if (WARN_ON(!r || !*r)) + return 0; + + return *r == data; +} + +int devm_hwrng_register(struct device *dev, struct hwrng *rng) +{ + struct hwrng **ptr; + int error; + + ptr = devres_alloc(devm_hwrng_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + error = hwrng_register(rng); + if (error) { + devres_free(ptr); + return error; + } + + *ptr = rng; + devres_add(dev, ptr); + return 0; +} +EXPORT_SYMBOL_GPL(devm_hwrng_register); + +void devm_hwrng_unregister(struct device *dev, struct hwrng *rng) +{ + devres_release(dev, devm_hwrng_release, devm_hwrng_match, rng); +} +EXPORT_SYMBOL_GPL(devm_hwrng_unregister); + static int __init hwrng_modinit(void) { return register_miscdev(); diff --git a/drivers/char/hw_random/exynos-rng.c b/drivers/char/hw_random/exynos-rng.c index fed0830bf724..dc4701fd814f 100644 --- a/drivers/char/hw_random/exynos-rng.c +++ b/drivers/char/hw_random/exynos-rng.c @@ -131,16 +131,7 @@ static int exynos_rng_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); - return hwrng_register(&exynos_rng->rng); -} - -static int exynos_rng_remove(struct platform_device *pdev) -{ - struct exynos_rng *exynos_rng = platform_get_drvdata(pdev); - - hwrng_unregister(&exynos_rng->rng); - - return 0; + return devm_hwrng_register(&pdev->dev, &exynos_rng->rng); } #ifdef CONFIG_PM @@ -172,7 +163,6 @@ static struct platform_driver exynos_rng_driver = { .pm = &exynos_rng_pm_ops, }, .probe = exynos_rng_probe, - .remove = exynos_rng_remove, }; module_platform_driver(exynos_rng_driver); diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c new file mode 100644 index 000000000000..3eaf7cb96d36 --- /dev/null +++ b/drivers/char/hw_random/iproc-rng200.c @@ -0,0 +1,239 @@ +/* +* Copyright (C) 2015 Broadcom 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. +* +* This program is distributed "as is" WITHOUT ANY WARRANTY of any +* kind, whether express or implied; without even the implied warranty +* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ +/* + * DESCRIPTION: The Broadcom iProc RNG200 Driver + */ + +#include <linux/hw_random.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/delay.h> + +/* Registers */ +#define RNG_CTRL_OFFSET 0x00 +#define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF +#define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001 +#define RNG_CTRL_RNG_RBGEN_DISABLE 0x00000000 + +#define RNG_SOFT_RESET_OFFSET 0x04 +#define RNG_SOFT_RESET 0x00000001 + +#define RBG_SOFT_RESET_OFFSET 0x08 +#define RBG_SOFT_RESET 0x00000001 + +#define RNG_INT_STATUS_OFFSET 0x18 +#define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000 +#define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000 +#define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020 +#define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001 + +#define RNG_FIFO_DATA_OFFSET 0x20 + +#define RNG_FIFO_COUNT_OFFSET 0x24 +#define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF + +struct iproc_rng200_dev { + struct hwrng rng; + void __iomem *base; +}; + +#define to_rng_priv(rng) container_of(rng, struct iproc_rng200_dev, rng) + +static void iproc_rng200_restart(void __iomem *rng_base) +{ + uint32_t val; + + /* Disable RBG */ + val = ioread32(rng_base + RNG_CTRL_OFFSET); + val &= ~RNG_CTRL_RNG_RBGEN_MASK; + val |= RNG_CTRL_RNG_RBGEN_DISABLE; + iowrite32(val, rng_base + RNG_CTRL_OFFSET); + + /* Clear all interrupt status */ + iowrite32(0xFFFFFFFFUL, rng_base + RNG_INT_STATUS_OFFSET); + + /* Reset RNG and RBG */ + val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); + val |= RBG_SOFT_RESET; + iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); + + val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); + val |= RNG_SOFT_RESET; + iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); + + val = ioread32(rng_base + RNG_SOFT_RESET_OFFSET); + val &= ~RNG_SOFT_RESET; + iowrite32(val, rng_base + RNG_SOFT_RESET_OFFSET); + + val = ioread32(rng_base + RBG_SOFT_RESET_OFFSET); + val &= ~RBG_SOFT_RESET; + iowrite32(val, rng_base + RBG_SOFT_RESET_OFFSET); + + /* Enable RBG */ + val = ioread32(rng_base + RNG_CTRL_OFFSET); + val &= ~RNG_CTRL_RNG_RBGEN_MASK; + val |= RNG_CTRL_RNG_RBGEN_ENABLE; + iowrite32(val, rng_base + RNG_CTRL_OFFSET); +} + +static int iproc_rng200_read(struct hwrng *rng, void *buf, size_t max, + bool wait) +{ + struct iproc_rng200_dev *priv = to_rng_priv(rng); + uint32_t num_remaining = max; + uint32_t status; + + #define MAX_RESETS_PER_READ 1 + uint32_t num_resets = 0; + + #define MAX_IDLE_TIME (1 * HZ) + unsigned long idle_endtime = jiffies + MAX_IDLE_TIME; + + while ((num_remaining > 0) && time_before(jiffies, idle_endtime)) { + + /* Is RNG sane? If not, reset it. */ + status = ioread32(priv->base + RNG_INT_STATUS_OFFSET); + if ((status & (RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK | + RNG_INT_STATUS_NIST_FAIL_IRQ_MASK)) != 0) { + + if (num_resets >= MAX_RESETS_PER_READ) + return max - num_remaining; + + iproc_rng200_restart(priv->base); + num_resets++; + } + + /* Are there any random numbers available? */ + if ((ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) & + RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK) > 0) { + + if (num_remaining >= sizeof(uint32_t)) { + /* Buffer has room to store entire word */ + *(uint32_t *)buf = ioread32(priv->base + + RNG_FIFO_DATA_OFFSET); + buf += sizeof(uint32_t); + num_remaining -= sizeof(uint32_t); + } else { + /* Buffer can only store partial word */ + uint32_t rnd_number = ioread32(priv->base + + RNG_FIFO_DATA_OFFSET); + memcpy(buf, &rnd_number, num_remaining); + buf += num_remaining; + num_remaining = 0; + } + + /* Reset the IDLE timeout */ + idle_endtime = jiffies + MAX_IDLE_TIME; + } else { + if (!wait) + /* Cannot wait, return immediately */ + return max - num_remaining; + + /* Can wait, give others chance to run */ + usleep_range(min(num_remaining * 10, 500U), 500); + } + } + + return max - num_remaining; +} + +static int iproc_rng200_init(struct hwrng *rng) +{ + struct iproc_rng200_dev *priv = to_rng_priv(rng); + uint32_t val; + + /* Setup RNG. */ + val = ioread32(priv->base + RNG_CTRL_OFFSET); + val &= ~RNG_CTRL_RNG_RBGEN_MASK; + val |= RNG_CTRL_RNG_RBGEN_ENABLE; + iowrite32(val, priv->base + RNG_CTRL_OFFSET); + + return 0; +} + +static void iproc_rng200_cleanup(struct hwrng *rng) +{ + struct iproc_rng200_dev *priv = to_rng_priv(rng); + uint32_t val; + + /* Disable RNG hardware */ + val = ioread32(priv->base + RNG_CTRL_OFFSET); + val &= ~RNG_CTRL_RNG_RBGEN_MASK; + val |= RNG_CTRL_RNG_RBGEN_DISABLE; + iowrite32(val, priv->base + RNG_CTRL_OFFSET); +} + +static int iproc_rng200_probe(struct platform_device *pdev) +{ + struct iproc_rng200_dev *priv; + struct resource *res; + struct device *dev = &pdev->dev; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Map peripheral */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to get rng resources\n"); + return -EINVAL; + } + + priv->base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->base)) { + dev_err(dev, "failed to remap rng regs\n"); + return PTR_ERR(priv->base); + } + + priv->rng.name = "iproc-rng200", + priv->rng.read = iproc_rng200_read, + priv->rng.init = iproc_rng200_init, + priv->rng.cleanup = iproc_rng200_cleanup, + + /* Register driver */ + ret = devm_hwrng_register(dev, &priv->rng); + if (ret) { + dev_err(dev, "hwrng registration failed\n"); + return ret; + } + + dev_info(dev, "hwrng registered\n"); + + return 0; +} + +static const struct of_device_id iproc_rng200_of_match[] = { + { .compatible = "brcm,iproc-rng200", }, + {}, +}; +MODULE_DEVICE_TABLE(of, iproc_rng200_of_match); + +static struct platform_driver iproc_rng200_driver = { + .driver = { + .name = "iproc-rng200", + .of_match_table = iproc_rng200_of_match, + }, + .probe = iproc_rng200_probe, +}; +module_platform_driver(iproc_rng200_driver); + +MODULE_AUTHOR("Broadcom"); +MODULE_DESCRIPTION("iProc RNG200 Random Number Generator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/char/hw_random/msm-rng.c b/drivers/char/hw_random/msm-rng.c index cea1c703d62f..96fb986402eb 100644 --- a/drivers/char/hw_random/msm-rng.c +++ b/drivers/char/hw_random/msm-rng.c @@ -157,7 +157,7 @@ static int msm_rng_probe(struct platform_device *pdev) rng->hwrng.cleanup = msm_rng_cleanup, rng->hwrng.read = msm_rng_read, - ret = hwrng_register(&rng->hwrng); + ret = devm_hwrng_register(&pdev->dev, &rng->hwrng); if (ret) { dev_err(&pdev->dev, "failed to register hwrng\n"); return ret; @@ -166,14 +166,6 @@ static int msm_rng_probe(struct platform_device *pdev) return 0; } -static int msm_rng_remove(struct platform_device *pdev) -{ - struct msm_rng *rng = platform_get_drvdata(pdev); - - hwrng_unregister(&rng->hwrng); - return 0; -} - static const struct of_device_id msm_rng_of_match[] = { { .compatible = "qcom,prng", }, {} @@ -182,7 +174,6 @@ MODULE_DEVICE_TABLE(of, msm_rng_of_match); static struct platform_driver msm_rng_driver = { .probe = msm_rng_probe, - .remove = msm_rng_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = of_match_ptr(msm_rng_of_match), diff --git a/drivers/char/hw_random/octeon-rng.c b/drivers/char/hw_random/octeon-rng.c index be1c3f607398..6234a4a19b56 100644 --- a/drivers/char/hw_random/octeon-rng.c +++ b/drivers/char/hw_random/octeon-rng.c @@ -105,7 +105,7 @@ static int octeon_rng_probe(struct platform_device *pdev) return 0; } -static int __exit octeon_rng_remove(struct platform_device *pdev) +static int octeon_rng_remove(struct platform_device *pdev) { struct hwrng *rng = platform_get_drvdata(pdev); @@ -119,7 +119,7 @@ static struct platform_driver octeon_rng_driver = { .name = "octeon_rng", }, .probe = octeon_rng_probe, - .remove = __exit_p(octeon_rng_remove), + .remove = octeon_rng_remove, }; module_platform_driver(octeon_rng_driver); diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c index d14dcf788f17..8a1432e8bb80 100644 --- a/drivers/char/hw_random/omap-rng.c +++ b/drivers/char/hw_random/omap-rng.c @@ -236,7 +236,7 @@ static int omap4_rng_init(struct omap_rng_dev *priv) u32 val; /* Return if RNG is already running. */ - if (omap_rng_read(priv, RNG_CONFIG_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) + if (omap_rng_read(priv, RNG_CONTROL_REG) & RNG_CONTROL_ENABLE_TRNG_MASK) return 0; val = RNG_CONFIG_MIN_REFIL_CYCLES << RNG_CONFIG_MIN_REFIL_CYCLES_SHIFT; @@ -262,7 +262,7 @@ static void omap4_rng_cleanup(struct omap_rng_dev *priv) val = omap_rng_read(priv, RNG_CONTROL_REG); val &= ~RNG_CONTROL_ENABLE_TRNG_MASK; - omap_rng_write(priv, RNG_CONFIG_REG, val); + omap_rng_write(priv, RNG_CONTROL_REG, val); } static irqreturn_t omap4_rng_irq(int irq, void *dev_id) @@ -408,7 +408,7 @@ err_ioremap: return ret; } -static int __exit omap_rng_remove(struct platform_device *pdev) +static int omap_rng_remove(struct platform_device *pdev) { struct omap_rng_dev *priv = platform_get_drvdata(pdev); @@ -422,9 +422,7 @@ static int __exit omap_rng_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP - -static int omap_rng_suspend(struct device *dev) +static int __maybe_unused omap_rng_suspend(struct device *dev) { struct omap_rng_dev *priv = dev_get_drvdata(dev); @@ -434,7 +432,7 @@ static int omap_rng_suspend(struct device *dev) return 0; } -static int omap_rng_resume(struct device *dev) +static int __maybe_unused omap_rng_resume(struct device *dev) { struct omap_rng_dev *priv = dev_get_drvdata(dev); @@ -445,22 +443,15 @@ static int omap_rng_resume(struct device *dev) } static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume); -#define OMAP_RNG_PM (&omap_rng_pm) - -#else - -#define OMAP_RNG_PM NULL - -#endif static struct platform_driver omap_rng_driver = { .driver = { .name = "omap_rng", - .pm = OMAP_RNG_PM, + .pm = &omap_rng_pm, .of_match_table = of_match_ptr(omap_rng_of_match), }, .probe = omap_rng_probe, - .remove = __exit_p(omap_rng_remove), + .remove = omap_rng_remove, }; module_platform_driver(omap_rng_driver); diff --git a/drivers/char/hw_random/pseries-rng.c b/drivers/char/hw_random/pseries-rng.c index bcf86f91800a..63ce51d09af1 100644 --- a/drivers/char/hw_random/pseries-rng.c +++ b/drivers/char/hw_random/pseries-rng.c @@ -61,13 +61,13 @@ static struct hwrng pseries_rng = { .read = pseries_rng_read, }; -static int __init pseries_rng_probe(struct vio_dev *dev, +static int pseries_rng_probe(struct vio_dev *dev, const struct vio_device_id *id) { return hwrng_register(&pseries_rng); } -static int __exit pseries_rng_remove(struct vio_dev *dev) +static int pseries_rng_remove(struct vio_dev *dev) { hwrng_unregister(&pseries_rng); return 0; diff --git a/drivers/char/hw_random/xgene-rng.c b/drivers/char/hw_random/xgene-rng.c index 23caa05380a8..c37cf754a985 100644 --- a/drivers/char/hw_random/xgene-rng.c +++ b/drivers/char/hw_random/xgene-rng.c @@ -21,6 +21,7 @@ * */ +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/hw_random.h> @@ -310,6 +311,14 @@ static int xgene_rng_init(struct hwrng *rng) return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id xgene_rng_acpi_match[] = { + { "APMC0D18", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, xgene_rng_acpi_match); +#endif + static struct hwrng xgene_rng_func = { .name = "xgene-rng", .init = xgene_rng_init, @@ -415,6 +424,7 @@ static struct platform_driver xgene_rng_driver = { .driver = { .name = "xgene-rng", .of_match_table = xgene_rng_of_match, + .acpi_match_table = ACPI_PTR(xgene_rng_acpi_match), }, }; diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 9c4fd7a8e2e5..6b1721f978c2 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -607,16 +607,16 @@ static ssize_t write_null(struct file *file, const char __user *buf, return count; } -static ssize_t aio_read_null(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t read_iter_null(struct kiocb *iocb, struct iov_iter *to) { return 0; } -static ssize_t aio_write_null(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t write_iter_null(struct kiocb *iocb, struct iov_iter *from) { - return iov_length(iov, nr_segs); + size_t count = iov_iter_count(from); + iov_iter_advance(from, count); + return count; } static int pipe_to_null(struct pipe_inode_info *info, struct pipe_buffer *buf, @@ -718,7 +718,7 @@ static int open_port(struct inode *inode, struct file *filp) #define zero_lseek null_lseek #define full_lseek null_lseek #define write_zero write_null -#define aio_write_zero aio_write_null +#define write_iter_zero write_iter_null #define open_mem open_port #define open_kmem open_mem @@ -750,8 +750,8 @@ static const struct file_operations null_fops = { .llseek = null_lseek, .read = read_null, .write = write_null, - .aio_read = aio_read_null, - .aio_write = aio_write_null, + .read_iter = read_iter_null, + .write_iter = write_iter_null, .splice_write = splice_write_null, }; @@ -764,10 +764,9 @@ static const struct file_operations __maybe_unused port_fops = { static const struct file_operations zero_fops = { .llseek = zero_lseek, - .read = new_sync_read, .write = write_zero, .read_iter = read_iter_zero, - .aio_write = aio_write_zero, + .write_iter = write_iter_zero, .mmap = mmap_zero, #ifndef CONFIG_MMU .mmap_capabilities = zero_mmap_capabilities, @@ -776,7 +775,6 @@ static const struct file_operations zero_fops = { static const struct file_operations full_fops = { .llseek = full_lseek, - .read = new_sync_read, .read_iter = read_iter_zero, .write = write_full, }; diff --git a/drivers/char/raw.c b/drivers/char/raw.c index 6e29bf2db536..5fc291c6157e 100644 --- a/drivers/char/raw.c +++ b/drivers/char/raw.c @@ -282,9 +282,7 @@ static long raw_ctl_compat_ioctl(struct file *file, unsigned int cmd, #endif static const struct file_operations raw_fops = { - .read = new_sync_read, .read_iter = blkdev_read_iter, - .write = new_sync_write, .write_iter = blkdev_write_iter, .fsync = blkdev_fsync, .open = raw_open, diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index 9d4e37549eb2..3b84a8b1bfbe 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -25,13 +25,14 @@ menuconfig TCG_TPM if TCG_TPM config TCG_TIS - tristate "TPM Interface Specification 1.2 Interface" + tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface" depends on X86 ---help--- If you have a TPM security chip that is compliant with the - TCG TIS 1.2 TPM specification say Yes and it will be accessible - from within Linux. To compile this driver as a module, choose - M here; the module will be called tpm_tis. + TCG TIS 1.2 TPM specification (TPM1.2) or the TCG PTP FIFO + specification (TPM2.0) say Yes and it will be accessible from + within Linux. To compile this driver as a module, choose M here; + the module will be called tpm_tis. config TCG_TIS_I2C_ATMEL tristate "TPM Interface Specification 1.2 Interface (I2C - Atmel)" @@ -100,16 +101,6 @@ config TCG_IBMVTPM will be accessible from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_ibmvtpm. -config TCG_TIS_I2C_ST33 - tristate "TPM Interface Specification 1.2 Interface (I2C - STMicroelectronics)" - depends on I2C - depends on GPIOLIB - ---help--- - If you have a TPM security chip from STMicroelectronics working with - an I2C bus say Yes and it will be accessible from within Linux. - To compile this driver as a module, choose M here; the module will be - called tpm_i2c_stm_st33. - config TCG_XEN tristate "XEN TPM Interface" depends on TCG_TPM && XEN @@ -131,4 +122,5 @@ config TCG_CRB from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_crb. +source "drivers/char/tpm/st33zp24/Kconfig" endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 990cf183931d..56e8f1f3dc7e 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -20,6 +20,6 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o -obj-$(CONFIG_TCG_TIS_I2C_ST33) += tpm_i2c_stm_st33.o +obj-$(CONFIG_TCG_TIS_ST33ZP24) += st33zp24/ obj-$(CONFIG_TCG_XEN) += xen-tpmfront.o obj-$(CONFIG_TCG_CRB) += tpm_crb.o diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig new file mode 100644 index 000000000000..09cb727864f0 --- /dev/null +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -0,0 +1,30 @@ +config TCG_TIS_ST33ZP24 + tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" + depends on GPIOLIB + ---help--- + STMicroelectronics ST33ZP24 core driver. It implements the core + TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will + register against it. + + To compile this driver as a module, choose m here. The module will be called + tpm_st33zp24. + +config TCG_TIS_ST33ZP24_I2C + tristate "TPM 1.2 ST33ZP24 I2C support" + depends on TCG_TIS_ST33ZP24 + depends on I2C + ---help--- + This module adds support for the STMicroelectronics TPM security chip + ST33ZP24 with i2c interface. + To compile this driver as a module, choose M here; the module will be + called tpm_st33zp24_i2c. + +config TCG_TIS_ST33ZP24_SPI + tristate "TPM 1.2 ST33ZP24 SPI support" + depends on TCG_TIS_ST33ZP24 + depends on SPI + ---help--- + This module adds support for the STMicroelectronics TPM security chip + ST33ZP24 with spi interface. + To compile this driver as a module, choose M here; the module will be + called tpm_st33zp24_spi. diff --git a/drivers/char/tpm/st33zp24/Makefile b/drivers/char/tpm/st33zp24/Makefile new file mode 100644 index 000000000000..74a722e5e068 --- /dev/null +++ b/drivers/char/tpm/st33zp24/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for ST33ZP24 TPM 1.2 driver +# + +tpm_st33zp24-objs = st33zp24.o +obj-$(CONFIG_TCG_TIS_ST33ZP24) += tpm_st33zp24.o + +tpm_st33zp24_i2c-objs = i2c.o +obj-$(CONFIG_TCG_TIS_ST33ZP24_I2C) += tpm_st33zp24_i2c.o + +tpm_st33zp24_spi-objs = spi.o +obj-$(CONFIG_TCG_TIS_ST33ZP24_SPI) += tpm_st33zp24_spi.o diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c new file mode 100644 index 000000000000..ad1ee180e0c2 --- /dev/null +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -0,0 +1,276 @@ +/* + * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/tpm.h> +#include <linux/platform_data/st33zp24.h> + +#include "st33zp24.h" + +#define TPM_DUMMY_BYTE 0xAA + +struct st33zp24_i2c_phy { + struct i2c_client *client; + u8 buf[TPM_BUFSIZE + 1]; + int io_lpcpd; +}; + +/* + * write8_reg + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: Returns negative errno, or else the number of bytes written. + */ +static int write8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +{ + struct st33zp24_i2c_phy *phy = phy_id; + + phy->buf[0] = tpm_register; + memcpy(phy->buf + 1, tpm_data, tpm_size); + return i2c_master_send(phy->client, phy->buf, tpm_size + 1); +} /* write8_reg() */ + +/* + * read8_reg + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +{ + struct st33zp24_i2c_phy *phy = phy_id; + u8 status = 0; + u8 data; + + data = TPM_DUMMY_BYTE; + status = write8_reg(phy, tpm_register, &data, 1); + if (status == 2) + status = i2c_master_recv(phy->client, tpm_data, tpm_size); + return status; +} /* read8_reg() */ + +/* + * st33zp24_i2c_send + * Send byte to the TIS register according to the ST33ZP24 I2C protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, the length of the data + * @return: number of byte written successfully: should be one if success. + */ +static int st33zp24_i2c_send(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + return write8_reg(phy_id, tpm_register | TPM_WRITE_DIRECTION, tpm_data, + tpm_size); +} + +/* + * st33zp24_i2c_recv + * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int st33zp24_i2c_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + return read8_reg(phy_id, tpm_register, tpm_data, tpm_size); +} + +static const struct st33zp24_phy_ops i2c_phy_ops = { + .send = st33zp24_i2c_send, + .recv = st33zp24_i2c_recv, +}; + +#ifdef CONFIG_OF +static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +{ + struct device_node *pp; + struct i2c_client *client = phy->client; + int gpio; + int ret; + + pp = client->dev.of_node; + if (!pp) { + dev_err(&client->dev, "No platform data\n"); + return -ENODEV; + } + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); + if (gpio < 0) { + dev_err(&client->dev, + "Failed to retrieve lpcpd-gpios from dts.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + /* GPIO request and configuration */ + ret = devm_gpio_request_one(&client->dev, gpio, + GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); + if (ret) { + dev_err(&client->dev, "Failed to request lpcpd pin\n"); + return -ENODEV; + } + phy->io_lpcpd = gpio; + + return 0; +} +#else +static int st33zp24_i2c_of_request_resources(struct st33zp24_i2c_phy *phy) +{ + return -ENODEV; +} +#endif + +static int st33zp24_i2c_request_resources(struct i2c_client *client, + struct st33zp24_i2c_phy *phy) +{ + struct st33zp24_platform_data *pdata; + int ret; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "No platform data\n"); + return -ENODEV; + } + + /* store for late use */ + phy->io_lpcpd = pdata->io_lpcpd; + + if (gpio_is_valid(pdata->io_lpcpd)) { + ret = devm_gpio_request_one(&client->dev, + pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, + "TPM IO_LPCPD"); + if (ret) { + dev_err(&client->dev, "Failed to request lpcpd pin\n"); + return ret; + } + } + + return 0; +} + +/* + * st33zp24_i2c_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +static int st33zp24_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct st33zp24_platform_data *pdata; + struct st33zp24_i2c_phy *phy; + + if (!client) { + pr_info("%s: i2c client is NULL. Device not accessible.\n", + __func__); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_info(&client->dev, "client not i2c capable\n"); + return -ENODEV; + } + + phy = devm_kzalloc(&client->dev, sizeof(struct st33zp24_i2c_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->client = client; + pdata = client->dev.platform_data; + if (!pdata && client->dev.of_node) { + ret = st33zp24_i2c_of_request_resources(phy); + if (ret) + return ret; + } else if (pdata) { + ret = st33zp24_i2c_request_resources(client, phy); + if (ret) + return ret; + } + + return st33zp24_probe(phy, &i2c_phy_ops, &client->dev, client->irq, + phy->io_lpcpd); +} + +/* + * st33zp24_i2c_remove remove the TPM device + * @param: client, the i2c_client description (TPM I2C description). + * @return: 0 in case of success. + */ +static int st33zp24_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = i2c_get_clientdata(client); + + return st33zp24_remove(chip); +} + +static const struct i2c_device_id st33zp24_i2c_id[] = { + {TPM_ST33_I2C, 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, st33zp24_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id of_st33zp24_i2c_match[] = { + { .compatible = "st,st33zp24-i2c", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); +#endif + +static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, + st33zp24_pm_resume); + +static struct i2c_driver st33zp24_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_ST33_I2C, + .pm = &st33zp24_i2c_ops, + .of_match_table = of_match_ptr(of_st33zp24_i2c_match), + }, + .probe = st33zp24_i2c_probe, + .remove = st33zp24_i2c_remove, + .id_table = st33zp24_i2c_id +}; + +module_i2c_driver(st33zp24_i2c_driver); + +MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); +MODULE_DESCRIPTION("STM TPM 1.2 I2C ST33 Driver"); +MODULE_VERSION("1.3.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/st33zp24/spi.c b/drivers/char/tpm/st33zp24/spi.c new file mode 100644 index 000000000000..f0184a1b0c1c --- /dev/null +++ b/drivers/char/tpm/st33zp24/spi.c @@ -0,0 +1,399 @@ +/* + * STMicroelectronics TPM SPI Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/gpio.h> +#include <linux/of_irq.h> +#include <linux/of_gpio.h> +#include <linux/tpm.h> +#include <linux/platform_data/st33zp24.h> + +#include "st33zp24.h" + +#define TPM_DATA_FIFO 0x24 +#define TPM_INTF_CAPABILITY 0x14 + +#define TPM_DUMMY_BYTE 0x00 + +#define MAX_SPI_LATENCY 15 +#define LOCALITY0 0 + +#define ST33ZP24_OK 0x5A +#define ST33ZP24_UNDEFINED_ERR 0x80 +#define ST33ZP24_BADLOCALITY 0x81 +#define ST33ZP24_TISREGISTER_UKNOWN 0x82 +#define ST33ZP24_LOCALITY_NOT_ACTIVATED 0x83 +#define ST33ZP24_HASH_END_BEFORE_HASH_START 0x84 +#define ST33ZP24_BAD_COMMAND_ORDER 0x85 +#define ST33ZP24_INCORECT_RECEIVED_LENGTH 0x86 +#define ST33ZP24_TPM_FIFO_OVERFLOW 0x89 +#define ST33ZP24_UNEXPECTED_READ_FIFO 0x8A +#define ST33ZP24_UNEXPECTED_WRITE_FIFO 0x8B +#define ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END 0x90 +#define ST33ZP24_DUMMY_BYTES 0x00 + +/* + * TPM command can be up to 2048 byte, A TPM response can be up to + * 1024 byte. + * Between command and response, there are latency byte (up to 15 + * usually on st33zp24 2 are enough). + * + * Overall when sending a command and expecting an answer we need if + * worst case: + * 2048 (for the TPM command) + 1024 (for the TPM answer). We need + * some latency byte before the answer is available (max 15). + * We have 2048 + 1024 + 15. + */ +#define ST33ZP24_SPI_BUFFER_SIZE (TPM_BUFSIZE + (TPM_BUFSIZE / 2) +\ + MAX_SPI_LATENCY) + + +struct st33zp24_spi_phy { + struct spi_device *spi_device; + struct spi_transfer spi_xfer; + u8 tx_buf[ST33ZP24_SPI_BUFFER_SIZE]; + u8 rx_buf[ST33ZP24_SPI_BUFFER_SIZE]; + + int io_lpcpd; + int latency; +}; + +static int st33zp24_status_to_errno(u8 code) +{ + switch (code) { + case ST33ZP24_OK: + return 0; + case ST33ZP24_UNDEFINED_ERR: + case ST33ZP24_BADLOCALITY: + case ST33ZP24_TISREGISTER_UKNOWN: + case ST33ZP24_LOCALITY_NOT_ACTIVATED: + case ST33ZP24_HASH_END_BEFORE_HASH_START: + case ST33ZP24_BAD_COMMAND_ORDER: + case ST33ZP24_UNEXPECTED_READ_FIFO: + case ST33ZP24_UNEXPECTED_WRITE_FIFO: + case ST33ZP24_CMDRDY_SET_WHEN_PROCESSING_HASH_END: + return -EPROTO; + case ST33ZP24_INCORECT_RECEIVED_LENGTH: + case ST33ZP24_TPM_FIFO_OVERFLOW: + return -EMSGSIZE; + case ST33ZP24_DUMMY_BYTES: + return -ENOSYS; + } + return code; +} + +/* + * st33zp24_spi_send + * Send byte to the TIS register according to the ST33ZP24 SPI protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be written + * @param: tpm_data, the tpm_data to write inside the tpm_register + * @param: tpm_size, The length of the data + * @return: should be zero if success else a negative error code. + */ +static int st33zp24_spi_send(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + u8 data = 0; + int total_length = 0, nbr_dummy_bytes = 0, ret = 0; + struct st33zp24_spi_phy *phy = phy_id; + struct spi_device *dev = phy->spi_device; + u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; + u8 *rx_buf = phy->spi_xfer.rx_buf; + + /* Pre-Header */ + data = TPM_WRITE_DIRECTION | LOCALITY0; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + data = tpm_register; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + + if (tpm_size > 0 && tpm_register == TPM_DATA_FIFO) { + tx_buf[total_length++] = tpm_size >> 8; + tx_buf[total_length++] = tpm_size; + } + + memcpy(&tx_buf[total_length], tpm_data, tpm_size); + total_length += tpm_size; + + nbr_dummy_bytes = phy->latency; + memset(&tx_buf[total_length], TPM_DUMMY_BYTE, nbr_dummy_bytes); + + phy->spi_xfer.len = total_length + nbr_dummy_bytes; + + ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + if (ret == 0) + ret = rx_buf[total_length + nbr_dummy_bytes - 1]; + + return st33zp24_status_to_errno(ret); +} /* st33zp24_spi_send() */ + +/* + * read8_recv + * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: should be zero if success else a negative error code. + */ +static int read8_reg(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size) +{ + u8 data = 0; + int total_length = 0, nbr_dummy_bytes, ret; + struct st33zp24_spi_phy *phy = phy_id; + struct spi_device *dev = phy->spi_device; + u8 *tx_buf = (u8 *)phy->spi_xfer.tx_buf; + u8 *rx_buf = phy->spi_xfer.rx_buf; + + /* Pre-Header */ + data = LOCALITY0; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + data = tpm_register; + memcpy(tx_buf + total_length, &data, sizeof(data)); + total_length++; + + nbr_dummy_bytes = phy->latency; + memset(&tx_buf[total_length], TPM_DUMMY_BYTE, + nbr_dummy_bytes + tpm_size); + + phy->spi_xfer.len = total_length + nbr_dummy_bytes + tpm_size; + + /* header + status byte + size of the data + status byte */ + ret = spi_sync_transfer(dev, &phy->spi_xfer, 1); + if (tpm_size > 0 && ret == 0) { + ret = rx_buf[total_length + nbr_dummy_bytes - 1]; + + memcpy(tpm_data, rx_buf + total_length + nbr_dummy_bytes, + tpm_size); + } + + return ret; +} /* read8_reg() */ + +/* + * st33zp24_spi_recv + * Recv byte from the TIS register according to the ST33ZP24 SPI protocol. + * @param: phy_id, the phy description + * @param: tpm_register, the tpm tis register where the data should be read + * @param: tpm_data, the TPM response + * @param: tpm_size, tpm TPM response size to read. + * @return: number of byte read successfully: should be one if success. + */ +static int st33zp24_spi_recv(void *phy_id, u8 tpm_register, u8 *tpm_data, + int tpm_size) +{ + int ret; + + ret = read8_reg(phy_id, tpm_register, tpm_data, tpm_size); + if (!st33zp24_status_to_errno(ret)) + return tpm_size; + return ret; +} /* st33zp24_spi_recv() */ + +static int evaluate_latency(void *phy_id) +{ + struct st33zp24_spi_phy *phy = phy_id; + int latency = 1, status = 0; + u8 data = 0; + + while (!status && latency < MAX_SPI_LATENCY) { + phy->latency = latency; + status = read8_reg(phy_id, TPM_INTF_CAPABILITY, &data, 1); + latency++; + } + return latency - 1; +} /* evaluate_latency() */ + +static const struct st33zp24_phy_ops spi_phy_ops = { + .send = st33zp24_spi_send, + .recv = st33zp24_spi_recv, +}; + +#ifdef CONFIG_OF +static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +{ + struct device_node *pp; + struct spi_device *dev = phy->spi_device; + int gpio; + int ret; + + pp = dev->dev.of_node; + if (!pp) { + dev_err(&dev->dev, "No platform data\n"); + return -ENODEV; + } + + /* Get GPIO from device tree */ + gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); + if (gpio < 0) { + dev_err(&dev->dev, + "Failed to retrieve lpcpd-gpios from dts.\n"); + phy->io_lpcpd = -1; + /* + * lpcpd pin is not specified. This is not an issue as + * power management can be also managed by TPM specific + * commands. So leave with a success status code. + */ + return 0; + } + /* GPIO request and configuration */ + ret = devm_gpio_request_one(&dev->dev, gpio, + GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); + if (ret) { + dev_err(&dev->dev, "Failed to request lpcpd pin\n"); + return -ENODEV; + } + phy->io_lpcpd = gpio; + + return 0; +} +#else +static int tpm_stm_spi_of_request_resources(struct st33zp24_spi_phy *phy) +{ + return -ENODEV; +} +#endif + +static int tpm_stm_spi_request_resources(struct spi_device *dev, + struct st33zp24_spi_phy *phy) +{ + struct st33zp24_platform_data *pdata; + int ret; + + pdata = dev->dev.platform_data; + if (!pdata) { + dev_err(&dev->dev, "No platform data\n"); + return -ENODEV; + } + + /* store for late use */ + phy->io_lpcpd = pdata->io_lpcpd; + + if (gpio_is_valid(pdata->io_lpcpd)) { + ret = devm_gpio_request_one(&dev->dev, + pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, + "TPM IO_LPCPD"); + if (ret) { + dev_err(&dev->dev, "%s : reset gpio_request failed\n", + __FILE__); + return ret; + } + } + + return 0; +} + +/* + * tpm_st33_spi_probe initialize the TPM device + * @param: dev, the spi_device drescription (TPM SPI description). + * @return: 0 in case of success. + * or a negative value describing the error. + */ +static int +tpm_st33_spi_probe(struct spi_device *dev) +{ + int ret; + struct st33zp24_platform_data *pdata; + struct st33zp24_spi_phy *phy; + + /* Check SPI platform functionnalities */ + if (!dev) { + pr_info("%s: dev is NULL. Device is not accessible.\n", + __func__); + return -ENODEV; + } + + phy = devm_kzalloc(&dev->dev, sizeof(struct st33zp24_spi_phy), + GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->spi_device = dev; + pdata = dev->dev.platform_data; + if (!pdata && dev->dev.of_node) { + ret = tpm_stm_spi_of_request_resources(phy); + if (ret) + return ret; + } else if (pdata) { + ret = tpm_stm_spi_request_resources(dev, phy); + if (ret) + return ret; + } + + phy->spi_xfer.tx_buf = phy->tx_buf; + phy->spi_xfer.rx_buf = phy->rx_buf; + + phy->latency = evaluate_latency(phy); + if (phy->latency <= 0) + return -ENODEV; + + return st33zp24_probe(phy, &spi_phy_ops, &dev->dev, dev->irq, + phy->io_lpcpd); +} + +/* + * tpm_st33_spi_remove remove the TPM device + * @param: client, the spi_device drescription (TPM SPI description). + * @return: 0 in case of success. + */ +static int tpm_st33_spi_remove(struct spi_device *dev) +{ + struct tpm_chip *chip = spi_get_drvdata(dev); + + return st33zp24_remove(chip); +} + +static const struct spi_device_id st33zp24_spi_id[] = { + {TPM_ST33_SPI, 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, st33zp24_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id of_st33zp24_spi_match[] = { + { .compatible = "st,st33zp24-spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, of_st33zp24_spi_match); +#endif + +static SIMPLE_DEV_PM_OPS(st33zp24_spi_ops, st33zp24_pm_suspend, + st33zp24_pm_resume); + +static struct spi_driver tpm_st33_spi_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TPM_ST33_SPI, + .pm = &st33zp24_spi_ops, + .of_match_table = of_match_ptr(of_st33zp24_spi_match), + }, + .probe = tpm_st33_spi_probe, + .remove = tpm_st33_spi_remove, + .id_table = st33zp24_spi_id, +}; + +module_spi_driver(tpm_st33_spi_driver); + +MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); +MODULE_DESCRIPTION("STM TPM 1.2 SPI ST33 Driver"); +MODULE_VERSION("1.3.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/st33zp24/st33zp24.c b/drivers/char/tpm/st33zp24/st33zp24.c new file mode 100644 index 000000000000..8d626784cd8d --- /dev/null +++ b/drivers/char/tpm/st33zp24/st33zp24.c @@ -0,0 +1,698 @@ +/* + * STMicroelectronics TPM Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/freezer.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/sched.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/slab.h> + +#include "../tpm.h" +#include "st33zp24.h" + +#define TPM_ACCESS 0x0 +#define TPM_STS 0x18 +#define TPM_DATA_FIFO 0x24 +#define TPM_INTF_CAPABILITY 0x14 +#define TPM_INT_STATUS 0x10 +#define TPM_INT_ENABLE 0x08 + +#define LOCALITY0 0 + +enum st33zp24_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum st33zp24_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum st33zp24_int_flags { + TPM_GLOBAL_INT_ENABLE = 0x80, + TPM_INTF_CMD_READY_INT = 0x080, + TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, + TPM_INTF_WAKE_UP_READY_INT = 0x020, + TPM_INTF_LOCALITY_CHANGE_INT = 0x004, + TPM_INTF_STS_VALID_INT = 0x002, + TPM_INTF_DATA_AVAIL_INT = 0x001, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, + TIS_LONG_TIMEOUT = 2000, +}; + +struct st33zp24_dev { + struct tpm_chip *chip; + void *phy_id; + const struct st33zp24_phy_ops *ops; + u32 intrs; + int io_lpcpd; +}; + +/* + * clear_interruption clear the pending interrupt. + * @param: tpm_dev, the tpm device device. + * @return: the interrupt status value. + */ +static u8 clear_interruption(struct st33zp24_dev *tpm_dev) +{ + u8 interrupt; + + tpm_dev->ops->recv(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1); + tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_STATUS, &interrupt, 1); + return interrupt; +} /* clear_interruption() */ + +/* + * st33zp24_cancel, cancel the current command execution or + * set STS to COMMAND READY. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + */ +static void st33zp24_cancel(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + data = TPM_STS_COMMAND_READY; + tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1); +} /* st33zp24_cancel() */ + +/* + * st33zp24_status return the TPM_STS register + * @param: chip, the tpm chip description + * @return: the TPM_STS register value. + */ +static u8 st33zp24_status(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + tpm_dev->ops->recv(tpm_dev->phy_id, TPM_STS, &data, 1); + return data; +} /* st33zp24_status() */ + +/* + * check_locality if the locality is active + * @param: chip, the tpm chip description + * @return: the active locality or -EACCESS. + */ +static int check_locality(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + u8 status; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + status = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_ACCESS, &data, 1); + if (status && (data & + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) + return chip->vendor.locality; + + return -EACCES; +} /* check_locality() */ + +/* + * request_locality request the TPM locality + * @param: chip, the chip description + * @return: the active locality or negative value. + */ +static int request_locality(struct tpm_chip *chip) +{ + unsigned long stop; + long ret; + struct st33zp24_dev *tpm_dev; + u8 data; + + if (check_locality(chip) == chip->vendor.locality) + return chip->vendor.locality; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + data = TPM_ACCESS_REQUEST_USE; + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); + if (ret < 0) + return ret; + + stop = jiffies + chip->vendor.timeout_a; + + /* Request locality is usually effective after the request */ + do { + if (check_locality(chip) >= 0) + return chip->vendor.locality; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + + /* could not get locality */ + return -EACCES; +} /* request_locality() */ + +/* + * release_locality release the active locality + * @param: chip, the tpm chip description. + */ +static void release_locality(struct tpm_chip *chip) +{ + struct st33zp24_dev *tpm_dev; + u8 data; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + data = TPM_ACCESS_ACTIVE_LOCALITY; + + tpm_dev->ops->send(tpm_dev->phy_id, TPM_ACCESS, &data, 1); +} + +/* + * get_burstcount return the burstcount value + * @param: chip, the chip description + * return: the burstcount or negative value. + */ +static int get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + int burstcnt, status; + u8 tpm_reg, temp; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + stop = jiffies + chip->vendor.timeout_d; + do { + tpm_reg = TPM_STS + 1; + status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + if (status < 0) + return -EBUSY; + + tpm_reg = TPM_STS + 2; + burstcnt = temp; + status = tpm_dev->ops->recv(tpm_dev->phy_id, tpm_reg, &temp, 1); + if (status < 0) + return -EBUSY; + + burstcnt |= temp << 8; + if (burstcnt) + return burstcnt; + msleep(TPM_TIMEOUT); + } while (time_before(jiffies, stop)); + return -EBUSY; +} /* get_burstcount() */ + + +/* + * wait_for_tpm_stat_cond + * @param: chip, chip description + * @param: mask, expected mask value + * @param: check_cancel, does the command expected to be canceled ? + * @param: canceled, did we received a cancel request ? + * @return: true if status == mask or if the command is canceled. + * false in other cases. + */ +static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, + bool check_cancel, bool *canceled) +{ + u8 status = chip->ops->status(chip); + + *canceled = false; + if ((status & mask) == mask) + return true; + if (check_cancel && chip->ops->req_canceled(chip, status)) { + *canceled = true; + return true; + } + return false; +} + +/* + * wait_for_stat wait for a TPM_STS value + * @param: chip, the tpm chip description + * @param: mask, the value mask to wait + * @param: timeout, the timeout + * @param: queue, the wait queue. + * @param: check_cancel, does the command can be cancelled ? + * @return: the tpm status, 0 if success, -ETIME if timeout is reached. + */ +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + wait_queue_head_t *queue, bool check_cancel) +{ + unsigned long stop; + int ret = 0; + bool canceled = false; + bool condition; + u32 cur_intrs; + u8 status; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + /* check current status */ + status = st33zp24_status(chip); + if ((status & mask) == mask) + return 0; + + stop = jiffies + timeout; + + if (chip->vendor.irq) { + cur_intrs = tpm_dev->intrs; + clear_interruption(tpm_dev); + enable_irq(chip->vendor.irq); + + do { + if (ret == -ERESTARTSYS && freezing(current)) + clear_thread_flag(TIF_SIGPENDING); + + timeout = stop - jiffies; + if ((long) timeout <= 0) + return -1; + + ret = wait_event_interruptible_timeout(*queue, + cur_intrs != tpm_dev->intrs, + timeout); + clear_interruption(tpm_dev); + condition = wait_for_tpm_stat_cond(chip, mask, + check_cancel, &canceled); + if (ret >= 0 && condition) { + if (canceled) + return -ECANCELED; + return 0; + } + } while (ret == -ERESTARTSYS && freezing(current)); + + disable_irq_nosync(chip->vendor.irq); + + } else { + do { + msleep(TPM_TIMEOUT); + status = chip->ops->status(chip); + if ((status & mask) == mask) + return 0; + } while (time_before(jiffies, stop)); + } + + return -ETIME; +} /* wait_for_stat() */ + +/* + * recv_data receive data + * @param: chip, the tpm chip description + * @param: buf, the buffer where the data are received + * @param: count, the number of data to receive + * @return: the number of bytes read from TPM FIFO. + */ +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0, burstcnt, len, ret; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + while (size < count && + wait_for_stat(chip, + TPM_STS_DATA_AVAIL | TPM_STS_VALID, + chip->vendor.timeout_c, + &chip->vendor.read_queue, true) == 0) { + burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; + len = min_t(int, burstcnt, count - size); + ret = tpm_dev->ops->recv(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + size, len); + if (ret < 0) + return ret; + + size += len; + } + return size; +} + +/* + * tpm_ioserirq_handler the serirq irq handler + * @param: irq, the tpm chip description + * @param: dev_id, the description of the chip + * @return: the status of the handler. + */ +static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) +{ + struct tpm_chip *chip = dev_id; + struct st33zp24_dev *tpm_dev; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + tpm_dev->intrs++; + wake_up_interruptible(&chip->vendor.read_queue); + disable_irq_nosync(chip->vendor.irq); + + return IRQ_HANDLED; +} /* tpm_ioserirq_handler() */ + +/* + * st33zp24_send send TPM commands through the I2C bus. + * + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h + * @param: buf, the buffer to send. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes sent. + * In other case, a < 0 value describing the issue. + */ +static int st33zp24_send(struct tpm_chip *chip, unsigned char *buf, + size_t len) +{ + u32 status, i, size, ordinal; + int burstcnt = 0; + int ret; + u8 data; + struct st33zp24_dev *tpm_dev; + + if (!chip) + return -EBUSY; + if (len < TPM_HEADER_SIZE) + return -EBUSY; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + ret = request_locality(chip); + if (ret < 0) + return ret; + + status = st33zp24_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + st33zp24_cancel(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, + &chip->vendor.read_queue, false) < 0) { + ret = -ETIME; + goto out_err; + } + } + + for (i = 0; i < len - 1;) { + burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; + size = min_t(int, len - i - 1, burstcnt); + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + i, size); + if (ret < 0) + goto out_err; + + i += size; + } + + status = st33zp24_status(chip); + if ((status & TPM_STS_DATA_EXPECT) == 0) { + ret = -EIO; + goto out_err; + } + + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_DATA_FIFO, + buf + len - 1, 1); + if (ret < 0) + goto out_err; + + status = st33zp24_status(chip); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + ret = -EIO; + goto out_err; + } + + data = TPM_STS_GO; + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_STS, &data, 1); + if (ret < 0) + goto out_err; + + if (chip->vendor.irq) { + ordinal = be32_to_cpu(*((__be32 *) (buf + 6))); + + ret = wait_for_stat(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID, + tpm_calc_ordinal_duration(chip, ordinal), + &chip->vendor.read_queue, false); + if (ret < 0) + goto out_err; + } + + return len; +out_err: + st33zp24_cancel(chip); + release_locality(chip); + return ret; +} + +/* + * st33zp24_recv received TPM response through TPM phy. + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: buf, the buffer to store datas. + * @param: count, the number of bytes to send. + * @return: In case of success the number of bytes received. + * In other case, a < 0 value describing the issue. + */ +static int st33zp24_recv(struct tpm_chip *chip, unsigned char *buf, + size_t count) +{ + int size = 0; + int expected; + + if (!chip) + return -EBUSY; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(&chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *)(buf + 2)); + if (expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(&chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + } + +out: + st33zp24_cancel(chip); + release_locality(chip); + return size; +} + +/* + * st33zp24_req_canceled + * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. + * @param: status, the TPM status. + * @return: Does TPM ready to compute a new command ? true. + */ +static bool st33zp24_req_canceled(struct tpm_chip *chip, u8 status) +{ + return (status == TPM_STS_COMMAND_READY); +} + +static const struct tpm_class_ops st33zp24_tpm = { + .send = st33zp24_send, + .recv = st33zp24_recv, + .cancel = st33zp24_cancel, + .status = st33zp24_status, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = st33zp24_req_canceled, +}; + +/* + * st33zp24_probe initialize the TPM device + * @param: client, the i2c_client drescription (TPM I2C description). + * @param: id, the i2c_device_id struct. + * @return: 0 in case of success. + * -1 in other case. + */ +int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, + struct device *dev, int irq, int io_lpcpd) +{ + int ret; + u8 intmask = 0; + struct tpm_chip *chip; + struct st33zp24_dev *tpm_dev; + + chip = tpmm_chip_alloc(dev, &st33zp24_tpm); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + tpm_dev = devm_kzalloc(dev, sizeof(struct st33zp24_dev), + GFP_KERNEL); + if (!tpm_dev) + return -ENOMEM; + + TPM_VPRIV(chip) = tpm_dev; + tpm_dev->phy_id = phy_id; + tpm_dev->ops = ops; + + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + chip->vendor.locality = LOCALITY0; + + if (irq) { + /* INTERRUPT Setup */ + init_waitqueue_head(&chip->vendor.read_queue); + tpm_dev->intrs = 0; + + if (request_locality(chip) != LOCALITY0) { + ret = -ENODEV; + goto _tpm_clean_answer; + } + + clear_interruption(tpm_dev); + ret = devm_request_irq(dev, irq, tpm_ioserirq_handler, + IRQF_TRIGGER_HIGH, "TPM SERIRQ management", + chip); + if (ret < 0) { + dev_err(&chip->dev, "TPM SERIRQ signals %d not available\n", + irq); + goto _tpm_clean_answer; + } + + intmask |= TPM_INTF_CMD_READY_INT + | TPM_INTF_STS_VALID_INT + | TPM_INTF_DATA_AVAIL_INT; + + ret = tpm_dev->ops->send(tpm_dev->phy_id, TPM_INT_ENABLE, + &intmask, 1); + if (ret < 0) + goto _tpm_clean_answer; + + intmask = TPM_GLOBAL_INT_ENABLE; + ret = tpm_dev->ops->send(tpm_dev->phy_id, (TPM_INT_ENABLE + 3), + &intmask, 1); + if (ret < 0) + goto _tpm_clean_answer; + + chip->vendor.irq = irq; + + disable_irq_nosync(chip->vendor.irq); + + tpm_gen_interrupt(chip); + } + + tpm_get_timeouts(chip); + tpm_do_selftest(chip); + + return tpm_chip_register(chip); +_tpm_clean_answer: + dev_info(&chip->dev, "TPM initialization fail\n"); + return ret; +} +EXPORT_SYMBOL(st33zp24_probe); + +/* + * st33zp24_remove remove the TPM device + * @param: tpm_data, the tpm phy. + * @return: 0 in case of success. + */ +int st33zp24_remove(struct tpm_chip *chip) +{ + tpm_chip_unregister(chip); + return 0; +} +EXPORT_SYMBOL(st33zp24_remove); + +#ifdef CONFIG_PM_SLEEP +/* + * st33zp24_pm_suspend suspend the TPM device + * @param: tpm_data, the tpm phy. + * @param: mesg, the power management message. + * @return: 0 in case of success. + */ +int st33zp24_pm_suspend(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_dev *tpm_dev; + int ret = 0; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + if (gpio_is_valid(tpm_dev->io_lpcpd)) + gpio_set_value(tpm_dev->io_lpcpd, 0); + else + ret = tpm_pm_suspend(dev); + + return ret; +} /* st33zp24_pm_suspend() */ +EXPORT_SYMBOL(st33zp24_pm_suspend); + +/* + * st33zp24_pm_resume resume the TPM device + * @param: tpm_data, the tpm phy. + * @return: 0 in case of success. + */ +int st33zp24_pm_resume(struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + struct st33zp24_dev *tpm_dev; + int ret = 0; + + tpm_dev = (struct st33zp24_dev *)TPM_VPRIV(chip); + + if (gpio_is_valid(tpm_dev->io_lpcpd)) { + gpio_set_value(tpm_dev->io_lpcpd, 1); + ret = wait_for_stat(chip, + TPM_STS_VALID, chip->vendor.timeout_b, + &chip->vendor.read_queue, false); + } else { + ret = tpm_pm_resume(dev); + if (!ret) + tpm_do_selftest(chip); + } + return ret; +} /* st33zp24_pm_resume() */ +EXPORT_SYMBOL(st33zp24_pm_resume); +#endif + +MODULE_AUTHOR("TPM support (TPMsupport@list.st.com)"); +MODULE_DESCRIPTION("ST33ZP24 TPM 1.2 driver"); +MODULE_VERSION("1.3.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/st33zp24/st33zp24.h b/drivers/char/tpm/st33zp24/st33zp24.h new file mode 100644 index 000000000000..c207cebf67dd --- /dev/null +++ b/drivers/char/tpm/st33zp24/st33zp24.h @@ -0,0 +1,37 @@ +/* + * STMicroelectronics TPM Linux driver for TPM ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 __LOCAL_ST33ZP24_H__ +#define __LOCAL_ST33ZP24_H__ + +#define TPM_WRITE_DIRECTION 0x80 +#define TPM_BUFSIZE 2048 + +struct st33zp24_phy_ops { + int (*send)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); + int (*recv)(void *phy_id, u8 tpm_register, u8 *tpm_data, int tpm_size); +}; + +#ifdef CONFIG_PM_SLEEP +int st33zp24_pm_suspend(struct device *dev); +int st33zp24_pm_resume(struct device *dev); +#endif + +int st33zp24_probe(void *phy_id, const struct st33zp24_phy_ops *ops, + struct device *dev, int irq, int io_lpcpd); +int st33zp24_remove(struct tpm_chip *chip); +#endif /* __LOCAL_ST33ZP24_H__ */ diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index e096e9cddb40..283f00a7f036 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -170,6 +170,41 @@ static void tpm_dev_del_device(struct tpm_chip *chip) device_unregister(&chip->dev); } +static int tpm1_chip_register(struct tpm_chip *chip) +{ + int rc; + + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return 0; + + rc = tpm_sysfs_add_device(chip); + if (rc) + return rc; + + rc = tpm_add_ppi(chip); + if (rc) { + tpm_sysfs_del_device(chip); + return rc; + } + + chip->bios_dir = tpm_bios_log_setup(chip->devname); + + return 0; +} + +static void tpm1_chip_unregister(struct tpm_chip *chip) +{ + if (chip->flags & TPM_CHIP_FLAG_TPM2) + return; + + if (chip->bios_dir) + tpm_bios_log_teardown(chip->bios_dir); + + tpm_remove_ppi(chip); + + tpm_sysfs_del_device(chip); +} + /* * tpm_chip_register() - create a character device for the TPM chip * @chip: TPM chip to use. @@ -185,22 +220,13 @@ int tpm_chip_register(struct tpm_chip *chip) { int rc; - /* Populate sysfs for TPM1 devices. */ - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - rc = tpm_sysfs_add_device(chip); - if (rc) - goto del_misc; - - rc = tpm_add_ppi(chip); - if (rc) - goto del_sysfs; - - chip->bios_dir = tpm_bios_log_setup(chip->devname); - } + rc = tpm1_chip_register(chip); + if (rc) + return rc; rc = tpm_dev_add_device(chip); if (rc) - return rc; + goto out_err; /* Make the chip available. */ spin_lock(&driver_lock); @@ -210,10 +236,8 @@ int tpm_chip_register(struct tpm_chip *chip) chip->flags |= TPM_CHIP_FLAG_REGISTERED; return 0; -del_sysfs: - tpm_sysfs_del_device(chip); -del_misc: - tpm_dev_del_device(chip); +out_err: + tpm1_chip_unregister(chip); return rc; } EXPORT_SYMBOL_GPL(tpm_chip_register); @@ -238,13 +262,7 @@ void tpm_chip_unregister(struct tpm_chip *chip) spin_unlock(&driver_lock); synchronize_rcu(); - if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { - if (chip->bios_dir) - tpm_bios_log_teardown(chip->bios_dir); - tpm_remove_ppi(chip); - tpm_sysfs_del_device(chip); - } - + tpm1_chip_unregister(chip); tpm_dev_del_device(chip); } EXPORT_SYMBOL_GPL(tpm_chip_unregister); diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c deleted file mode 100644 index 612845b36c29..000000000000 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ /dev/null @@ -1,911 +0,0 @@ -/* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010, 2014 STMicroelectronics - * - * 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, see <http://www.gnu.org/licenses/>. - * - * STMicroelectronics version 1.2.1, Copyright (C) 2014 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: tpm_stm_st33_i2c.c - * - * @Synopsis: - * 09/15/2010: First shot driver tpm_tis driver for - * lpc is used as model. - */ - -#include <linux/pci.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/i2c.h> -#include <linux/fs.h> -#include <linux/miscdevice.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/wait.h> -#include <linux/freezer.h> -#include <linux/string.h> -#include <linux/interrupt.h> -#include <linux/sysfs.h> -#include <linux/gpio.h> -#include <linux/sched.h> -#include <linux/uaccess.h> -#include <linux/io.h> -#include <linux/slab.h> -#include <linux/of_irq.h> -#include <linux/of_gpio.h> - -#include <linux/platform_data/tpm_stm_st33.h> -#include "tpm.h" - -#define TPM_ACCESS 0x0 -#define TPM_STS 0x18 -#define TPM_HASH_END 0x20 -#define TPM_DATA_FIFO 0x24 -#define TPM_HASH_DATA 0x24 -#define TPM_HASH_START 0x28 -#define TPM_INTF_CAPABILITY 0x14 -#define TPM_INT_STATUS 0x10 -#define TPM_INT_ENABLE 0x08 - -#define TPM_DUMMY_BYTE 0xAA -#define TPM_WRITE_DIRECTION 0x80 -#define TPM_HEADER_SIZE 10 -#define TPM_BUFSIZE 2048 - -#define LOCALITY0 0 - - -enum stm33zp24_access { - TPM_ACCESS_VALID = 0x80, - TPM_ACCESS_ACTIVE_LOCALITY = 0x20, - TPM_ACCESS_REQUEST_PENDING = 0x04, - TPM_ACCESS_REQUEST_USE = 0x02, -}; - -enum stm33zp24_status { - TPM_STS_VALID = 0x80, - TPM_STS_COMMAND_READY = 0x40, - TPM_STS_GO = 0x20, - TPM_STS_DATA_AVAIL = 0x10, - TPM_STS_DATA_EXPECT = 0x08, -}; - -enum stm33zp24_int_flags { - TPM_GLOBAL_INT_ENABLE = 0x80, - TPM_INTF_CMD_READY_INT = 0x080, - TPM_INTF_FIFO_AVALAIBLE_INT = 0x040, - TPM_INTF_WAKE_UP_READY_INT = 0x020, - TPM_INTF_LOCALITY_CHANGE_INT = 0x004, - TPM_INTF_STS_VALID_INT = 0x002, - TPM_INTF_DATA_AVAIL_INT = 0x001, -}; - -enum tis_defaults { - TIS_SHORT_TIMEOUT = 750, - TIS_LONG_TIMEOUT = 2000, -}; - -struct tpm_stm_dev { - struct i2c_client *client; - struct tpm_chip *chip; - u8 buf[TPM_BUFSIZE + 1]; - u32 intrs; - int io_lpcpd; -}; - -/* - * write8_reg - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: Returns negative errno, or else the number of bytes written. - */ -static int write8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, - u8 *tpm_data, u16 tpm_size) -{ - tpm_dev->buf[0] = tpm_register; - memcpy(tpm_dev->buf + 1, tpm_data, tpm_size); - return i2c_master_send(tpm_dev->client, tpm_dev->buf, tpm_size + 1); -} /* write8_reg() */ - -/* - * read8_reg - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -static int read8_reg(struct tpm_stm_dev *tpm_dev, u8 tpm_register, - u8 *tpm_data, int tpm_size) -{ - u8 status = 0; - u8 data; - - data = TPM_DUMMY_BYTE; - status = write8_reg(tpm_dev, tpm_register, &data, 1); - if (status == 2) - status = i2c_master_recv(tpm_dev->client, tpm_data, tpm_size); - return status; -} /* read8_reg() */ - -/* - * I2C_WRITE_DATA - * Send byte to the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_dev, the chip description - * @param: tpm_register, the tpm tis register where the data should be written - * @param: tpm_data, the tpm_data to write inside the tpm_register - * @param: tpm_size, The length of the data - * @return: number of byte written successfully: should be one if success. - */ -#define I2C_WRITE_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \ - (write8_reg(tpm_dev, tpm_register | \ - TPM_WRITE_DIRECTION, tpm_data, tpm_size)) - -/* - * I2C_READ_DATA - * Recv byte from the TIS register according to the ST33ZP24 I2C protocol. - * @param: tpm_dev, the chip description - * @param: tpm_register, the tpm tis register where the data should be read - * @param: tpm_data, the TPM response - * @param: tpm_size, tpm TPM response size to read. - * @return: number of byte read successfully: should be one if success. - */ -#define I2C_READ_DATA(tpm_dev, tpm_register, tpm_data, tpm_size) \ - (read8_reg(tpm_dev, tpm_register, tpm_data, tpm_size)) - -/* - * clear_interruption - * clear the TPM interrupt register. - * @param: tpm, the chip description - * @return: the TPM_INT_STATUS value - */ -static u8 clear_interruption(struct tpm_stm_dev *tpm_dev) -{ - u8 interrupt; - - I2C_READ_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); - I2C_WRITE_DATA(tpm_dev, TPM_INT_STATUS, &interrupt, 1); - return interrupt; -} /* clear_interruption() */ - -/* - * tpm_stm_i2c_cancel, cancel is not implemented. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - */ -static void tpm_stm_i2c_cancel(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - data = TPM_STS_COMMAND_READY; - I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); -} /* tpm_stm_i2c_cancel() */ - -/* - * tpm_stm_spi_status return the TPM_STS register - * @param: chip, the tpm chip description - * @return: the TPM_STS register value. - */ -static u8 tpm_stm_i2c_status(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - I2C_READ_DATA(tpm_dev, TPM_STS, &data, 1); - return data; -} /* tpm_stm_i2c_status() */ - - -/* - * check_locality if the locality is active - * @param: chip, the tpm chip description - * @return: the active locality or -EACCESS. - */ -static int check_locality(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - u8 status; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - status = I2C_READ_DATA(tpm_dev, TPM_ACCESS, &data, 1); - if (status && (data & - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == - (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) - return chip->vendor.locality; - - return -EACCES; -} /* check_locality() */ - -/* - * request_locality request the TPM locality - * @param: chip, the chip description - * @return: the active locality or EACCESS. - */ -static int request_locality(struct tpm_chip *chip) -{ - unsigned long stop; - long ret; - struct tpm_stm_dev *tpm_dev; - u8 data; - - if (check_locality(chip) == chip->vendor.locality) - return chip->vendor.locality; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - data = TPM_ACCESS_REQUEST_USE; - ret = I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); - if (ret < 0) - goto end; - - stop = jiffies + chip->vendor.timeout_a; - - /* Request locality is usually effective after the request */ - do { - if (check_locality(chip) >= 0) - return chip->vendor.locality; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - ret = -EACCES; -end: - return ret; -} /* request_locality() */ - -/* - * release_locality release the active locality - * @param: chip, the tpm chip description. - */ -static void release_locality(struct tpm_chip *chip) -{ - struct tpm_stm_dev *tpm_dev; - u8 data; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - data = TPM_ACCESS_ACTIVE_LOCALITY; - - I2C_WRITE_DATA(tpm_dev, TPM_ACCESS, &data, 1); -} - -/* - * get_burstcount return the burstcount address 0x19 0x1A - * @param: chip, the chip description - * return: the burstcount. - */ -static int get_burstcount(struct tpm_chip *chip) -{ - unsigned long stop; - int burstcnt, status; - u8 tpm_reg, temp; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - stop = jiffies + chip->vendor.timeout_d; - do { - tpm_reg = TPM_STS + 1; - status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - tpm_reg = tpm_reg + 1; - burstcnt = temp; - status = I2C_READ_DATA(tpm_dev, tpm_reg, &temp, 1); - if (status < 0) - goto end; - - burstcnt |= temp << 8; - if (burstcnt) - return burstcnt; - msleep(TPM_TIMEOUT); - } while (time_before(jiffies, stop)); - -end: - return -EBUSY; -} /* get_burstcount() */ - -static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, - bool check_cancel, bool *canceled) -{ - u8 status = chip->ops->status(chip); - - *canceled = false; - if ((status & mask) == mask) - return true; - if (check_cancel && chip->ops->req_canceled(chip, status)) { - *canceled = true; - return true; - } - return false; -} - -/* - * interrupt_to_status - * @param: irq_mask, the irq mask value to wait - * @return: the corresponding tpm_sts value - */ -static u8 interrupt_to_status(u8 irq_mask) -{ - u8 status = 0; - - if ((irq_mask & TPM_INTF_STS_VALID_INT) == TPM_INTF_STS_VALID_INT) - status |= TPM_STS_VALID; - if ((irq_mask & TPM_INTF_DATA_AVAIL_INT) == TPM_INTF_DATA_AVAIL_INT) - status |= TPM_STS_DATA_AVAIL; - if ((irq_mask & TPM_INTF_CMD_READY_INT) == TPM_INTF_CMD_READY_INT) - status |= TPM_STS_COMMAND_READY; - - return status; -} /* status_to_interrupt() */ - -/* - * wait_for_stat wait for a TPM_STS value - * @param: chip, the tpm chip description - * @param: mask, the value mask to wait - * @param: timeout, the timeout - * @param: queue, the wait queue. - * @param: check_cancel, does the command can be cancelled ? - * @return: the tpm status, 0 if success, -ETIME if timeout is reached. - */ -static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, - wait_queue_head_t *queue, bool check_cancel) -{ - unsigned long stop; - int ret; - bool canceled = false; - bool condition; - u32 cur_intrs; - u8 interrupt, status; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - /* check current status */ - status = tpm_stm_i2c_status(chip); - if ((status & mask) == mask) - return 0; - - stop = jiffies + timeout; - - if (chip->vendor.irq) { - cur_intrs = tpm_dev->intrs; - interrupt = clear_interruption(tpm_dev); - enable_irq(chip->vendor.irq); - -again: - timeout = stop - jiffies; - if ((long) timeout <= 0) - return -1; - - ret = wait_event_interruptible_timeout(*queue, - cur_intrs != tpm_dev->intrs, timeout); - - interrupt |= clear_interruption(tpm_dev); - status = interrupt_to_status(interrupt); - condition = wait_for_tpm_stat_cond(chip, mask, - check_cancel, &canceled); - - if (ret >= 0 && condition) { - if (canceled) - return -ECANCELED; - return 0; - } - if (ret == -ERESTARTSYS && freezing(current)) { - clear_thread_flag(TIF_SIGPENDING); - goto again; - } - disable_irq_nosync(chip->vendor.irq); - - } else { - do { - msleep(TPM_TIMEOUT); - status = chip->ops->status(chip); - if ((status & mask) == mask) - return 0; - } while (time_before(jiffies, stop)); - } - - return -ETIME; -} /* wait_for_stat() */ - -/* - * recv_data receive data - * @param: chip, the tpm chip description - * @param: buf, the buffer where the data are received - * @param: count, the number of data to receive - * @return: the number of bytes read from TPM FIFO. - */ -static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) -{ - int size = 0, burstcnt, len, ret; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - while (size < count && - wait_for_stat(chip, - TPM_STS_DATA_AVAIL | TPM_STS_VALID, - chip->vendor.timeout_c, - &chip->vendor.read_queue, true) == 0) { - burstcnt = get_burstcount(chip); - if (burstcnt < 0) - return burstcnt; - len = min_t(int, burstcnt, count - size); - ret = I2C_READ_DATA(tpm_dev, TPM_DATA_FIFO, buf + size, len); - if (ret < 0) - return ret; - - size += len; - } - return size; -} - -/* - * tpm_ioserirq_handler the serirq irq handler - * @param: irq, the tpm chip description - * @param: dev_id, the description of the chip - * @return: the status of the handler. - */ -static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) -{ - struct tpm_chip *chip = dev_id; - struct tpm_stm_dev *tpm_dev; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - - tpm_dev->intrs++; - wake_up_interruptible(&chip->vendor.read_queue); - disable_irq_nosync(chip->vendor.irq); - - return IRQ_HANDLED; -} /* tpm_ioserirq_handler() */ - - -/* - * tpm_stm_i2c_send send TPM commands through the I2C bus. - * - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h - * @param: buf, the buffer to send. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes sent. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, - size_t len) -{ - u32 status, i, size; - int burstcnt = 0; - int ret; - u8 data; - struct i2c_client *client; - struct tpm_stm_dev *tpm_dev; - - if (!chip) - return -EBUSY; - if (len < TPM_HEADER_SIZE) - return -EBUSY; - - tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - client = tpm_dev->client; - - client->flags = 0; - - ret = request_locality(chip); - if (ret < 0) - return ret; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_COMMAND_READY) == 0) { - tpm_stm_i2c_cancel(chip); - if (wait_for_stat - (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b, - &chip->vendor.read_queue, false) < 0) { - ret = -ETIME; - goto out_err; - } - } - - for (i = 0; i < len - 1;) { - burstcnt = get_burstcount(chip); - if (burstcnt < 0) - return burstcnt; - size = min_t(int, len - i - 1, burstcnt); - ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + i, size); - if (ret < 0) - goto out_err; - - i += size; - } - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) == 0) { - ret = -EIO; - goto out_err; - } - - ret = I2C_WRITE_DATA(tpm_dev, TPM_DATA_FIFO, buf + len - 1, 1); - if (ret < 0) - goto out_err; - - status = tpm_stm_i2c_status(chip); - if ((status & TPM_STS_DATA_EXPECT) != 0) { - ret = -EIO; - goto out_err; - } - - data = TPM_STS_GO; - I2C_WRITE_DATA(tpm_dev, TPM_STS, &data, 1); - - return len; -out_err: - tpm_stm_i2c_cancel(chip); - release_locality(chip); - return ret; -} - -/* - * tpm_stm_i2c_recv received TPM response through the I2C bus. - * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h. - * @param: buf, the buffer to store datas. - * @param: count, the number of bytes to send. - * @return: In case of success the number of bytes received. - * In other case, a < 0 value describing the issue. - */ -static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, - size_t count) -{ - int size = 0; - int expected; - - if (!chip) - return -EBUSY; - - if (count < TPM_HEADER_SIZE) { - size = -EIO; - goto out; - } - - size = recv_data(chip, buf, TPM_HEADER_SIZE); - if (size < TPM_HEADER_SIZE) { - dev_err(chip->pdev, "Unable to read header\n"); - goto out; - } - - expected = be32_to_cpu(*(__be32 *)(buf + 2)); - if (expected > count) { - size = -EIO; - goto out; - } - - size += recv_data(chip, &buf[TPM_HEADER_SIZE], - expected - TPM_HEADER_SIZE); - if (size < expected) { - dev_err(chip->pdev, "Unable to read remainder of result\n"); - size = -ETIME; - goto out; - } - -out: - chip->ops->cancel(chip); - release_locality(chip); - return size; -} - -static bool tpm_stm_i2c_req_canceled(struct tpm_chip *chip, u8 status) -{ - return (status == TPM_STS_COMMAND_READY); -} - -static const struct tpm_class_ops st_i2c_tpm = { - .send = tpm_stm_i2c_send, - .recv = tpm_stm_i2c_recv, - .cancel = tpm_stm_i2c_cancel, - .status = tpm_stm_i2c_status, - .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, - .req_canceled = tpm_stm_i2c_req_canceled, -}; - -#ifdef CONFIG_OF -static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip) -{ - struct device_node *pp; - struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - struct i2c_client *client = tpm_dev->client; - int gpio; - int ret; - - pp = client->dev.of_node; - if (!pp) { - dev_err(chip->pdev, "No platform data\n"); - return -ENODEV; - } - - /* Get GPIO from device tree */ - gpio = of_get_named_gpio(pp, "lpcpd-gpios", 0); - if (gpio < 0) { - dev_err(chip->pdev, "Failed to retrieve lpcpd-gpios from dts.\n"); - tpm_dev->io_lpcpd = -1; - /* - * lpcpd pin is not specified. This is not an issue as - * power management can be also managed by TPM specific - * commands. So leave with a success status code. - */ - return 0; - } - /* GPIO request and configuration */ - ret = devm_gpio_request_one(&client->dev, gpio, - GPIOF_OUT_INIT_HIGH, "TPM IO LPCPD"); - if (ret) { - dev_err(chip->pdev, "Failed to request lpcpd pin\n"); - return -ENODEV; - } - tpm_dev->io_lpcpd = gpio; - - return 0; -} -#else -static int tpm_stm_i2c_of_request_resources(struct tpm_chip *chip) -{ - return -ENODEV; -} -#endif - -static int tpm_stm_i2c_request_resources(struct i2c_client *client, - struct tpm_chip *chip) -{ - struct st33zp24_platform_data *pdata; - struct tpm_stm_dev *tpm_dev = (struct tpm_stm_dev *)TPM_VPRIV(chip); - int ret; - - pdata = client->dev.platform_data; - if (!pdata) { - dev_err(chip->pdev, "No platform data\n"); - return -ENODEV; - } - - /* store for late use */ - tpm_dev->io_lpcpd = pdata->io_lpcpd; - - if (gpio_is_valid(pdata->io_lpcpd)) { - ret = devm_gpio_request_one(&client->dev, - pdata->io_lpcpd, GPIOF_OUT_INIT_HIGH, - "TPM IO_LPCPD"); - if (ret) { - dev_err(chip->pdev, "%s : reset gpio_request failed\n", - __FILE__); - return ret; - } - } - - return 0; -} - -/* - * tpm_stm_i2c_probe initialize the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: id, the i2c_device_id struct. - * @return: 0 in case of success. - * -1 in other case. - */ -static int -tpm_stm_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int ret; - u8 intmask = 0; - struct tpm_chip *chip; - struct st33zp24_platform_data *platform_data; - struct tpm_stm_dev *tpm_dev; - - if (!client) { - pr_info("%s: i2c client is NULL. Device not accessible.\n", - __func__); - return -ENODEV; - } - - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { - dev_info(&client->dev, "client not i2c capable\n"); - return -ENODEV; - } - - tpm_dev = devm_kzalloc(&client->dev, sizeof(struct tpm_stm_dev), - GFP_KERNEL); - if (!tpm_dev) - return -ENOMEM; - - chip = tpmm_chip_alloc(&client->dev, &st_i2c_tpm); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - TPM_VPRIV(chip) = tpm_dev; - tpm_dev->client = client; - - platform_data = client->dev.platform_data; - if (!platform_data && client->dev.of_node) { - ret = tpm_stm_i2c_of_request_resources(chip); - if (ret) - goto _tpm_clean_answer; - } else if (platform_data) { - ret = tpm_stm_i2c_request_resources(client, chip); - if (ret) - goto _tpm_clean_answer; - } - - chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); - chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); - - chip->vendor.locality = LOCALITY0; - - if (client->irq) { - /* INTERRUPT Setup */ - init_waitqueue_head(&chip->vendor.read_queue); - tpm_dev->intrs = 0; - - if (request_locality(chip) != LOCALITY0) { - ret = -ENODEV; - goto _tpm_clean_answer; - } - - clear_interruption(tpm_dev); - ret = devm_request_irq(&client->dev, client->irq, - tpm_ioserirq_handler, - IRQF_TRIGGER_HIGH, - "TPM SERIRQ management", chip); - if (ret < 0) { - dev_err(chip->pdev, "TPM SERIRQ signals %d not available\n", - client->irq); - goto _tpm_clean_answer; - } - - intmask |= TPM_INTF_CMD_READY_INT - | TPM_INTF_STS_VALID_INT - | TPM_INTF_DATA_AVAIL_INT; - - ret = I2C_WRITE_DATA(tpm_dev, TPM_INT_ENABLE, &intmask, 1); - if (ret < 0) - goto _tpm_clean_answer; - - intmask = TPM_GLOBAL_INT_ENABLE; - ret = I2C_WRITE_DATA(tpm_dev, (TPM_INT_ENABLE + 3), - &intmask, 1); - if (ret < 0) - goto _tpm_clean_answer; - - chip->vendor.irq = client->irq; - - disable_irq_nosync(chip->vendor.irq); - - tpm_gen_interrupt(chip); - } - - tpm_get_timeouts(chip); - tpm_do_selftest(chip); - - return tpm_chip_register(chip); -_tpm_clean_answer: - dev_info(chip->pdev, "TPM I2C initialisation fail\n"); - return ret; -} - -/* - * tpm_stm_i2c_remove remove the TPM device - * @param: client, the i2c_client description (TPM I2C description). - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_remove(struct i2c_client *client) -{ - struct tpm_chip *chip = - (struct tpm_chip *) i2c_get_clientdata(client); - - if (chip) - tpm_chip_unregister(chip); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -/* - * tpm_stm_i2c_pm_suspend suspend the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @param: mesg, the power management message. - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_pm_suspend(struct device *dev) -{ - struct st33zp24_platform_data *pin_infos = dev->platform_data; - int ret = 0; - - if (gpio_is_valid(pin_infos->io_lpcpd)) - gpio_set_value(pin_infos->io_lpcpd, 0); - else - ret = tpm_pm_suspend(dev); - - return ret; -} /* tpm_stm_i2c_suspend() */ - -/* - * tpm_stm_i2c_pm_resume resume the TPM device - * @param: client, the i2c_client drescription (TPM I2C description). - * @return: 0 in case of success. - */ -static int tpm_stm_i2c_pm_resume(struct device *dev) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - struct st33zp24_platform_data *pin_infos = dev->platform_data; - - int ret = 0; - - if (gpio_is_valid(pin_infos->io_lpcpd)) { - gpio_set_value(pin_infos->io_lpcpd, 1); - ret = wait_for_stat(chip, - TPM_STS_VALID, chip->vendor.timeout_b, - &chip->vendor.read_queue, false); - } else { - ret = tpm_pm_resume(dev); - if (!ret) - tpm_do_selftest(chip); - } - return ret; -} /* tpm_stm_i2c_pm_resume() */ -#endif - -static const struct i2c_device_id tpm_stm_i2c_id[] = { - {TPM_ST33_I2C, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, tpm_stm_i2c_id); - -#ifdef CONFIG_OF -static const struct of_device_id of_st33zp24_i2c_match[] = { - { .compatible = "st,st33zp24-i2c", }, - {} -}; -MODULE_DEVICE_TABLE(of, of_st33zp24_i2c_match); -#endif - -static SIMPLE_DEV_PM_OPS(tpm_stm_i2c_ops, tpm_stm_i2c_pm_suspend, - tpm_stm_i2c_pm_resume); - -static struct i2c_driver tpm_stm_i2c_driver = { - .driver = { - .owner = THIS_MODULE, - .name = TPM_ST33_I2C, - .pm = &tpm_stm_i2c_ops, - .of_match_table = of_match_ptr(of_st33zp24_i2c_match), - }, - .probe = tpm_stm_i2c_probe, - .remove = tpm_stm_i2c_remove, - .id_table = tpm_stm_i2c_id -}; - -module_i2c_driver(tpm_stm_i2c_driver); - -MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)"); -MODULE_DESCRIPTION("STM TPM I2C ST33 Driver"); -MODULE_VERSION("1.2.1"); -MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 29ba520ac24d..6c488e635fdd 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -591,27 +591,8 @@ static void tpm_inf_pnp_remove(struct pnp_dev *dev) } } -static int tpm_inf_pnp_suspend(struct pnp_dev *dev, pm_message_t pm_state) -{ - struct tpm_chip *chip = pnp_get_drvdata(dev); - int rc; - if (chip) { - u8 savestate[] = { - 0, 193, /* TPM_TAG_RQU_COMMAND */ - 0, 0, 0, 10, /* blob length (in bytes) */ - 0, 0, 0, 152 /* TPM_ORD_SaveState */ - }; - dev_info(&dev->dev, "saving TPM state\n"); - rc = tpm_inf_send(chip, savestate, sizeof(savestate)); - if (rc < 0) { - dev_err(&dev->dev, "error while saving TPM state\n"); - return rc; - } - } - return 0; -} - -static int tpm_inf_pnp_resume(struct pnp_dev *dev) +#ifdef CONFIG_PM_SLEEP +static int tpm_inf_resume(struct device *dev) { /* Re-configure TPM after suspending */ tpm_config_out(ENABLE_REGISTER_PAIR, TPM_INF_ADDR); @@ -625,16 +606,19 @@ static int tpm_inf_pnp_resume(struct pnp_dev *dev) tpm_config_out(DISABLE_REGISTER_PAIR, TPM_INF_ADDR); /* disable RESET, LP and IRQC */ tpm_data_out(RESET_LP_IRQC_DISABLE, CMD); - return tpm_pm_resume(&dev->dev); + return tpm_pm_resume(dev); } +#endif +static SIMPLE_DEV_PM_OPS(tpm_inf_pm, tpm_pm_suspend, tpm_inf_resume); static struct pnp_driver tpm_inf_pnp_driver = { .name = "tpm_inf_pnp", .id_table = tpm_inf_pnp_tbl, .probe = tpm_inf_pnp_probe, - .suspend = tpm_inf_pnp_suspend, - .resume = tpm_inf_pnp_resume, - .remove = tpm_inf_pnp_remove + .remove = tpm_inf_pnp_remove, + .driver = { + .pm = &tpm_inf_pm, + } }; module_pnp_driver(tpm_inf_pnp_driver); diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 2fb0fdfc87df..800bf41718e1 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -391,7 +391,7 @@ config CRYPTO_DEV_ATMEL_SHA config CRYPTO_DEV_CCP bool "Support for AMD Cryptographic Coprocessor" - depends on (X86 && PCI) || ARM64 + depends on ((X86 && PCI) || (ARM64 && (OF_ADDRESS || ACPI))) && HAS_IOMEM default n help The AMD Cryptographic Coprocessor provides hardware support @@ -436,4 +436,26 @@ config CRYPTO_DEV_QCE hardware. To compile this driver as a module, choose M here. The module will be called qcrypto. +config CRYPTO_DEV_VMX + bool "Support for VMX cryptographic acceleration instructions" + depends on PPC64 + default n + help + Support for VMX cryptographic acceleration instructions. + +source "drivers/crypto/vmx/Kconfig" + +config CRYPTO_DEV_IMGTEC_HASH + depends on MIPS || COMPILE_TEST + tristate "Imagination Technologies hardware hash accelerator" + select CRYPTO_ALGAPI + select CRYPTO_MD5 + select CRYPTO_SHA1 + select CRYPTO_SHA256 + select CRYPTO_HASH + help + This driver interfaces with the Imagination Technologies + hardware hash accelerator. Supporting MD5/SHA1/SHA224/SHA256 + hashing algorithms. + endif # CRYPTO_HW diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 3924f93d5774..fb84be7e6be5 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o +obj-$(CONFIG_CRYPTO_DEV_IMGTEC_HASH) += img-hash.o obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o obj-$(CONFIG_CRYPTO_DEV_MV_CESA) += mv_cesa.o obj-$(CONFIG_CRYPTO_DEV_MXS_DCP) += mxs-dcp.o @@ -25,3 +26,4 @@ obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/ obj-$(CONFIG_CRYPTO_DEV_QAT) += qat/ obj-$(CONFIG_CRYPTO_DEV_QCE) += qce/ +obj-$(CONFIG_CRYPTO_DEV_VMX) += vmx/ diff --git a/drivers/crypto/amcc/crypto4xx_core.c b/drivers/crypto/amcc/crypto4xx_core.c index d02b77150070..3b28e8c3de28 100644 --- a/drivers/crypto/amcc/crypto4xx_core.c +++ b/drivers/crypto/amcc/crypto4xx_core.c @@ -1155,7 +1155,7 @@ struct crypto4xx_alg_common crypto4xx_alg[] = { /** * Module Initialization Routine */ -static int __init crypto4xx_probe(struct platform_device *ofdev) +static int crypto4xx_probe(struct platform_device *ofdev) { int rc; struct resource res; @@ -1263,7 +1263,7 @@ err_alloc_dev: return rc; } -static int __exit crypto4xx_remove(struct platform_device *ofdev) +static int crypto4xx_remove(struct platform_device *ofdev) { struct device *dev = &ofdev->dev; struct crypto4xx_core_device *core_dev = dev_get_drvdata(dev); @@ -1291,7 +1291,7 @@ static struct platform_driver crypto4xx_driver = { .of_match_table = crypto4xx_match, }, .probe = crypto4xx_probe, - .remove = __exit_p(crypto4xx_remove), + .remove = crypto4xx_remove, }; module_platform_driver(crypto4xx_driver); diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c index 6597aac9905d..0f9a9dc06a83 100644 --- a/drivers/crypto/atmel-aes.c +++ b/drivers/crypto/atmel-aes.c @@ -315,10 +315,10 @@ static int atmel_aes_crypt_dma(struct atmel_aes_dev *dd, dd->dma_size = length; - if (!(dd->flags & AES_FLAGS_FAST)) { - dma_sync_single_for_device(dd->dev, dma_addr_in, length, - DMA_TO_DEVICE); - } + dma_sync_single_for_device(dd->dev, dma_addr_in, length, + DMA_TO_DEVICE); + dma_sync_single_for_device(dd->dev, dma_addr_out, length, + DMA_FROM_DEVICE); if (dd->flags & AES_FLAGS_CFB8) { dd->dma_lch_in.dma_conf.dst_addr_width = @@ -391,6 +391,11 @@ static int atmel_aes_crypt_cpu_start(struct atmel_aes_dev *dd) { dd->flags &= ~AES_FLAGS_DMA; + dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in, + dd->dma_size, DMA_TO_DEVICE); + dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out, + dd->dma_size, DMA_FROM_DEVICE); + /* use cache buffers */ dd->nb_in_sg = atmel_aes_sg_length(dd->req, dd->in_sg); if (!dd->nb_in_sg) @@ -459,6 +464,9 @@ static int atmel_aes_crypt_dma_start(struct atmel_aes_dev *dd) dd->flags |= AES_FLAGS_FAST; } else { + dma_sync_single_for_cpu(dd->dev, dd->dma_addr_in, + dd->dma_size, DMA_TO_DEVICE); + /* use cache buffers */ count = atmel_aes_sg_copy(&dd->in_sg, &dd->in_offset, dd->buf_in, dd->buflen, dd->total, 0); @@ -619,7 +627,7 @@ static int atmel_aes_crypt_dma_stop(struct atmel_aes_dev *dd) dma_unmap_sg(dd->dev, dd->out_sg, 1, DMA_FROM_DEVICE); dma_unmap_sg(dd->dev, dd->in_sg, 1, DMA_TO_DEVICE); } else { - dma_sync_single_for_device(dd->dev, dd->dma_addr_out, + dma_sync_single_for_cpu(dd->dev, dd->dma_addr_out, dd->dma_size, DMA_FROM_DEVICE); /* copy data */ @@ -1246,6 +1254,11 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd) /* keep only major version number */ switch (dd->hw_version & 0xff0) { + case 0x200: + dd->caps.has_dualbuff = 1; + dd->caps.has_cfb64 = 1; + dd->caps.max_burst_size = 4; + break; case 0x130: dd->caps.has_dualbuff = 1; dd->caps.has_cfb64 = 1; @@ -1336,6 +1349,7 @@ static int atmel_aes_probe(struct platform_device *pdev) platform_set_drvdata(pdev, aes_dd); INIT_LIST_HEAD(&aes_dd->list); + spin_lock_init(&aes_dd->lock); tasklet_init(&aes_dd->done_task, atmel_aes_done_task, (unsigned long)aes_dd); @@ -1374,7 +1388,7 @@ static int atmel_aes_probe(struct platform_device *pdev) /* Initializing the clock */ aes_dd->iclk = clk_get(&pdev->dev, "aes_clk"); if (IS_ERR(aes_dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); + dev_err(dev, "clock initialization failed.\n"); err = PTR_ERR(aes_dd->iclk); goto clk_err; } diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c index 34db04addc18..5b35433c5399 100644 --- a/drivers/crypto/atmel-sha.c +++ b/drivers/crypto/atmel-sha.c @@ -163,8 +163,20 @@ static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx) count = min(ctx->sg->length - ctx->offset, ctx->total); count = min(count, ctx->buflen - ctx->bufcnt); - if (count <= 0) - break; + if (count <= 0) { + /* + * Check if count <= 0 because the buffer is full or + * because the sg length is 0. In the latest case, + * check if there is another sg in the list, a 0 length + * sg doesn't necessarily mean the end of the sg list. + */ + if ((ctx->sg->length == 0) && !sg_is_last(ctx->sg)) { + ctx->sg = sg_next(ctx->sg); + continue; + } else { + break; + } + } scatterwalk_map_and_copy(ctx->buffer + ctx->bufcnt, ctx->sg, ctx->offset, count, 0); @@ -420,14 +432,8 @@ static int atmel_sha_xmit_dma(struct atmel_sha_dev *dd, dma_addr_t dma_addr1, dev_dbg(dd->dev, "xmit_dma: digcnt: 0x%llx 0x%llx, length: %d, final: %d\n", ctx->digcnt[1], ctx->digcnt[0], length1, final); - if (ctx->flags & (SHA_FLAGS_SHA1 | SHA_FLAGS_SHA224 | - SHA_FLAGS_SHA256)) { - dd->dma_lch_in.dma_conf.src_maxburst = 16; - dd->dma_lch_in.dma_conf.dst_maxburst = 16; - } else { - dd->dma_lch_in.dma_conf.src_maxburst = 32; - dd->dma_lch_in.dma_conf.dst_maxburst = 32; - } + dd->dma_lch_in.dma_conf.src_maxburst = 16; + dd->dma_lch_in.dma_conf.dst_maxburst = 16; dmaengine_slave_config(dd->dma_lch_in.chan, &dd->dma_lch_in.dma_conf); @@ -529,7 +535,7 @@ static int atmel_sha_update_dma_slow(struct atmel_sha_dev *dd) if (final) atmel_sha_fill_padding(ctx, 0); - if (final || (ctx->bufcnt == ctx->buflen && ctx->total)) { + if (final || (ctx->bufcnt == ctx->buflen)) { count = ctx->bufcnt; ctx->bufcnt = 0; return atmel_sha_xmit_dma_map(dd, ctx, count, final); @@ -1266,6 +1272,12 @@ static void atmel_sha_get_cap(struct atmel_sha_dev *dd) /* keep only major version number */ switch (dd->hw_version & 0xff0) { + case 0x420: + dd->caps.has_dma = 1; + dd->caps.has_dualbuff = 1; + dd->caps.has_sha224 = 1; + dd->caps.has_sha_384_512 = 1; + break; case 0x410: dd->caps.has_dma = 1; dd->caps.has_dualbuff = 1; @@ -1349,6 +1361,7 @@ static int atmel_sha_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sha_dd); INIT_LIST_HEAD(&sha_dd->list); + spin_lock_init(&sha_dd->lock); tasklet_init(&sha_dd->done_task, atmel_sha_done_task, (unsigned long)sha_dd); @@ -1385,7 +1398,7 @@ static int atmel_sha_probe(struct platform_device *pdev) /* Initializing the clock */ sha_dd->iclk = clk_get(&pdev->dev, "sha_clk"); if (IS_ERR(sha_dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); + dev_err(dev, "clock initialization failed.\n"); err = PTR_ERR(sha_dd->iclk); goto clk_err; } diff --git a/drivers/crypto/atmel-tdes.c b/drivers/crypto/atmel-tdes.c index 258772d9b22f..ca2999709eb4 100644 --- a/drivers/crypto/atmel-tdes.c +++ b/drivers/crypto/atmel-tdes.c @@ -1370,6 +1370,7 @@ static int atmel_tdes_probe(struct platform_device *pdev) platform_set_drvdata(pdev, tdes_dd); INIT_LIST_HEAD(&tdes_dd->list); + spin_lock_init(&tdes_dd->lock); tasklet_init(&tdes_dd->done_task, atmel_tdes_done_task, (unsigned long)tdes_dd); @@ -1408,7 +1409,7 @@ static int atmel_tdes_probe(struct platform_device *pdev) /* Initializing the clock */ tdes_dd->iclk = clk_get(&pdev->dev, "tdes_clk"); if (IS_ERR(tdes_dd->iclk)) { - dev_err(dev, "clock intialization failed.\n"); + dev_err(dev, "clock initialization failed.\n"); err = PTR_ERR(tdes_dd->iclk); goto clk_err; } diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index f347ab7eea95..ba0532efd3ae 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -1172,6 +1172,7 @@ static int ahash_final_no_ctx(struct ahash_request *req) return -ENOMEM; } + edesc->sec4_sg_bytes = 0; sh_len = desc_len(sh_desc); desc = edesc->hw_desc; init_job_desc_shared(desc, ptr, sh_len, HDR_SHARE_DEFER | HDR_REVERSE); diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c index ae31e555793c..26a544b505f1 100644 --- a/drivers/crypto/caam/caamrng.c +++ b/drivers/crypto/caam/caamrng.c @@ -52,7 +52,7 @@ /* length of descriptors */ #define DESC_JOB_O_LEN (CAAM_CMD_SZ * 2 + CAAM_PTR_SZ * 2) -#define DESC_RNG_LEN (10 * CAAM_CMD_SZ) +#define DESC_RNG_LEN (4 * CAAM_CMD_SZ) /* Buffer, its dma address and lock */ struct buf_data { @@ -90,8 +90,8 @@ static inline void rng_unmap_ctx(struct caam_rng_ctx *ctx) struct device *jrdev = ctx->jrdev; if (ctx->sh_desc_dma) - dma_unmap_single(jrdev, ctx->sh_desc_dma, DESC_RNG_LEN, - DMA_TO_DEVICE); + dma_unmap_single(jrdev, ctx->sh_desc_dma, + desc_bytes(ctx->sh_desc), DMA_TO_DEVICE); rng_unmap_buf(jrdev, &ctx->bufs[0]); rng_unmap_buf(jrdev, &ctx->bufs[1]); } diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile index 7f592d8d07bb..55a1f3951578 100644 --- a/drivers/crypto/ccp/Makefile +++ b/drivers/crypto/ccp/Makefile @@ -1,11 +1,6 @@ obj-$(CONFIG_CRYPTO_DEV_CCP_DD) += ccp.o -ccp-objs := ccp-dev.o ccp-ops.o -ifdef CONFIG_X86 -ccp-objs += ccp-pci.o -endif -ifdef CONFIG_ARM64 -ccp-objs += ccp-platform.o -endif +ccp-objs := ccp-dev.o ccp-ops.o ccp-platform.o +ccp-$(CONFIG_PCI) += ccp-pci.o obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o ccp-crypto-objs := ccp-crypto-main.o \ diff --git a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c index 8e162ad82085..ea7e8446956a 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes-cmac.c +++ b/drivers/crypto/ccp/ccp-crypto-aes-cmac.c @@ -23,7 +23,6 @@ #include "ccp-crypto.h" - static int ccp_aes_cmac_complete(struct crypto_async_request *async_req, int ret) { @@ -38,11 +37,13 @@ static int ccp_aes_cmac_complete(struct crypto_async_request *async_req, if (rctx->hash_rem) { /* Save remaining data to buffer */ unsigned int offset = rctx->nbytes - rctx->hash_rem; + scatterwalk_map_and_copy(rctx->buf, rctx->src, offset, rctx->hash_rem, 0); rctx->buf_count = rctx->hash_rem; - } else + } else { rctx->buf_count = 0; + } /* Update result area if supplied */ if (req->result) @@ -202,7 +203,7 @@ static int ccp_aes_cmac_digest(struct ahash_request *req) } static int ccp_aes_cmac_setkey(struct crypto_ahash *tfm, const u8 *key, - unsigned int key_len) + unsigned int key_len) { struct ccp_ctx *ctx = crypto_tfm_ctx(crypto_ahash_tfm(tfm)); struct ccp_crypto_ahash_alg *alg = @@ -292,7 +293,8 @@ static int ccp_aes_cmac_cra_init(struct crypto_tfm *tfm) crypto_ahash_set_reqsize(ahash, sizeof(struct ccp_aes_cmac_req_ctx)); cipher_tfm = crypto_alloc_cipher("aes", 0, - CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK); + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK); if (IS_ERR(cipher_tfm)) { pr_warn("could not load aes cipher driver\n"); return PTR_ERR(cipher_tfm); @@ -354,7 +356,7 @@ int ccp_register_aes_cmac_algs(struct list_head *head) ret = crypto_register_ahash(alg); if (ret) { pr_err("%s ahash algorithm registration error (%d)\n", - base->cra_name, ret); + base->cra_name, ret); kfree(ccp_alg); return ret; } diff --git a/drivers/crypto/ccp/ccp-crypto-aes-xts.c b/drivers/crypto/ccp/ccp-crypto-aes-xts.c index 0cc5594b7de3..52c7395cb8d8 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes-xts.c +++ b/drivers/crypto/ccp/ccp-crypto-aes-xts.c @@ -21,7 +21,6 @@ #include "ccp-crypto.h" - struct ccp_aes_xts_def { const char *name; const char *drv_name; @@ -216,7 +215,6 @@ static void ccp_aes_xts_cra_exit(struct crypto_tfm *tfm) ctx->u.aes.tfm_ablkcipher = NULL; } - static int ccp_register_aes_xts_alg(struct list_head *head, const struct ccp_aes_xts_def *def) { @@ -255,7 +253,7 @@ static int ccp_register_aes_xts_alg(struct list_head *head, ret = crypto_register_alg(alg); if (ret) { pr_err("%s ablkcipher algorithm registration error (%d)\n", - alg->cra_name, ret); + alg->cra_name, ret); kfree(ccp_alg); return ret; } diff --git a/drivers/crypto/ccp/ccp-crypto-aes.c b/drivers/crypto/ccp/ccp-crypto-aes.c index e46490db0f63..7984f910884d 100644 --- a/drivers/crypto/ccp/ccp-crypto-aes.c +++ b/drivers/crypto/ccp/ccp-crypto-aes.c @@ -22,7 +22,6 @@ #include "ccp-crypto.h" - static int ccp_aes_complete(struct crypto_async_request *async_req, int ret) { struct ablkcipher_request *req = ablkcipher_request_cast(async_req); @@ -345,7 +344,7 @@ static int ccp_register_aes_alg(struct list_head *head, ret = crypto_register_alg(alg); if (ret) { pr_err("%s ablkcipher algorithm registration error (%d)\n", - alg->cra_name, ret); + alg->cra_name, ret); kfree(ccp_alg); return ret; } diff --git a/drivers/crypto/ccp/ccp-crypto-main.c b/drivers/crypto/ccp/ccp-crypto-main.c index 4d4e016d755b..bdec01ec608f 100644 --- a/drivers/crypto/ccp/ccp-crypto-main.c +++ b/drivers/crypto/ccp/ccp-crypto-main.c @@ -33,7 +33,6 @@ static unsigned int sha_disable; module_param(sha_disable, uint, 0444); MODULE_PARM_DESC(sha_disable, "Disable use of SHA - any non-zero value"); - /* List heads for the supported algorithms */ static LIST_HEAD(hash_algs); static LIST_HEAD(cipher_algs); @@ -48,6 +47,7 @@ struct ccp_crypto_queue { struct list_head *backlog; unsigned int cmd_count; }; + #define CCP_CRYPTO_MAX_QLEN 100 static struct ccp_crypto_queue req_queue; @@ -77,7 +77,6 @@ struct ccp_crypto_cpu { int err; }; - static inline bool ccp_crypto_success(int err) { if (err && (err != -EINPROGRESS) && (err != -EBUSY)) @@ -143,7 +142,7 @@ static void ccp_crypto_complete(void *data, int err) int ret; if (err == -EINPROGRESS) { - /* Only propogate the -EINPROGRESS if necessary */ + /* Only propagate the -EINPROGRESS if necessary */ if (crypto_cmd->ret == -EBUSY) { crypto_cmd->ret = -EINPROGRESS; req->complete(req, -EINPROGRESS); diff --git a/drivers/crypto/ccp/ccp-crypto-sha.c b/drivers/crypto/ccp/ccp-crypto-sha.c index 96531571f7cf..507b34e0cc19 100644 --- a/drivers/crypto/ccp/ccp-crypto-sha.c +++ b/drivers/crypto/ccp/ccp-crypto-sha.c @@ -23,7 +23,6 @@ #include "ccp-crypto.h" - static int ccp_sha_complete(struct crypto_async_request *async_req, int ret) { struct ahash_request *req = ahash_request_cast(async_req); @@ -37,11 +36,13 @@ static int ccp_sha_complete(struct crypto_async_request *async_req, int ret) if (rctx->hash_rem) { /* Save remaining data to buffer */ unsigned int offset = rctx->nbytes - rctx->hash_rem; + scatterwalk_map_and_copy(rctx->buf, rctx->src, offset, rctx->hash_rem, 0); rctx->buf_count = rctx->hash_rem; - } else + } else { rctx->buf_count = 0; + } /* Update result area if supplied */ if (req->result) @@ -227,8 +228,9 @@ static int ccp_sha_setkey(struct crypto_ahash *tfm, const u8 *key, } key_len = digest_size; - } else + } else { memcpy(ctx->u.sha.key, key, key_len); + } for (i = 0; i < block_size; i++) { ctx->u.sha.ipad[i] = ctx->u.sha.key[i] ^ 0x36; @@ -355,7 +357,7 @@ static int ccp_register_hmac_alg(struct list_head *head, ret = crypto_register_ahash(alg); if (ret) { pr_err("%s ahash algorithm registration error (%d)\n", - base->cra_name, ret); + base->cra_name, ret); kfree(ccp_alg); return ret; } @@ -410,7 +412,7 @@ static int ccp_register_sha_alg(struct list_head *head, ret = crypto_register_ahash(alg); if (ret) { pr_err("%s ahash algorithm registration error (%d)\n", - base->cra_name, ret); + base->cra_name, ret); kfree(ccp_alg); return ret; } diff --git a/drivers/crypto/ccp/ccp-crypto.h b/drivers/crypto/ccp/ccp-crypto.h index 9aa4ae184f7f..76a96f0f44c6 100644 --- a/drivers/crypto/ccp/ccp-crypto.h +++ b/drivers/crypto/ccp/ccp-crypto.h @@ -13,7 +13,6 @@ #ifndef __CCP_CRYPTO_H__ #define __CCP_CRYPTO_H__ - #include <linux/list.h> #include <linux/wait.h> #include <linux/pci.h> @@ -25,7 +24,6 @@ #include <crypto/hash.h> #include <crypto/sha.h> - #define CCP_CRA_PRIORITY 300 struct ccp_crypto_ablkcipher_alg { @@ -68,7 +66,6 @@ static inline struct ccp_crypto_ahash_alg * return container_of(ahash_alg, struct ccp_crypto_ahash_alg, alg); } - /***** AES related defines *****/ struct ccp_aes_ctx { /* Fallback cipher for XTS with unsupported unit sizes */ diff --git a/drivers/crypto/ccp/ccp-dev.c b/drivers/crypto/ccp/ccp-dev.c index ca29c120b85f..861bacc1bb94 100644 --- a/drivers/crypto/ccp/ccp-dev.c +++ b/drivers/crypto/ccp/ccp-dev.c @@ -37,7 +37,6 @@ struct ccp_tasklet_data { struct ccp_cmd *cmd; }; - static struct ccp_device *ccp_dev; static inline struct ccp_device *ccp_get_device(void) { @@ -296,11 +295,9 @@ struct ccp_device *ccp_alloc_struct(struct device *dev) { struct ccp_device *ccp; - ccp = kzalloc(sizeof(*ccp), GFP_KERNEL); - if (ccp == NULL) { - dev_err(dev, "unable to allocate device struct\n"); + ccp = devm_kzalloc(dev, sizeof(*ccp), GFP_KERNEL); + if (!ccp) return NULL; - } ccp->dev = dev; INIT_LIST_HEAD(&ccp->cmd); diff --git a/drivers/crypto/ccp/ccp-dev.h b/drivers/crypto/ccp/ccp-dev.h index 62ff35a6b9ec..6ff89031fb96 100644 --- a/drivers/crypto/ccp/ccp-dev.h +++ b/drivers/crypto/ccp/ccp-dev.h @@ -21,7 +21,7 @@ #include <linux/wait.h> #include <linux/dmapool.h> #include <linux/hw_random.h> - +#include <linux/bitops.h> #define MAX_DMAPOOL_NAME_LEN 32 @@ -33,7 +33,6 @@ #define CACHE_NONE 0x00 #define CACHE_WB_NO_ALLOC 0xb7 - /****** Register Mappings ******/ #define Q_MASK_REG 0x000 #define TRNG_OUT_REG 0x00c @@ -54,8 +53,8 @@ #define CMD_Q_CACHE_BASE 0x228 #define CMD_Q_CACHE_INC 0x20 -#define CMD_Q_ERROR(__qs) ((__qs) & 0x0000003f); -#define CMD_Q_DEPTH(__qs) (((__qs) >> 12) & 0x0000000f); +#define CMD_Q_ERROR(__qs) ((__qs) & 0x0000003f) +#define CMD_Q_DEPTH(__qs) (((__qs) >> 12) & 0x0000000f) /****** REQ0 Related Values ******/ #define REQ0_WAIT_FOR_WRITE 0x00000004 @@ -103,7 +102,6 @@ /****** REQ6 Related Values ******/ #define REQ6_MEMTYPE_SHIFT 16 - /****** Key Storage Block ******/ #define KSB_START 77 #define KSB_END 127 @@ -114,7 +112,7 @@ #define CCP_JOBID_MASK 0x0000003f #define CCP_DMAPOOL_MAX_SIZE 64 -#define CCP_DMAPOOL_ALIGN (1 << 5) +#define CCP_DMAPOOL_ALIGN BIT(5) #define CCP_REVERSE_BUF_SIZE 64 @@ -142,7 +140,6 @@ #define CCP_ECC_RESULT_OFFSET 60 #define CCP_ECC_RESULT_SUCCESS 0x0001 - struct ccp_device; struct ccp_cmd; @@ -261,7 +258,6 @@ struct ccp_device { unsigned int axcache; }; - int ccp_pci_init(void); void ccp_pci_exit(void); diff --git a/drivers/crypto/ccp/ccp-ops.c b/drivers/crypto/ccp/ccp-ops.c index 8729364261d7..71f2e3c89424 100644 --- a/drivers/crypto/ccp/ccp-ops.c +++ b/drivers/crypto/ccp/ccp-ops.c @@ -27,7 +27,6 @@ #include "ccp-dev.h" - enum ccp_memtype { CCP_MEMTYPE_SYSTEM = 0, CCP_MEMTYPE_KSB, @@ -515,7 +514,6 @@ static int ccp_init_sg_workarea(struct ccp_sg_workarea *wa, struct device *dev, if (!wa->dma_count) return -ENOMEM; - return 0; } @@ -763,8 +761,9 @@ static void ccp_prepare_data(struct ccp_data *src, struct ccp_data *dst, sg_dst_len = sg_dma_len(dst->sg_wa.sg) - dst->sg_wa.sg_used; sg_dst_len = min_t(u64, src->sg_wa.bytes_left, sg_dst_len); op_len = min(sg_src_len, sg_dst_len); - } else + } else { op_len = sg_src_len; + } /* The data operation length will be at least block_size in length * or the smaller of available sg room remaining for the source or @@ -1131,9 +1130,9 @@ static int ccp_run_aes_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) if (ret) goto e_ctx; - if (in_place) + if (in_place) { dst = src; - else { + } else { ret = ccp_init_data(&dst, cmd_q, aes->dst, aes->src_len, AES_BLOCK_SIZE, DMA_FROM_DEVICE); if (ret) @@ -1304,9 +1303,9 @@ static int ccp_run_xts_aes_cmd(struct ccp_cmd_queue *cmd_q, if (ret) goto e_ctx; - if (in_place) + if (in_place) { dst = src; - else { + } else { ret = ccp_init_data(&dst, cmd_q, xts->dst, xts->src_len, unit_size, DMA_FROM_DEVICE); if (ret) @@ -1451,8 +1450,9 @@ static int ccp_run_sha_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) goto e_ctx; } memcpy(ctx.address, init, CCP_SHA_CTXSIZE); - } else + } else { ccp_set_dm_area(&ctx, 0, sha->ctx, 0, sha->ctx_len); + } ret = ccp_copy_to_ksb(cmd_q, &ctx, op.jobid, op.ksb_ctx, CCP_PASSTHRU_BYTESWAP_256BIT); @@ -1732,9 +1732,9 @@ static int ccp_run_passthru_cmd(struct ccp_cmd_queue *cmd_q, if (ret) goto e_mask; - if (in_place) + if (in_place) { dst = src; - else { + } else { ret = ccp_init_data(&dst, cmd_q, pt->dst, pt->src_len, CCP_PASSTHRU_MASKSIZE, DMA_FROM_DEVICE); if (ret) @@ -1974,7 +1974,7 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) src.address += CCP_ECC_OPERAND_SIZE; /* Set the first point Z coordianate to 1 */ - *(src.address) = 0x01; + *src.address = 0x01; src.address += CCP_ECC_OPERAND_SIZE; if (ecc->function == CCP_ECC_FUNCTION_PADD_384BIT) { @@ -1989,7 +1989,7 @@ static int ccp_run_ecc_pm_cmd(struct ccp_cmd_queue *cmd_q, struct ccp_cmd *cmd) src.address += CCP_ECC_OPERAND_SIZE; /* Set the second point Z coordianate to 1 */ - *(src.address) = 0x01; + *src.address = 0x01; src.address += CCP_ECC_OPERAND_SIZE; } else { /* Copy the Domain "a" parameter */ diff --git a/drivers/crypto/ccp/ccp-pci.c b/drivers/crypto/ccp/ccp-pci.c index 7f89c946adfe..af190d4795a8 100644 --- a/drivers/crypto/ccp/ccp-pci.c +++ b/drivers/crypto/ccp/ccp-pci.c @@ -174,11 +174,10 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (!ccp) goto e_err; - ccp_pci = kzalloc(sizeof(*ccp_pci), GFP_KERNEL); - if (!ccp_pci) { - ret = -ENOMEM; - goto e_free1; - } + ccp_pci = devm_kzalloc(dev, sizeof(*ccp_pci), GFP_KERNEL); + if (!ccp_pci) + goto e_err; + ccp->dev_specific = ccp_pci; ccp->get_irq = ccp_get_irqs; ccp->free_irq = ccp_free_irqs; @@ -186,7 +185,7 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = pci_request_regions(pdev, "ccp"); if (ret) { dev_err(dev, "pci_request_regions failed (%d)\n", ret); - goto e_free2; + goto e_err; } ret = pci_enable_device(pdev); @@ -204,7 +203,7 @@ static int ccp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) ret = -EIO; ccp->io_map = pci_iomap(pdev, bar, 0); - if (ccp->io_map == NULL) { + if (!ccp->io_map) { dev_err(dev, "pci_iomap failed\n"); goto e_device; } @@ -239,12 +238,6 @@ e_device: e_regions: pci_release_regions(pdev); -e_free2: - kfree(ccp_pci); - -e_free1: - kfree(ccp); - e_err: dev_notice(dev, "initialization failed\n"); return ret; @@ -266,8 +259,6 @@ static void ccp_pci_remove(struct pci_dev *pdev) pci_release_regions(pdev); - kfree(ccp); - dev_notice(dev, "disabled\n"); } diff --git a/drivers/crypto/ccp/ccp-platform.c b/drivers/crypto/ccp/ccp-platform.c index 8c50bad25f7e..b1c20b2b5647 100644 --- a/drivers/crypto/ccp/ccp-platform.c +++ b/drivers/crypto/ccp/ccp-platform.c @@ -23,9 +23,15 @@ #include <linux/delay.h> #include <linux/ccp.h> #include <linux/of.h> +#include <linux/of_address.h> +#include <linux/acpi.h> #include "ccp-dev.h" +struct ccp_platform { + int use_acpi; + int coherent; +}; static int ccp_get_irq(struct ccp_device *ccp) { @@ -84,10 +90,64 @@ static struct resource *ccp_find_mmio_area(struct ccp_device *ccp) return NULL; } +#ifdef CONFIG_ACPI +static int ccp_acpi_support(struct ccp_device *ccp) +{ + struct ccp_platform *ccp_platform = ccp->dev_specific; + struct acpi_device *adev = ACPI_COMPANION(ccp->dev); + acpi_handle handle; + acpi_status status; + unsigned long long data; + int cca; + + /* Retrieve the device cache coherency value */ + handle = adev->handle; + do { + status = acpi_evaluate_integer(handle, "_CCA", NULL, &data); + if (!ACPI_FAILURE(status)) { + cca = data; + break; + } + } while (!ACPI_FAILURE(status)); + + if (ACPI_FAILURE(status)) { + dev_err(ccp->dev, "error obtaining acpi coherency value\n"); + return -EINVAL; + } + + ccp_platform->coherent = !!cca; + + return 0; +} +#else /* CONFIG_ACPI */ +static int ccp_acpi_support(struct ccp_device *ccp) +{ + return -EINVAL; +} +#endif + +#ifdef CONFIG_OF +static int ccp_of_support(struct ccp_device *ccp) +{ + struct ccp_platform *ccp_platform = ccp->dev_specific; + + ccp_platform->coherent = of_dma_is_coherent(ccp->dev->of_node); + + return 0; +} +#else +static int ccp_of_support(struct ccp_device *ccp) +{ + return -EINVAL; +} +#endif + static int ccp_platform_probe(struct platform_device *pdev) { struct ccp_device *ccp; + struct ccp_platform *ccp_platform; struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct resource *ior; int ret; @@ -96,24 +156,40 @@ static int ccp_platform_probe(struct platform_device *pdev) if (!ccp) goto e_err; - ccp->dev_specific = NULL; + ccp_platform = devm_kzalloc(dev, sizeof(*ccp_platform), GFP_KERNEL); + if (!ccp_platform) + goto e_err; + + ccp->dev_specific = ccp_platform; ccp->get_irq = ccp_get_irqs; ccp->free_irq = ccp_free_irqs; + ccp_platform->use_acpi = (!adev || acpi_disabled) ? 0 : 1; + ior = ccp_find_mmio_area(ccp); ccp->io_map = devm_ioremap_resource(dev, ior); if (IS_ERR(ccp->io_map)) { ret = PTR_ERR(ccp->io_map); - goto e_free; + goto e_err; } ccp->io_regs = ccp->io_map; if (!dev->dma_mask) dev->dma_mask = &dev->coherent_dma_mask; - *(dev->dma_mask) = DMA_BIT_MASK(48); - dev->coherent_dma_mask = DMA_BIT_MASK(48); + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret); + goto e_err; + } + + if (ccp_platform->use_acpi) + ret = ccp_acpi_support(ccp); + else + ret = ccp_of_support(ccp); + if (ret) + goto e_err; - if (of_property_read_bool(dev->of_node, "dma-coherent")) + if (ccp_platform->coherent) ccp->axcache = CACHE_WB_NO_ALLOC; else ccp->axcache = CACHE_NONE; @@ -122,15 +198,12 @@ static int ccp_platform_probe(struct platform_device *pdev) ret = ccp_init(ccp); if (ret) - goto e_free; + goto e_err; dev_notice(dev, "enabled\n"); return 0; -e_free: - kfree(ccp); - e_err: dev_notice(dev, "initialization failed\n"); return ret; @@ -143,8 +216,6 @@ static int ccp_platform_remove(struct platform_device *pdev) ccp_destroy(ccp); - kfree(ccp); - dev_notice(dev, "disabled\n"); return 0; @@ -200,15 +271,29 @@ static int ccp_platform_resume(struct platform_device *pdev) } #endif -static const struct of_device_id ccp_platform_ids[] = { +#ifdef CONFIG_ACPI +static const struct acpi_device_id ccp_acpi_match[] = { + { "AMDI0C00", 0 }, + { }, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id ccp_of_match[] = { { .compatible = "amd,ccp-seattle-v1a" }, { }, }; +#endif static struct platform_driver ccp_platform_driver = { .driver = { .name = "AMD Cryptographic Coprocessor", - .of_match_table = ccp_platform_ids, +#ifdef CONFIG_ACPI + .acpi_match_table = ccp_acpi_match, +#endif +#ifdef CONFIG_OF + .of_match_table = ccp_of_match, +#endif }, .probe = ccp_platform_probe, .remove = ccp_platform_remove, diff --git a/drivers/crypto/img-hash.c b/drivers/crypto/img-hash.c new file mode 100644 index 000000000000..ad47d0d61098 --- /dev/null +++ b/drivers/crypto/img-hash.c @@ -0,0 +1,1029 @@ +/* + * Copyright (c) 2014 Imagination Technologies + * Authors: Will Thomas, James Hartley + * + * 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. + * + * Interface structure taken from omap-sham driver + */ + +#include <linux/clk.h> +#include <linux/dmaengine.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/scatterlist.h> + +#include <crypto/internal/hash.h> +#include <crypto/md5.h> +#include <crypto/sha.h> + +#define CR_RESET 0 +#define CR_RESET_SET 1 +#define CR_RESET_UNSET 0 + +#define CR_MESSAGE_LENGTH_H 0x4 +#define CR_MESSAGE_LENGTH_L 0x8 + +#define CR_CONTROL 0xc +#define CR_CONTROL_BYTE_ORDER_3210 0 +#define CR_CONTROL_BYTE_ORDER_0123 1 +#define CR_CONTROL_BYTE_ORDER_2310 2 +#define CR_CONTROL_BYTE_ORDER_1032 3 +#define CR_CONTROL_BYTE_ORDER_SHIFT 8 +#define CR_CONTROL_ALGO_MD5 0 +#define CR_CONTROL_ALGO_SHA1 1 +#define CR_CONTROL_ALGO_SHA224 2 +#define CR_CONTROL_ALGO_SHA256 3 + +#define CR_INTSTAT 0x10 +#define CR_INTENAB 0x14 +#define CR_INTCLEAR 0x18 +#define CR_INT_RESULTS_AVAILABLE BIT(0) +#define CR_INT_NEW_RESULTS_SET BIT(1) +#define CR_INT_RESULT_READ_ERR BIT(2) +#define CR_INT_MESSAGE_WRITE_ERROR BIT(3) +#define CR_INT_STATUS BIT(8) + +#define CR_RESULT_QUEUE 0x1c +#define CR_RSD0 0x40 +#define CR_CORE_REV 0x50 +#define CR_CORE_DES1 0x60 +#define CR_CORE_DES2 0x70 + +#define DRIVER_FLAGS_BUSY BIT(0) +#define DRIVER_FLAGS_FINAL BIT(1) +#define DRIVER_FLAGS_DMA_ACTIVE BIT(2) +#define DRIVER_FLAGS_OUTPUT_READY BIT(3) +#define DRIVER_FLAGS_INIT BIT(4) +#define DRIVER_FLAGS_CPU BIT(5) +#define DRIVER_FLAGS_DMA_READY BIT(6) +#define DRIVER_FLAGS_ERROR BIT(7) +#define DRIVER_FLAGS_SG BIT(8) +#define DRIVER_FLAGS_SHA1 BIT(18) +#define DRIVER_FLAGS_SHA224 BIT(19) +#define DRIVER_FLAGS_SHA256 BIT(20) +#define DRIVER_FLAGS_MD5 BIT(21) + +#define IMG_HASH_QUEUE_LENGTH 20 +#define IMG_HASH_DMA_THRESHOLD 64 + +#ifdef __LITTLE_ENDIAN +#define IMG_HASH_BYTE_ORDER CR_CONTROL_BYTE_ORDER_3210 +#else +#define IMG_HASH_BYTE_ORDER CR_CONTROL_BYTE_ORDER_0123 +#endif + +struct img_hash_dev; + +struct img_hash_request_ctx { + struct img_hash_dev *hdev; + u8 digest[SHA256_DIGEST_SIZE] __aligned(sizeof(u32)); + unsigned long flags; + size_t digsize; + + dma_addr_t dma_addr; + size_t dma_ct; + + /* sg root */ + struct scatterlist *sgfirst; + /* walk state */ + struct scatterlist *sg; + size_t nents; + size_t offset; + unsigned int total; + size_t sent; + + unsigned long op; + + size_t bufcnt; + u8 buffer[0] __aligned(sizeof(u32)); + struct ahash_request fallback_req; +}; + +struct img_hash_ctx { + struct img_hash_dev *hdev; + unsigned long flags; + struct crypto_ahash *fallback; +}; + +struct img_hash_dev { + struct list_head list; + struct device *dev; + struct clk *hash_clk; + struct clk *sys_clk; + void __iomem *io_base; + + phys_addr_t bus_addr; + void __iomem *cpu_addr; + + spinlock_t lock; + int err; + struct tasklet_struct done_task; + struct tasklet_struct dma_task; + + unsigned long flags; + struct crypto_queue queue; + struct ahash_request *req; + + struct dma_chan *dma_lch; +}; + +struct img_hash_drv { + struct list_head dev_list; + spinlock_t lock; +}; + +static struct img_hash_drv img_hash = { + .dev_list = LIST_HEAD_INIT(img_hash.dev_list), + .lock = __SPIN_LOCK_UNLOCKED(img_hash.lock), +}; + +static inline u32 img_hash_read(struct img_hash_dev *hdev, u32 offset) +{ + return readl_relaxed(hdev->io_base + offset); +} + +static inline void img_hash_write(struct img_hash_dev *hdev, + u32 offset, u32 value) +{ + writel_relaxed(value, hdev->io_base + offset); +} + +static inline u32 img_hash_read_result_queue(struct img_hash_dev *hdev) +{ + return be32_to_cpu(img_hash_read(hdev, CR_RESULT_QUEUE)); +} + +static void img_hash_start(struct img_hash_dev *hdev, bool dma) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + u32 cr = IMG_HASH_BYTE_ORDER << CR_CONTROL_BYTE_ORDER_SHIFT; + + if (ctx->flags & DRIVER_FLAGS_MD5) + cr |= CR_CONTROL_ALGO_MD5; + else if (ctx->flags & DRIVER_FLAGS_SHA1) + cr |= CR_CONTROL_ALGO_SHA1; + else if (ctx->flags & DRIVER_FLAGS_SHA224) + cr |= CR_CONTROL_ALGO_SHA224; + else if (ctx->flags & DRIVER_FLAGS_SHA256) + cr |= CR_CONTROL_ALGO_SHA256; + dev_dbg(hdev->dev, "Starting hash process\n"); + img_hash_write(hdev, CR_CONTROL, cr); + + /* + * The hardware block requires two cycles between writing the control + * register and writing the first word of data in non DMA mode, to + * ensure the first data write is not grouped in burst with the control + * register write a read is issued to 'flush' the bus. + */ + if (!dma) + img_hash_read(hdev, CR_CONTROL); +} + +static int img_hash_xmit_cpu(struct img_hash_dev *hdev, const u8 *buf, + size_t length, int final) +{ + u32 count, len32; + const u32 *buffer = (const u32 *)buf; + + dev_dbg(hdev->dev, "xmit_cpu: length: %zu bytes\n", length); + + if (final) + hdev->flags |= DRIVER_FLAGS_FINAL; + + len32 = DIV_ROUND_UP(length, sizeof(u32)); + + for (count = 0; count < len32; count++) + writel_relaxed(buffer[count], hdev->cpu_addr); + + return -EINPROGRESS; +} + +static void img_hash_dma_callback(void *data) +{ + struct img_hash_dev *hdev = (struct img_hash_dev *)data; + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + + if (ctx->bufcnt) { + img_hash_xmit_cpu(hdev, ctx->buffer, ctx->bufcnt, 0); + ctx->bufcnt = 0; + } + if (ctx->sg) + tasklet_schedule(&hdev->dma_task); +} + +static int img_hash_xmit_dma(struct img_hash_dev *hdev, struct scatterlist *sg) +{ + struct dma_async_tx_descriptor *desc; + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + + ctx->dma_ct = dma_map_sg(hdev->dev, sg, 1, DMA_MEM_TO_DEV); + if (ctx->dma_ct == 0) { + dev_err(hdev->dev, "Invalid DMA sg\n"); + hdev->err = -EINVAL; + return -EINVAL; + } + + desc = dmaengine_prep_slave_sg(hdev->dma_lch, + sg, + ctx->dma_ct, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!desc) { + dev_err(hdev->dev, "Null DMA descriptor\n"); + hdev->err = -EINVAL; + dma_unmap_sg(hdev->dev, sg, 1, DMA_MEM_TO_DEV); + return -EINVAL; + } + desc->callback = img_hash_dma_callback; + desc->callback_param = hdev; + dmaengine_submit(desc); + dma_async_issue_pending(hdev->dma_lch); + + return 0; +} + +static int img_hash_write_via_cpu(struct img_hash_dev *hdev) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + + ctx->bufcnt = sg_copy_to_buffer(hdev->req->src, sg_nents(ctx->sg), + ctx->buffer, hdev->req->nbytes); + + ctx->total = hdev->req->nbytes; + ctx->bufcnt = 0; + + hdev->flags |= (DRIVER_FLAGS_CPU | DRIVER_FLAGS_FINAL); + + img_hash_start(hdev, false); + + return img_hash_xmit_cpu(hdev, ctx->buffer, ctx->total, 1); +} + +static int img_hash_finish(struct ahash_request *req) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(req); + + if (!req->result) + return -EINVAL; + + memcpy(req->result, ctx->digest, ctx->digsize); + + return 0; +} + +static void img_hash_copy_hash(struct ahash_request *req) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(req); + u32 *hash = (u32 *)ctx->digest; + int i; + + for (i = (ctx->digsize / sizeof(u32)) - 1; i >= 0; i--) + hash[i] = img_hash_read_result_queue(ctx->hdev); +} + +static void img_hash_finish_req(struct ahash_request *req, int err) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(req); + struct img_hash_dev *hdev = ctx->hdev; + + if (!err) { + img_hash_copy_hash(req); + if (DRIVER_FLAGS_FINAL & hdev->flags) + err = img_hash_finish(req); + } else { + dev_warn(hdev->dev, "Hash failed with error %d\n", err); + ctx->flags |= DRIVER_FLAGS_ERROR; + } + + hdev->flags &= ~(DRIVER_FLAGS_DMA_READY | DRIVER_FLAGS_OUTPUT_READY | + DRIVER_FLAGS_CPU | DRIVER_FLAGS_BUSY | DRIVER_FLAGS_FINAL); + + if (req->base.complete) + req->base.complete(&req->base, err); +} + +static int img_hash_write_via_dma(struct img_hash_dev *hdev) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + + img_hash_start(hdev, true); + + dev_dbg(hdev->dev, "xmit dma size: %d\n", ctx->total); + + if (!ctx->total) + hdev->flags |= DRIVER_FLAGS_FINAL; + + hdev->flags |= DRIVER_FLAGS_DMA_ACTIVE | DRIVER_FLAGS_FINAL; + + tasklet_schedule(&hdev->dma_task); + + return -EINPROGRESS; +} + +static int img_hash_dma_init(struct img_hash_dev *hdev) +{ + struct dma_slave_config dma_conf; + int err = -EINVAL; + + hdev->dma_lch = dma_request_slave_channel(hdev->dev, "tx"); + if (!hdev->dma_lch) { + dev_err(hdev->dev, "Couldn't aquire a slave DMA channel.\n"); + return -EBUSY; + } + dma_conf.direction = DMA_MEM_TO_DEV; + dma_conf.dst_addr = hdev->bus_addr; + dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dma_conf.dst_maxburst = 16; + dma_conf.device_fc = false; + + err = dmaengine_slave_config(hdev->dma_lch, &dma_conf); + if (err) { + dev_err(hdev->dev, "Couldn't configure DMA slave.\n"); + dma_release_channel(hdev->dma_lch); + return err; + } + + return 0; +} + +static void img_hash_dma_task(unsigned long d) +{ + struct img_hash_dev *hdev = (struct img_hash_dev *)d; + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + u8 *addr; + size_t nbytes, bleft, wsend, len, tbc; + struct scatterlist tsg; + + if (!ctx->sg) + return; + + addr = sg_virt(ctx->sg); + nbytes = ctx->sg->length - ctx->offset; + + /* + * The hash accelerator does not support a data valid mask. This means + * that if each dma (i.e. per page) is not a multiple of 4 bytes, the + * padding bytes in the last word written by that dma would erroneously + * be included in the hash. To avoid this we round down the transfer, + * and add the excess to the start of the next dma. It does not matter + * that the final dma may not be a multiple of 4 bytes as the hashing + * block is programmed to accept the correct number of bytes. + */ + + bleft = nbytes % 4; + wsend = (nbytes / 4); + + if (wsend) { + sg_init_one(&tsg, addr + ctx->offset, wsend * 4); + if (img_hash_xmit_dma(hdev, &tsg)) { + dev_err(hdev->dev, "DMA failed, falling back to CPU"); + ctx->flags |= DRIVER_FLAGS_CPU; + hdev->err = 0; + img_hash_xmit_cpu(hdev, addr + ctx->offset, + wsend * 4, 0); + ctx->sent += wsend * 4; + wsend = 0; + } else { + ctx->sent += wsend * 4; + } + } + + if (bleft) { + ctx->bufcnt = sg_pcopy_to_buffer(ctx->sgfirst, ctx->nents, + ctx->buffer, bleft, ctx->sent); + tbc = 0; + ctx->sg = sg_next(ctx->sg); + while (ctx->sg && (ctx->bufcnt < 4)) { + len = ctx->sg->length; + if (likely(len > (4 - ctx->bufcnt))) + len = 4 - ctx->bufcnt; + tbc = sg_pcopy_to_buffer(ctx->sgfirst, ctx->nents, + ctx->buffer + ctx->bufcnt, len, + ctx->sent + ctx->bufcnt); + ctx->bufcnt += tbc; + if (tbc >= ctx->sg->length) { + ctx->sg = sg_next(ctx->sg); + tbc = 0; + } + } + + ctx->sent += ctx->bufcnt; + ctx->offset = tbc; + + if (!wsend) + img_hash_dma_callback(hdev); + } else { + ctx->offset = 0; + ctx->sg = sg_next(ctx->sg); + } +} + +static int img_hash_write_via_dma_stop(struct img_hash_dev *hdev) +{ + struct img_hash_request_ctx *ctx = ahash_request_ctx(hdev->req); + + if (ctx->flags & DRIVER_FLAGS_SG) + dma_unmap_sg(hdev->dev, ctx->sg, ctx->dma_ct, DMA_TO_DEVICE); + + return 0; +} + +static int img_hash_process_data(struct img_hash_dev *hdev) +{ + struct ahash_request *req = hdev->req; + struct img_hash_request_ctx *ctx = ahash_request_ctx(req); + int err = 0; + + ctx->bufcnt = 0; + + if (req->nbytes >= IMG_HASH_DMA_THRESHOLD) { + dev_dbg(hdev->dev, "process data request(%d bytes) using DMA\n", + req->nbytes); + err = img_hash_write_via_dma(hdev); + } else { + dev_dbg(hdev->dev, "process data request(%d bytes) using CPU\n", + req->nbytes); + err = img_hash_write_via_cpu(hdev); + } + return err; +} + +static int img_hash_hw_init(struct img_hash_dev *hdev) +{ + unsigned long long nbits; + u32 u, l; + + img_hash_write(hdev, CR_RESET, CR_RESET_SET); + img_hash_write(hdev, CR_RESET, CR_RESET_UNSET); + img_hash_write(hdev, CR_INTENAB, CR_INT_NEW_RESULTS_SET); + + nbits = (u64)hdev->req->nbytes << 3; + u = nbits >> 32; + l = nbits; + img_hash_write(hdev, CR_MESSAGE_LENGTH_H, u); + img_hash_write(hdev, CR_MESSAGE_LENGTH_L, l); + + if (!(DRIVER_FLAGS_INIT & hdev->flags)) { + hdev->flags |= DRIVER_FLAGS_INIT; + hdev->err = 0; + } + dev_dbg(hdev->dev, "hw initialized, nbits: %llx\n", nbits); + return 0; +} + +static int img_hash_init(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct img_hash_request_ctx *rctx = ahash_request_ctx(req); + struct img_hash_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback); + rctx->fallback_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + + return crypto_ahash_init(&rctx->fallback_req); +} + +static int img_hash_handle_queue(struct img_hash_dev *hdev, + struct ahash_request *req) +{ + struct crypto_async_request *async_req, *backlog; + struct img_hash_request_ctx *ctx; + unsigned long flags; + int err = 0, res = 0; + + spin_lock_irqsave(&hdev->lock, flags); + + if (req) + res = ahash_enqueue_request(&hdev->queue, req); + + if (DRIVER_FLAGS_BUSY & hdev->flags) { + spin_unlock_irqrestore(&hdev->lock, flags); + return res; + } + + backlog = crypto_get_backlog(&hdev->queue); + async_req = crypto_dequeue_request(&hdev->queue); + if (async_req) + hdev->flags |= DRIVER_FLAGS_BUSY; + + spin_unlock_irqrestore(&hdev->lock, flags); + + if (!async_req) + return res; + + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + + req = ahash_request_cast(async_req); + hdev->req = req; + + ctx = ahash_request_ctx(req); + + dev_info(hdev->dev, "processing req, op: %lu, bytes: %d\n", + ctx->op, req->nbytes); + + err = img_hash_hw_init(hdev); + + if (!err) + err = img_hash_process_data(hdev); + + if (err != -EINPROGRESS) { + /* done_task will not finish so do it here */ + img_hash_finish_req(req, err); + } + return res; +} + +static int img_hash_update(struct ahash_request *req) +{ + struct img_hash_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct img_hash_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback); + rctx->fallback_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->fallback_req.nbytes = req->nbytes; + rctx->fallback_req.src = req->src; + + return crypto_ahash_update(&rctx->fallback_req); +} + +static int img_hash_final(struct ahash_request *req) +{ + struct img_hash_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct img_hash_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback); + rctx->fallback_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->fallback_req.result = req->result; + + return crypto_ahash_final(&rctx->fallback_req); +} + +static int img_hash_finup(struct ahash_request *req) +{ + struct img_hash_request_ctx *rctx = ahash_request_ctx(req); + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct img_hash_ctx *ctx = crypto_ahash_ctx(tfm); + + ahash_request_set_tfm(&rctx->fallback_req, ctx->fallback); + rctx->fallback_req.base.flags = req->base.flags + & CRYPTO_TFM_REQ_MAY_SLEEP; + rctx->fallback_req.nbytes = req->nbytes; + rctx->fallback_req.src = req->src; + rctx->fallback_req.result = req->result; + + return crypto_ahash_finup(&rctx->fallback_req); +} + +static int img_hash_digest(struct ahash_request *req) +{ + struct crypto_ahash *tfm = crypto_ahash_reqtfm(req); + struct img_hash_ctx *tctx = crypto_ahash_ctx(tfm); + struct img_hash_request_ctx *ctx = ahash_request_ctx(req); + struct img_hash_dev *hdev = NULL; + struct img_hash_dev *tmp; + int err; + + spin_lock(&img_hash.lock); + if (!tctx->hdev) { + list_for_each_entry(tmp, &img_hash.dev_list, list) { + hdev = tmp; + break; + } + tctx->hdev = hdev; + + } else { + hdev = tctx->hdev; + } + + spin_unlock(&img_hash.lock); + ctx->hdev = hdev; + ctx->flags = 0; + ctx->digsize = crypto_ahash_digestsize(tfm); + + switch (ctx->digsize) { + case SHA1_DIGEST_SIZE: + ctx->flags |= DRIVER_FLAGS_SHA1; + break; + case SHA256_DIGEST_SIZE: + ctx->flags |= DRIVER_FLAGS_SHA256; + break; + case SHA224_DIGEST_SIZE: + ctx->flags |= DRIVER_FLAGS_SHA224; + break; + case MD5_DIGEST_SIZE: + ctx->flags |= DRIVER_FLAGS_MD5; + break; + default: + return -EINVAL; + } + + ctx->bufcnt = 0; + ctx->offset = 0; + ctx->sent = 0; + ctx->total = req->nbytes; + ctx->sg = req->src; + ctx->sgfirst = req->src; + ctx->nents = sg_nents(ctx->sg); + + err = img_hash_handle_queue(tctx->hdev, req); + + return err; +} + +static int img_hash_cra_init(struct crypto_tfm *tfm) +{ + struct img_hash_ctx *ctx = crypto_tfm_ctx(tfm); + const char *alg_name = crypto_tfm_alg_name(tfm); + int err = -ENOMEM; + + ctx->fallback = crypto_alloc_ahash(alg_name, 0, + CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(ctx->fallback)) { + pr_err("img_hash: Could not load fallback driver.\n"); + err = PTR_ERR(ctx->fallback); + goto err; + } + crypto_ahash_set_reqsize(__crypto_ahash_cast(tfm), + sizeof(struct img_hash_request_ctx) + + IMG_HASH_DMA_THRESHOLD); + + return 0; + +err: + return err; +} + +static void img_hash_cra_exit(struct crypto_tfm *tfm) +{ + struct img_hash_ctx *tctx = crypto_tfm_ctx(tfm); + + crypto_free_ahash(tctx->fallback); +} + +static irqreturn_t img_irq_handler(int irq, void *dev_id) +{ + struct img_hash_dev *hdev = dev_id; + u32 reg; + + reg = img_hash_read(hdev, CR_INTSTAT); + img_hash_write(hdev, CR_INTCLEAR, reg); + + if (reg & CR_INT_NEW_RESULTS_SET) { + dev_dbg(hdev->dev, "IRQ CR_INT_NEW_RESULTS_SET\n"); + if (DRIVER_FLAGS_BUSY & hdev->flags) { + hdev->flags |= DRIVER_FLAGS_OUTPUT_READY; + if (!(DRIVER_FLAGS_CPU & hdev->flags)) + hdev->flags |= DRIVER_FLAGS_DMA_READY; + tasklet_schedule(&hdev->done_task); + } else { + dev_warn(hdev->dev, + "HASH interrupt when no active requests.\n"); + } + } else if (reg & CR_INT_RESULTS_AVAILABLE) { + dev_warn(hdev->dev, + "IRQ triggered before the hash had completed\n"); + } else if (reg & CR_INT_RESULT_READ_ERR) { + dev_warn(hdev->dev, + "Attempt to read from an empty result queue\n"); + } else if (reg & CR_INT_MESSAGE_WRITE_ERROR) { + dev_warn(hdev->dev, + "Data written before the hardware was configured\n"); + } + return IRQ_HANDLED; +} + +static struct ahash_alg img_algs[] = { + { + .init = img_hash_init, + .update = img_hash_update, + .final = img_hash_final, + .finup = img_hash_finup, + .digest = img_hash_digest, + .halg = { + .digestsize = MD5_DIGEST_SIZE, + .base = { + .cra_name = "md5", + .cra_driver_name = "img-md5", + .cra_priority = 300, + .cra_flags = + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = MD5_HMAC_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct img_hash_ctx), + .cra_init = img_hash_cra_init, + .cra_exit = img_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } + }, + { + .init = img_hash_init, + .update = img_hash_update, + .final = img_hash_final, + .finup = img_hash_finup, + .digest = img_hash_digest, + .halg = { + .digestsize = SHA1_DIGEST_SIZE, + .base = { + .cra_name = "sha1", + .cra_driver_name = "img-sha1", + .cra_priority = 300, + .cra_flags = + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA1_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct img_hash_ctx), + .cra_init = img_hash_cra_init, + .cra_exit = img_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } + }, + { + .init = img_hash_init, + .update = img_hash_update, + .final = img_hash_final, + .finup = img_hash_finup, + .digest = img_hash_digest, + .halg = { + .digestsize = SHA224_DIGEST_SIZE, + .base = { + .cra_name = "sha224", + .cra_driver_name = "img-sha224", + .cra_priority = 300, + .cra_flags = + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA224_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct img_hash_ctx), + .cra_init = img_hash_cra_init, + .cra_exit = img_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } + }, + { + .init = img_hash_init, + .update = img_hash_update, + .final = img_hash_final, + .finup = img_hash_finup, + .digest = img_hash_digest, + .halg = { + .digestsize = SHA256_DIGEST_SIZE, + .base = { + .cra_name = "sha256", + .cra_driver_name = "img-sha256", + .cra_priority = 300, + .cra_flags = + CRYPTO_ALG_ASYNC | + CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = SHA256_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct img_hash_ctx), + .cra_init = img_hash_cra_init, + .cra_exit = img_hash_cra_exit, + .cra_module = THIS_MODULE, + } + } + } +}; + +static int img_register_algs(struct img_hash_dev *hdev) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(img_algs); i++) { + err = crypto_register_ahash(&img_algs[i]); + if (err) + goto err_reg; + } + return 0; + +err_reg: + for (; i--; ) + crypto_unregister_ahash(&img_algs[i]); + + return err; +} + +static int img_unregister_algs(struct img_hash_dev *hdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(img_algs); i++) + crypto_unregister_ahash(&img_algs[i]); + return 0; +} + +static void img_hash_done_task(unsigned long data) +{ + struct img_hash_dev *hdev = (struct img_hash_dev *)data; + int err = 0; + + if (hdev->err == -EINVAL) { + err = hdev->err; + goto finish; + } + + if (!(DRIVER_FLAGS_BUSY & hdev->flags)) { + img_hash_handle_queue(hdev, NULL); + return; + } + + if (DRIVER_FLAGS_CPU & hdev->flags) { + if (DRIVER_FLAGS_OUTPUT_READY & hdev->flags) { + hdev->flags &= ~DRIVER_FLAGS_OUTPUT_READY; + goto finish; + } + } else if (DRIVER_FLAGS_DMA_READY & hdev->flags) { + if (DRIVER_FLAGS_DMA_ACTIVE & hdev->flags) { + hdev->flags &= ~DRIVER_FLAGS_DMA_ACTIVE; + img_hash_write_via_dma_stop(hdev); + if (hdev->err) { + err = hdev->err; + goto finish; + } + } + if (DRIVER_FLAGS_OUTPUT_READY & hdev->flags) { + hdev->flags &= ~(DRIVER_FLAGS_DMA_READY | + DRIVER_FLAGS_OUTPUT_READY); + goto finish; + } + } + return; + +finish: + img_hash_finish_req(hdev->req, err); +} + +static const struct of_device_id img_hash_match[] = { + { .compatible = "img,hash-accelerator" }, + {} +}; +MODULE_DEVICE_TABLE(of, img_hash_match); + +static int img_hash_probe(struct platform_device *pdev) +{ + struct img_hash_dev *hdev; + struct device *dev = &pdev->dev; + struct resource *hash_res; + int irq; + int err; + + hdev = devm_kzalloc(dev, sizeof(*hdev), GFP_KERNEL); + if (hdev == NULL) + return -ENOMEM; + + spin_lock_init(&hdev->lock); + + hdev->dev = dev; + + platform_set_drvdata(pdev, hdev); + + INIT_LIST_HEAD(&hdev->list); + + tasklet_init(&hdev->done_task, img_hash_done_task, (unsigned long)hdev); + tasklet_init(&hdev->dma_task, img_hash_dma_task, (unsigned long)hdev); + + crypto_init_queue(&hdev->queue, IMG_HASH_QUEUE_LENGTH); + + /* Register bank */ + hash_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + hdev->io_base = devm_ioremap_resource(dev, hash_res); + if (IS_ERR(hdev->io_base)) { + err = PTR_ERR(hdev->io_base); + dev_err(dev, "can't ioremap, returned %d\n", err); + + goto res_err; + } + + /* Write port (DMA or CPU) */ + hash_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + hdev->cpu_addr = devm_ioremap_resource(dev, hash_res); + if (IS_ERR(hdev->cpu_addr)) { + dev_err(dev, "can't ioremap write port\n"); + err = PTR_ERR(hdev->cpu_addr); + goto res_err; + } + hdev->bus_addr = hash_res->start; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(dev, "no IRQ resource info\n"); + err = irq; + goto res_err; + } + + err = devm_request_irq(dev, irq, img_irq_handler, 0, + dev_name(dev), hdev); + if (err) { + dev_err(dev, "unable to request irq\n"); + goto res_err; + } + dev_dbg(dev, "using IRQ channel %d\n", irq); + + hdev->hash_clk = devm_clk_get(&pdev->dev, "hash"); + if (IS_ERR(hdev->hash_clk)) { + dev_err(dev, "clock initialization failed.\n"); + err = PTR_ERR(hdev->hash_clk); + goto res_err; + } + + hdev->sys_clk = devm_clk_get(&pdev->dev, "sys"); + if (IS_ERR(hdev->sys_clk)) { + dev_err(dev, "clock initialization failed.\n"); + err = PTR_ERR(hdev->sys_clk); + goto res_err; + } + + err = clk_prepare_enable(hdev->hash_clk); + if (err) + goto res_err; + + err = clk_prepare_enable(hdev->sys_clk); + if (err) + goto clk_err; + + err = img_hash_dma_init(hdev); + if (err) + goto dma_err; + + dev_dbg(dev, "using %s for DMA transfers\n", + dma_chan_name(hdev->dma_lch)); + + spin_lock(&img_hash.lock); + list_add_tail(&hdev->list, &img_hash.dev_list); + spin_unlock(&img_hash.lock); + + err = img_register_algs(hdev); + if (err) + goto err_algs; + dev_dbg(dev, "Img MD5/SHA1/SHA224/SHA256 Hardware accelerator initialized\n"); + + return 0; + +err_algs: + spin_lock(&img_hash.lock); + list_del(&hdev->list); + spin_unlock(&img_hash.lock); + dma_release_channel(hdev->dma_lch); +dma_err: + clk_disable_unprepare(hdev->sys_clk); +clk_err: + clk_disable_unprepare(hdev->hash_clk); +res_err: + tasklet_kill(&hdev->done_task); + tasklet_kill(&hdev->dma_task); + + return err; +} + +static int img_hash_remove(struct platform_device *pdev) +{ + static struct img_hash_dev *hdev; + + hdev = platform_get_drvdata(pdev); + spin_lock(&img_hash.lock); + list_del(&hdev->list); + spin_unlock(&img_hash.lock); + + img_unregister_algs(hdev); + + tasklet_kill(&hdev->done_task); + tasklet_kill(&hdev->dma_task); + + dma_release_channel(hdev->dma_lch); + + clk_disable_unprepare(hdev->hash_clk); + clk_disable_unprepare(hdev->sys_clk); + + return 0; +} + +static struct platform_driver img_hash_driver = { + .probe = img_hash_probe, + .remove = img_hash_remove, + .driver = { + .name = "img-hash-accelerator", + .of_match_table = of_match_ptr(img_hash_match), + } +}; +module_platform_driver(img_hash_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Imgtec SHA1/224/256 & MD5 hw accelerator driver"); +MODULE_AUTHOR("Will Thomas."); +MODULE_AUTHOR("James Hartley <james.hartley@imgtec.com>"); diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c index 829d6394fb33..59ed54e464a9 100644 --- a/drivers/crypto/mxs-dcp.c +++ b/drivers/crypto/mxs-dcp.c @@ -153,7 +153,7 @@ static int mxs_dcp_start_dma(struct dcp_async_ctx *actx) struct dcp *sdcp = global_sdcp; const int chan = actx->chan; uint32_t stat; - int ret; + unsigned long ret; struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan]; dma_addr_t desc_phys = dma_map_single(sdcp->dev, desc, sizeof(*desc), diff --git a/drivers/crypto/omap-aes.c b/drivers/crypto/omap-aes.c index 42f95a4326b0..9a28b7e07c71 100644 --- a/drivers/crypto/omap-aes.c +++ b/drivers/crypto/omap-aes.c @@ -554,15 +554,23 @@ static int omap_aes_crypt_dma_stop(struct omap_aes_dev *dd) return err; } -static int omap_aes_check_aligned(struct scatterlist *sg) +static int omap_aes_check_aligned(struct scatterlist *sg, int total) { + int len = 0; + while (sg) { if (!IS_ALIGNED(sg->offset, 4)) return -1; if (!IS_ALIGNED(sg->length, AES_BLOCK_SIZE)) return -1; + + len += sg->length; sg = sg_next(sg); } + + if (len != total) + return -1; + return 0; } @@ -633,8 +641,8 @@ static int omap_aes_handle_queue(struct omap_aes_dev *dd, dd->in_sg = req->src; dd->out_sg = req->dst; - if (omap_aes_check_aligned(dd->in_sg) || - omap_aes_check_aligned(dd->out_sg)) { + if (omap_aes_check_aligned(dd->in_sg, dd->total) || + omap_aes_check_aligned(dd->out_sg, dd->total)) { if (omap_aes_copy_sgs(dd)) pr_err("Failed to copy SGs for unaligned cases\n"); dd->sgs_copied = 1; diff --git a/drivers/crypto/omap-sham.c b/drivers/crypto/omap-sham.c index 3c76696ee578..4d63e0d4da9a 100644 --- a/drivers/crypto/omap-sham.c +++ b/drivers/crypto/omap-sham.c @@ -640,6 +640,7 @@ static size_t omap_sham_append_sg(struct omap_sham_reqctx *ctx) while (ctx->sg) { vaddr = kmap_atomic(sg_page(ctx->sg)); + vaddr += ctx->sg->offset; count = omap_sham_append_buffer(ctx, vaddr + ctx->offset, @@ -1945,6 +1946,7 @@ static int omap_sham_probe(struct platform_device *pdev) dd->flags |= dd->pdata->flags; pm_runtime_enable(dev); + pm_runtime_irq_safe(dev); pm_runtime_get_sync(dev); rev = omap_sham_read(dd, SHA_REG_REV(dd)); pm_runtime_put_sync(&pdev->dev); diff --git a/drivers/crypto/qat/qat_common/adf_accel_devices.h b/drivers/crypto/qat/qat_common/adf_accel_devices.h index 19c0efa29ab3..f22ce7169fa5 100644 --- a/drivers/crypto/qat/qat_common/adf_accel_devices.h +++ b/drivers/crypto/qat/qat_common/adf_accel_devices.h @@ -52,7 +52,6 @@ #include <linux/io.h> #include "adf_cfg_common.h" -#define PCI_VENDOR_ID_INTEL 0x8086 #define ADF_DH895XCC_DEVICE_NAME "dh895xcc" #define ADF_DH895XCC_PCI_DEVICE_ID 0x435 #define ADF_PCI_MAX_BARS 3 diff --git a/drivers/crypto/qat/qat_common/adf_accel_engine.c b/drivers/crypto/qat/qat_common/adf_accel_engine.c index c77453b900a3..7f8b66c915ed 100644 --- a/drivers/crypto/qat/qat_common/adf_accel_engine.c +++ b/drivers/crypto/qat/qat_common/adf_accel_engine.c @@ -60,36 +60,40 @@ int adf_ae_fw_load(struct adf_accel_dev *accel_dev) if (request_firmware(&loader_data->uof_fw, hw_device->fw_name, &accel_dev->accel_pci_dev.pci_dev->dev)) { - pr_err("QAT: Failed to load firmware %s\n", hw_device->fw_name); + dev_err(&GET_DEV(accel_dev), "Failed to load firmware %s\n", + hw_device->fw_name); return -EFAULT; } uof_size = loader_data->uof_fw->size; uof_addr = (void *)loader_data->uof_fw->data; if (qat_uclo_map_uof_obj(loader_data->fw_loader, uof_addr, uof_size)) { - pr_err("QAT: Failed to map UOF\n"); + dev_err(&GET_DEV(accel_dev), "Failed to map UOF\n"); goto out_err; } if (qat_uclo_wr_all_uimage(loader_data->fw_loader)) { - pr_err("QAT: Failed to map UOF\n"); + dev_err(&GET_DEV(accel_dev), "Failed to map UOF\n"); goto out_err; } return 0; out_err: - release_firmware(loader_data->uof_fw); + adf_ae_fw_release(accel_dev); return -EFAULT; } -int adf_ae_fw_release(struct adf_accel_dev *accel_dev) +void adf_ae_fw_release(struct adf_accel_dev *accel_dev) { struct adf_fw_loader_data *loader_data = accel_dev->fw_loader; - release_firmware(loader_data->uof_fw); qat_uclo_del_uof_obj(loader_data->fw_loader); qat_hal_deinit(loader_data->fw_loader); + + if (loader_data->uof_fw) + release_firmware(loader_data->uof_fw); + + loader_data->uof_fw = NULL; loader_data->fw_loader = NULL; - return 0; } int adf_ae_start(struct adf_accel_dev *accel_dev) @@ -104,8 +108,9 @@ int adf_ae_start(struct adf_accel_dev *accel_dev) ae_ctr++; } } - pr_info("QAT: qat_dev%d started %d acceleration engines\n", - accel_dev->accel_id, ae_ctr); + dev_info(&GET_DEV(accel_dev), + "qat_dev%d started %d acceleration engines\n", + accel_dev->accel_id, ae_ctr); return 0; } @@ -121,8 +126,9 @@ int adf_ae_stop(struct adf_accel_dev *accel_dev) ae_ctr++; } } - pr_info("QAT: qat_dev%d stopped %d acceleration engines\n", - accel_dev->accel_id, ae_ctr); + dev_info(&GET_DEV(accel_dev), + "qat_dev%d stopped %d acceleration engines\n", + accel_dev->accel_id, ae_ctr); return 0; } @@ -147,12 +153,12 @@ int adf_ae_init(struct adf_accel_dev *accel_dev) accel_dev->fw_loader = loader_data; if (qat_hal_init(accel_dev)) { - pr_err("QAT: Failed to init the AEs\n"); + dev_err(&GET_DEV(accel_dev), "Failed to init the AEs\n"); kfree(loader_data); return -EFAULT; } if (adf_ae_reset(accel_dev, 0)) { - pr_err("QAT: Failed to reset the AEs\n"); + dev_err(&GET_DEV(accel_dev), "Failed to reset the AEs\n"); qat_hal_deinit(loader_data->fw_loader); kfree(loader_data); return -EFAULT; @@ -162,6 +168,9 @@ int adf_ae_init(struct adf_accel_dev *accel_dev) int adf_ae_shutdown(struct adf_accel_dev *accel_dev) { + struct adf_fw_loader_data *loader_data = accel_dev->fw_loader; + + qat_hal_deinit(loader_data->fw_loader); kfree(accel_dev->fw_loader); accel_dev->fw_loader = NULL; return 0; diff --git a/drivers/crypto/qat/qat_common/adf_aer.c b/drivers/crypto/qat/qat_common/adf_aer.c index fa1fef824de2..2dbc733b8ab2 100644 --- a/drivers/crypto/qat/qat_common/adf_aer.c +++ b/drivers/crypto/qat/qat_common/adf_aer.c @@ -60,14 +60,14 @@ static pci_ers_result_t adf_error_detected(struct pci_dev *pdev, { struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); - pr_info("QAT: Acceleration driver hardware error detected.\n"); + dev_info(&pdev->dev, "Acceleration driver hardware error detected.\n"); if (!accel_dev) { - pr_err("QAT: Can't find acceleration device\n"); + dev_err(&pdev->dev, "Can't find acceleration device\n"); return PCI_ERS_RESULT_DISCONNECT; } if (state == pci_channel_io_perm_failure) { - pr_err("QAT: Can't recover from device error\n"); + dev_err(&pdev->dev, "Can't recover from device error\n"); return PCI_ERS_RESULT_DISCONNECT; } @@ -88,10 +88,12 @@ static void adf_dev_restore(struct adf_accel_dev *accel_dev) struct pci_dev *parent = pdev->bus->self; uint16_t bridge_ctl = 0; - pr_info("QAT: Resetting device qat_dev%d\n", accel_dev->accel_id); + dev_info(&GET_DEV(accel_dev), "Resetting device qat_dev%d\n", + accel_dev->accel_id); if (!pci_wait_for_pending_transaction(pdev)) - pr_info("QAT: Transaction still in progress. Proceeding\n"); + dev_info(&GET_DEV(accel_dev), + "Transaction still in progress. Proceeding\n"); pci_read_config_word(parent, PCI_BRIDGE_CONTROL, &bridge_ctl); bridge_ctl |= PCI_BRIDGE_CTL_BUS_RESET; @@ -158,7 +160,8 @@ static int adf_dev_aer_schedule_reset(struct adf_accel_dev *accel_dev, unsigned long timeout = wait_for_completion_timeout( &reset_data->compl, wait_jiffies); if (!timeout) { - pr_err("QAT: Reset device timeout expired\n"); + dev_err(&GET_DEV(accel_dev), + "Reset device timeout expired\n"); ret = -EFAULT; } kfree(reset_data); @@ -184,8 +187,8 @@ static pci_ers_result_t adf_slot_reset(struct pci_dev *pdev) static void adf_resume(struct pci_dev *pdev) { - pr_info("QAT: Acceleration driver reset completed\n"); - pr_info("QAT: Device is up and runnig\n"); + dev_info(&pdev->dev, "Acceleration driver reset completed\n"); + dev_info(&pdev->dev, "Device is up and runnig\n"); } static struct pci_error_handlers adf_err_handler = { @@ -236,7 +239,7 @@ EXPORT_SYMBOL_GPL(adf_disable_aer); int adf_init_aer(void) { device_reset_wq = create_workqueue("qat_device_reset_wq"); - return (device_reset_wq == NULL) ? -EFAULT : 0; + return !device_reset_wq ? -EFAULT : 0; } void adf_exit_aer(void) diff --git a/drivers/crypto/qat/qat_common/adf_cfg.c b/drivers/crypto/qat/qat_common/adf_cfg.c index de16da9070a5..ab65bc274561 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg.c +++ b/drivers/crypto/qat/qat_common/adf_cfg.c @@ -142,7 +142,8 @@ int adf_cfg_dev_add(struct adf_accel_dev *accel_dev) dev_cfg_data, &qat_dev_cfg_fops); if (!dev_cfg_data->debug) { - pr_err("QAT: Failed to create qat cfg debugfs entry.\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to create qat cfg debugfs entry.\n"); kfree(dev_cfg_data); accel_dev->cfg = NULL; return -EFAULT; @@ -305,7 +306,7 @@ int adf_cfg_add_key_value_param(struct adf_accel_dev *accel_dev, snprintf(key_val->val, ADF_CFG_MAX_VAL_LEN_IN_BYTES, "0x%lx", (unsigned long)val); } else { - pr_err("QAT: Unknown type given.\n"); + dev_err(&GET_DEV(accel_dev), "Unknown type given.\n"); kfree(key_val); return -1; } diff --git a/drivers/crypto/qat/qat_common/adf_cfg_strings.h b/drivers/crypto/qat/qat_common/adf_cfg_strings.h index c7ac758ebc90..13575111382c 100644 --- a/drivers/crypto/qat/qat_common/adf_cfg_strings.h +++ b/drivers/crypto/qat/qat_common/adf_cfg_strings.h @@ -59,7 +59,7 @@ #define ADF_RING_SYM_TX "RingSymTx" #define ADF_RING_RND_TX "RingNrbgTx" #define ADF_RING_ASYM_RX "RingAsymRx" -#define ADF_RING_SYM_RX "RinSymRx" +#define ADF_RING_SYM_RX "RingSymRx" #define ADF_RING_RND_RX "RingNrbgRx" #define ADF_RING_DC_TX "RingTx" #define ADF_RING_DC_RX "RingRx" @@ -69,15 +69,15 @@ #define ADF_DC "Dc" #define ADF_ETRMGR_COALESCING_ENABLED "InterruptCoalescingEnabled" #define ADF_ETRMGR_COALESCING_ENABLED_FORMAT \ - ADF_ETRMGR_BANK"%d"ADF_ETRMGR_COALESCING_ENABLED + ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCING_ENABLED #define ADF_ETRMGR_COALESCE_TIMER "InterruptCoalescingTimerNs" #define ADF_ETRMGR_COALESCE_TIMER_FORMAT \ - ADF_ETRMGR_BANK"%d"ADF_ETRMGR_COALESCE_TIMER + ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCE_TIMER #define ADF_ETRMGR_COALESCING_MSG_ENABLED "InterruptCoalescingNumResponses" #define ADF_ETRMGR_COALESCING_MSG_ENABLED_FORMAT \ - ADF_ETRMGR_BANK"%d"ADF_ETRMGR_COALESCING_MSG_ENABLED + ADF_ETRMGR_BANK "%d" ADF_ETRMGR_COALESCING_MSG_ENABLED #define ADF_ETRMGR_CORE_AFFINITY "CoreAffinity" #define ADF_ETRMGR_CORE_AFFINITY_FORMAT \ - ADF_ETRMGR_BANK"%d"ADF_ETRMGR_CORE_AFFINITY + ADF_ETRMGR_BANK "%d" ADF_ETRMGR_CORE_AFFINITY #define ADF_ACCEL_STR "Accelerator%d" #endif diff --git a/drivers/crypto/qat/qat_common/adf_common_drv.h b/drivers/crypto/qat/qat_common/adf_common_drv.h index a62e485c8786..0666ee6a3360 100644 --- a/drivers/crypto/qat/qat_common/adf_common_drv.h +++ b/drivers/crypto/qat/qat_common/adf_common_drv.h @@ -115,7 +115,7 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev); int adf_ae_init(struct adf_accel_dev *accel_dev); int adf_ae_shutdown(struct adf_accel_dev *accel_dev); int adf_ae_fw_load(struct adf_accel_dev *accel_dev); -int adf_ae_fw_release(struct adf_accel_dev *accel_dev); +void adf_ae_fw_release(struct adf_accel_dev *accel_dev); int adf_ae_start(struct adf_accel_dev *accel_dev); int adf_ae_stop(struct adf_accel_dev *accel_dev); diff --git a/drivers/crypto/qat/qat_common/adf_ctl_drv.c b/drivers/crypto/qat/qat_common/adf_ctl_drv.c index 74207a6f0516..cb5f066e93a6 100644 --- a/drivers/crypto/qat/qat_common/adf_ctl_drv.c +++ b/drivers/crypto/qat/qat_common/adf_ctl_drv.c @@ -77,14 +77,14 @@ struct adf_ctl_drv_info { struct class *drv_class; }; -static struct adf_ctl_drv_info adt_ctl_drv; +static struct adf_ctl_drv_info adf_ctl_drv; static void adf_chr_drv_destroy(void) { - device_destroy(adt_ctl_drv.drv_class, MKDEV(adt_ctl_drv.major, 0)); - cdev_del(&adt_ctl_drv.drv_cdev); - class_destroy(adt_ctl_drv.drv_class); - unregister_chrdev_region(MKDEV(adt_ctl_drv.major, 0), 1); + device_destroy(adf_ctl_drv.drv_class, MKDEV(adf_ctl_drv.major, 0)); + cdev_del(&adf_ctl_drv.drv_cdev); + class_destroy(adf_ctl_drv.drv_class); + unregister_chrdev_region(MKDEV(adf_ctl_drv.major, 0), 1); } static int adf_chr_drv_create(void) @@ -97,20 +97,20 @@ static int adf_chr_drv_create(void) return -EFAULT; } - adt_ctl_drv.drv_class = class_create(THIS_MODULE, DEVICE_NAME); - if (IS_ERR(adt_ctl_drv.drv_class)) { + adf_ctl_drv.drv_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(adf_ctl_drv.drv_class)) { pr_err("QAT: class_create failed for adf_ctl\n"); goto err_chrdev_unreg; } - adt_ctl_drv.major = MAJOR(dev_id); - cdev_init(&adt_ctl_drv.drv_cdev, &adf_ctl_ops); - if (cdev_add(&adt_ctl_drv.drv_cdev, dev_id, 1)) { + adf_ctl_drv.major = MAJOR(dev_id); + cdev_init(&adf_ctl_drv.drv_cdev, &adf_ctl_ops); + if (cdev_add(&adf_ctl_drv.drv_cdev, dev_id, 1)) { pr_err("QAT: cdev add failed\n"); goto err_class_destr; } - drv_device = device_create(adt_ctl_drv.drv_class, NULL, - MKDEV(adt_ctl_drv.major, 0), + drv_device = device_create(adf_ctl_drv.drv_class, NULL, + MKDEV(adf_ctl_drv.major, 0), NULL, DEVICE_NAME); if (IS_ERR(drv_device)) { pr_err("QAT: failed to create device\n"); @@ -118,9 +118,9 @@ static int adf_chr_drv_create(void) } return 0; err_cdev_del: - cdev_del(&adt_ctl_drv.drv_cdev); + cdev_del(&adf_ctl_drv.drv_cdev); err_class_destr: - class_destroy(adt_ctl_drv.drv_class); + class_destroy(adf_ctl_drv.drv_class); err_chrdev_unreg: unregister_chrdev_region(dev_id, 1); return -EFAULT; @@ -159,14 +159,16 @@ static int adf_add_key_value_data(struct adf_accel_dev *accel_dev, if (adf_cfg_add_key_value_param(accel_dev, section, key_val->key, (void *)val, key_val->type)) { - pr_err("QAT: failed to add keyvalue.\n"); + dev_err(&GET_DEV(accel_dev), + "failed to add hex keyvalue.\n"); return -EFAULT; } } else { if (adf_cfg_add_key_value_param(accel_dev, section, key_val->key, key_val->val, key_val->type)) { - pr_err("QAT: failed to add keyvalue.\n"); + dev_err(&GET_DEV(accel_dev), + "failed to add keyvalue.\n"); return -EFAULT; } } @@ -185,12 +187,14 @@ static int adf_copy_key_value_data(struct adf_accel_dev *accel_dev, while (section_head) { if (copy_from_user(§ion, (void __user *)section_head, sizeof(*section_head))) { - pr_err("QAT: failed to copy section info\n"); + dev_err(&GET_DEV(accel_dev), + "failed to copy section info\n"); goto out_err; } if (adf_cfg_section_add(accel_dev, section.name)) { - pr_err("QAT: failed to add section.\n"); + dev_err(&GET_DEV(accel_dev), + "failed to add section.\n"); goto out_err; } @@ -199,7 +203,8 @@ static int adf_copy_key_value_data(struct adf_accel_dev *accel_dev, while (params_head) { if (copy_from_user(&key_val, (void __user *)params_head, sizeof(key_val))) { - pr_err("QAT: Failed to copy keyvalue.\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to copy keyvalue.\n"); goto out_err; } if (adf_add_key_value_data(accel_dev, section.name, @@ -258,8 +263,9 @@ static int adf_ctl_is_device_in_use(int id) if (id == dev->accel_id || id == ADF_CFG_ALL_DEVICES) { if (adf_devmgr_in_reset(dev) || adf_dev_in_use(dev)) { - pr_info("QAT: device qat_dev%d is busy\n", - dev->accel_id); + dev_info(&GET_DEV(dev), + "device qat_dev%d is busy\n", + dev->accel_id); return -EBUSY; } } @@ -280,7 +286,8 @@ static int adf_ctl_stop_devices(uint32_t id) continue; if (adf_dev_stop(accel_dev)) { - pr_err("QAT: Failed to stop qat_dev%d\n", id); + dev_err(&GET_DEV(accel_dev), + "Failed to stop qat_dev%d\n", id); ret = -EFAULT; } else { adf_dev_shutdown(accel_dev); @@ -343,17 +350,20 @@ static int adf_ctl_ioctl_dev_start(struct file *fp, unsigned int cmd, } if (!adf_dev_started(accel_dev)) { - pr_info("QAT: Starting acceleration device qat_dev%d.\n", - ctl_data->device_id); + dev_info(&GET_DEV(accel_dev), + "Starting acceleration device qat_dev%d.\n", + ctl_data->device_id); ret = adf_dev_init(accel_dev); if (!ret) ret = adf_dev_start(accel_dev); } else { - pr_info("QAT: Acceleration device qat_dev%d already started.\n", - ctl_data->device_id); + dev_info(&GET_DEV(accel_dev), + "Acceleration device qat_dev%d already started.\n", + ctl_data->device_id); } if (ret) { - pr_err("QAT: Failed to start qat_dev%d\n", ctl_data->device_id); + dev_err(&GET_DEV(accel_dev), "Failed to start qat_dev%d\n", + ctl_data->device_id); adf_dev_stop(accel_dev); adf_dev_shutdown(accel_dev); } @@ -408,7 +418,7 @@ static int adf_ctl_ioctl_get_status(struct file *fp, unsigned int cmd, if (copy_to_user((void __user *)arg, &dev_info, sizeof(struct adf_dev_status_info))) { - pr_err("QAT: failed to copy status.\n"); + dev_err(&GET_DEV(accel_dev), "failed to copy status.\n"); return -EFAULT; } return 0; diff --git a/drivers/crypto/qat/qat_common/adf_dev_mgr.c b/drivers/crypto/qat/qat_common/adf_dev_mgr.c index 4a0a829d4500..3f0ff9e7d840 100644 --- a/drivers/crypto/qat/qat_common/adf_dev_mgr.c +++ b/drivers/crypto/qat/qat_common/adf_dev_mgr.c @@ -67,7 +67,8 @@ int adf_devmgr_add_dev(struct adf_accel_dev *accel_dev) struct list_head *itr; if (num_devices == ADF_MAX_DEVICES) { - pr_err("QAT: Only support up to %d devices\n", ADF_MAX_DEVICES); + dev_err(&GET_DEV(accel_dev), "Only support up to %d devices\n", + ADF_MAX_DEVICES); return -EFAULT; } diff --git a/drivers/crypto/qat/qat_common/adf_init.c b/drivers/crypto/qat/qat_common/adf_init.c index 8f0ca498ab87..245f43237a2d 100644 --- a/drivers/crypto/qat/qat_common/adf_init.c +++ b/drivers/crypto/qat/qat_common/adf_init.c @@ -124,12 +124,12 @@ int adf_dev_init(struct adf_accel_dev *accel_dev) if (!hw_data) { dev_err(&GET_DEV(accel_dev), - "QAT: Failed to init device - hw_data not set\n"); + "Failed to init device - hw_data not set\n"); return -EFAULT; } if (!test_bit(ADF_STATUS_CONFIGURED, &accel_dev->status)) { - pr_info("QAT: Device not configured\n"); + dev_err(&GET_DEV(accel_dev), "Device not configured\n"); return -EFAULT; } @@ -151,20 +151,21 @@ int adf_dev_init(struct adf_accel_dev *accel_dev) hw_data->enable_ints(accel_dev); if (adf_ae_init(accel_dev)) { - pr_err("QAT: Failed to initialise Acceleration Engine\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to initialise Acceleration Engine\n"); return -EFAULT; } set_bit(ADF_STATUS_AE_INITIALISED, &accel_dev->status); if (adf_ae_fw_load(accel_dev)) { - pr_err("QAT: Failed to load acceleration FW\n"); - adf_ae_fw_release(accel_dev); + dev_err(&GET_DEV(accel_dev), + "Failed to load acceleration FW\n"); return -EFAULT; } set_bit(ADF_STATUS_AE_UCODE_LOADED, &accel_dev->status); if (hw_data->alloc_irq(accel_dev)) { - pr_err("QAT: Failed to allocate interrupts\n"); + dev_err(&GET_DEV(accel_dev), "Failed to allocate interrupts\n"); return -EFAULT; } set_bit(ADF_STATUS_IRQ_ALLOCATED, &accel_dev->status); @@ -179,8 +180,9 @@ int adf_dev_init(struct adf_accel_dev *accel_dev) if (!service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { - pr_err("QAT: Failed to initialise service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to initialise service %s\n", + service->name); return -EFAULT; } set_bit(accel_dev->accel_id, &service->init_status); @@ -190,8 +192,9 @@ int adf_dev_init(struct adf_accel_dev *accel_dev) if (service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_INIT)) { - pr_err("QAT: Failed to initialise service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to initialise service %s\n", + service->name); return -EFAULT; } set_bit(accel_dev->accel_id, &service->init_status); @@ -221,7 +224,7 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) set_bit(ADF_STATUS_STARTING, &accel_dev->status); if (adf_ae_start(accel_dev)) { - pr_err("QAT: AE Start Failed\n"); + dev_err(&GET_DEV(accel_dev), "AE Start Failed\n"); return -EFAULT; } set_bit(ADF_STATUS_AE_STARTED, &accel_dev->status); @@ -231,8 +234,9 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) if (!service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_START)) { - pr_err("QAT: Failed to start service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to start service %s\n", + service->name); return -EFAULT; } set_bit(accel_dev->accel_id, &service->start_status); @@ -242,8 +246,9 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) if (service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_START)) { - pr_err("QAT: Failed to start service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to start service %s\n", + service->name); return -EFAULT; } set_bit(accel_dev->accel_id, &service->start_status); @@ -253,7 +258,8 @@ int adf_dev_start(struct adf_accel_dev *accel_dev) set_bit(ADF_STATUS_STARTED, &accel_dev->status); if (qat_algs_register()) { - pr_err("QAT: Failed to register crypto algs\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to register crypto algs\n"); set_bit(ADF_STATUS_STARTING, &accel_dev->status); clear_bit(ADF_STATUS_STARTED, &accel_dev->status); return -EFAULT; @@ -287,7 +293,8 @@ int adf_dev_stop(struct adf_accel_dev *accel_dev) clear_bit(ADF_STATUS_STARTED, &accel_dev->status); if (qat_algs_unregister()) - pr_err("QAT: Failed to unregister crypto algs\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to unregister crypto algs\n"); list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); @@ -310,8 +317,9 @@ int adf_dev_stop(struct adf_accel_dev *accel_dev) if (!test_bit(accel_dev->accel_id, &service->start_status)) continue; if (service->event_hld(accel_dev, ADF_EVENT_STOP)) - pr_err("QAT: Failed to shutdown service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to shutdown service %s\n", + service->name); else clear_bit(accel_dev->accel_id, &service->start_status); } @@ -321,7 +329,7 @@ int adf_dev_stop(struct adf_accel_dev *accel_dev) if (test_bit(ADF_STATUS_AE_STARTED, &accel_dev->status)) { if (adf_ae_stop(accel_dev)) - pr_err("QAT: failed to stop AE\n"); + dev_err(&GET_DEV(accel_dev), "failed to stop AE\n"); else clear_bit(ADF_STATUS_AE_STARTED, &accel_dev->status); } @@ -350,16 +358,14 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev) } if (test_bit(ADF_STATUS_AE_UCODE_LOADED, &accel_dev->status)) { - if (adf_ae_fw_release(accel_dev)) - pr_err("QAT: Failed to release the ucode\n"); - else - clear_bit(ADF_STATUS_AE_UCODE_LOADED, - &accel_dev->status); + adf_ae_fw_release(accel_dev); + clear_bit(ADF_STATUS_AE_UCODE_LOADED, &accel_dev->status); } if (test_bit(ADF_STATUS_AE_INITIALISED, &accel_dev->status)) { if (adf_ae_shutdown(accel_dev)) - pr_err("QAT: Failed to shutdown Accel Engine\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to shutdown Accel Engine\n"); else clear_bit(ADF_STATUS_AE_INITIALISED, &accel_dev->status); @@ -372,8 +378,9 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev) if (!test_bit(accel_dev->accel_id, &service->init_status)) continue; if (service->event_hld(accel_dev, ADF_EVENT_SHUTDOWN)) - pr_err("QAT: Failed to shutdown service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to shutdown service %s\n", + service->name); else clear_bit(accel_dev->accel_id, &service->init_status); } @@ -384,8 +391,9 @@ void adf_dev_shutdown(struct adf_accel_dev *accel_dev) if (!test_bit(accel_dev->accel_id, &service->init_status)) continue; if (service->event_hld(accel_dev, ADF_EVENT_SHUTDOWN)) - pr_err("QAT: Failed to shutdown service %s\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to shutdown service %s\n", + service->name); else clear_bit(accel_dev->accel_id, &service->init_status); } @@ -419,16 +427,18 @@ int adf_dev_restarting_notify(struct adf_accel_dev *accel_dev) if (service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) - pr_err("QAT: Failed to restart service %s.\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to restart service %s.\n", + service->name); } list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (!service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_RESTARTING)) - pr_err("QAT: Failed to restart service %s.\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to restart service %s.\n", + service->name); } return 0; } @@ -443,16 +453,18 @@ int adf_dev_restarted_notify(struct adf_accel_dev *accel_dev) if (service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) - pr_err("QAT: Failed to restart service %s.\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to restart service %s.\n", + service->name); } list_for_each(list_itr, &service_table) { service = list_entry(list_itr, struct service_hndl, list); if (!service->admin) continue; if (service->event_hld(accel_dev, ADF_EVENT_RESTARTED)) - pr_err("QAT: Failed to restart service %s.\n", - service->name); + dev_err(&GET_DEV(accel_dev), + "Failed to restart service %s.\n", + service->name); } return 0; } diff --git a/drivers/crypto/qat/qat_common/adf_transport.c b/drivers/crypto/qat/qat_common/adf_transport.c index 7dd54aaee9fa..ccec327489da 100644 --- a/drivers/crypto/qat/qat_common/adf_transport.c +++ b/drivers/crypto/qat/qat_common/adf_transport.c @@ -195,7 +195,7 @@ static int adf_init_ring(struct adf_etr_ring_data *ring) memset(ring->base_addr, 0x7F, ring_size_bytes); /* The base_addr has to be aligned to the size of the buffer */ if (adf_check_ring_alignment(ring->dma_addr, ring_size_bytes)) { - pr_err("QAT: Ring address not aligned\n"); + dev_err(&GET_DEV(accel_dev), "Ring address not aligned\n"); dma_free_coherent(&GET_DEV(accel_dev), ring_size_bytes, ring->base_addr, ring->dma_addr); return -EFAULT; @@ -242,32 +242,33 @@ int adf_create_ring(struct adf_accel_dev *accel_dev, const char *section, int ret; if (bank_num >= GET_MAX_BANKS(accel_dev)) { - pr_err("QAT: Invalid bank number\n"); + dev_err(&GET_DEV(accel_dev), "Invalid bank number\n"); return -EFAULT; } if (msg_size > ADF_MSG_SIZE_TO_BYTES(ADF_MAX_MSG_SIZE)) { - pr_err("QAT: Invalid msg size\n"); + dev_err(&GET_DEV(accel_dev), "Invalid msg size\n"); return -EFAULT; } if (ADF_MAX_INFLIGHTS(adf_verify_ring_size(msg_size, num_msgs), ADF_BYTES_TO_MSG_SIZE(msg_size)) < 2) { - pr_err("QAT: Invalid ring size for given msg size\n"); + dev_err(&GET_DEV(accel_dev), + "Invalid ring size for given msg size\n"); return -EFAULT; } if (adf_cfg_get_param_value(accel_dev, section, ring_name, val)) { - pr_err("QAT: Section %s, no such entry : %s\n", - section, ring_name); + dev_err(&GET_DEV(accel_dev), "Section %s, no such entry : %s\n", + section, ring_name); return -EFAULT; } if (kstrtouint(val, 10, &ring_num)) { - pr_err("QAT: Can't get ring number\n"); + dev_err(&GET_DEV(accel_dev), "Can't get ring number\n"); return -EFAULT; } bank = &transport_data->banks[bank_num]; if (adf_reserve_ring(bank, ring_num)) { - pr_err("QAT: Ring %d, %s already exists.\n", - ring_num, ring_name); + dev_err(&GET_DEV(accel_dev), "Ring %d, %s already exists.\n", + ring_num, ring_name); return -EFAULT; } ring = &bank->rings[ring_num]; @@ -287,7 +288,8 @@ int adf_create_ring(struct adf_accel_dev *accel_dev, const char *section, accel_dev->hw_device->hw_arb_ring_enable(ring); if (adf_ring_debugfs_add(ring, ring_name)) { - pr_err("QAT: Couldn't add ring debugfs entry\n"); + dev_err(&GET_DEV(accel_dev), + "Couldn't add ring debugfs entry\n"); ret = -EFAULT; goto err; } @@ -428,7 +430,8 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, goto err; } else { if (i < hw_data->tx_rx_gap) { - pr_err("QAT: Invalid tx rings mask config\n"); + dev_err(&GET_DEV(accel_dev), + "Invalid tx rings mask config\n"); goto err; } tx_ring = &bank->rings[i - hw_data->tx_rx_gap]; @@ -436,7 +439,8 @@ static int adf_init_bank(struct adf_accel_dev *accel_dev, } } if (adf_bank_debugfs_add(bank)) { - pr_err("QAT: Failed to add bank debugfs entry\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to add bank debugfs entry\n"); goto err; } @@ -492,7 +496,8 @@ int adf_init_etr_data(struct adf_accel_dev *accel_dev) etr_data->debug = debugfs_create_dir("transport", accel_dev->debugfs_dir); if (!etr_data->debug) { - pr_err("QAT: Unable to create transport debugfs entry\n"); + dev_err(&GET_DEV(accel_dev), + "Unable to create transport debugfs entry\n"); ret = -ENOENT; goto err_bank_debug; } diff --git a/drivers/crypto/qat/qat_common/adf_transport_debug.c b/drivers/crypto/qat/qat_common/adf_transport_debug.c index 6b6974553514..e41986967294 100644 --- a/drivers/crypto/qat/qat_common/adf_transport_debug.c +++ b/drivers/crypto/qat/qat_common/adf_transport_debug.c @@ -100,6 +100,8 @@ static int adf_ring_show(struct seq_file *sfile, void *v) empty = READ_CSR_E_STAT(csr, bank->bank_number); seq_puts(sfile, "------- Ring configuration -------\n"); + seq_printf(sfile, "ring name: %s\n", + ring->ring_debug->ring_name); seq_printf(sfile, "ring num %d, bank num %d\n", ring->ring_number, ring->bank->bank_number); seq_printf(sfile, "head %x, tail %x, empty: %d\n", diff --git a/drivers/crypto/qat/qat_common/icp_qat_hw.h b/drivers/crypto/qat/qat_common/icp_qat_hw.h index 68f191b653b0..121d5e6e46ca 100644 --- a/drivers/crypto/qat/qat_common/icp_qat_hw.h +++ b/drivers/crypto/qat/qat_common/icp_qat_hw.h @@ -145,7 +145,7 @@ struct icp_qat_hw_auth_setup { }; #define QAT_HW_DEFAULT_ALIGNMENT 8 -#define QAT_HW_ROUND_UP(val, n) (((val) + ((n)-1)) & (~(n-1))) +#define QAT_HW_ROUND_UP(val, n) (((val) + ((n) - 1)) & (~(n - 1))) #define ICP_QAT_HW_NULL_STATE1_SZ 32 #define ICP_QAT_HW_MD5_STATE1_SZ 16 #define ICP_QAT_HW_SHA1_STATE1_SZ 20 diff --git a/drivers/crypto/qat/qat_common/qat_crypto.c b/drivers/crypto/qat/qat_common/qat_crypto.c index 828f2a686aab..3bd705ca5973 100644 --- a/drivers/crypto/qat/qat_common/qat_crypto.c +++ b/drivers/crypto/qat/qat_common/qat_crypto.c @@ -110,13 +110,13 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node) list_for_each(itr, adf_devmgr_get_head()) { accel_dev = list_entry(itr, struct adf_accel_dev, list); if ((node == dev_to_node(&GET_DEV(accel_dev)) || - dev_to_node(&GET_DEV(accel_dev)) < 0) - && adf_dev_started(accel_dev)) + dev_to_node(&GET_DEV(accel_dev)) < 0) && + adf_dev_started(accel_dev)) break; accel_dev = NULL; } if (!accel_dev) { - pr_err("QAT: Could not find device on node %d\n", node); + pr_err("QAT: Could not find a device on node %d\n", node); accel_dev = adf_devmgr_get_first(); } if (!accel_dev || !adf_dev_started(accel_dev)) @@ -137,7 +137,8 @@ struct qat_crypto_instance *qat_crypto_get_instance_node(int node) if (atomic_add_return(1, &inst_best->refctr) == 1) { if (adf_dev_get(accel_dev)) { atomic_dec(&inst_best->refctr); - pr_err("QAT: Could increment dev refctr\n"); + dev_err(&GET_DEV(accel_dev), + "Could not increment dev refctr\n"); return NULL; } } diff --git a/drivers/crypto/qat/qat_common/qat_hal.c b/drivers/crypto/qat/qat_common/qat_hal.c index b818c19713bf..274ff7e9de6e 100644 --- a/drivers/crypto/qat/qat_common/qat_hal.c +++ b/drivers/crypto/qat/qat_common/qat_hal.c @@ -434,8 +434,8 @@ static void qat_hal_reset_timestamp(struct icp_qat_fw_loader_handle *handle) SET_GLB_CSR(handle, MISC_CONTROL, misc_ctl | MC_TIMESTAMP_ENABLE); } -#define ESRAM_AUTO_TINIT (1<<2) -#define ESRAM_AUTO_TINIT_DONE (1<<3) +#define ESRAM_AUTO_TINIT BIT(2) +#define ESRAM_AUTO_TINIT_DONE BIT(3) #define ESRAM_AUTO_INIT_USED_CYCLES (1640) #define ESRAM_AUTO_INIT_CSR_OFFSET 0xC1C static int qat_hal_init_esram(struct icp_qat_fw_loader_handle *handle) @@ -718,7 +718,7 @@ int qat_hal_init(struct adf_accel_dev *accel_dev) handle->hal_handle->ae_max_num = max_en_ae_id + 1; /* take all AEs out of reset */ if (qat_hal_clr_reset(handle)) { - pr_err("QAT: qat_hal_clr_reset error\n"); + dev_err(&GET_DEV(accel_dev), "qat_hal_clr_reset error\n"); goto out_err; } if (qat_hal_clear_gpr(handle)) diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_admin.c b/drivers/crypto/qat/qat_dh895xcc/adf_admin.c index 53c491b59f07..e4666065c399 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_admin.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_admin.c @@ -93,7 +93,8 @@ int adf_put_admin_msg_sync(struct adf_accel_dev *accel_dev, memcpy(out, admin->virt_addr + offset + ADF_ADMINMSG_LEN, ADF_ADMINMSG_LEN); else - pr_err("QAT: Failed to send admin msg to accelerator\n"); + dev_err(&GET_DEV(accel_dev), + "Failed to send admin msg to accelerator\n"); mutex_unlock(&admin->lock); return received ? 0 : -EFAULT; diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c index 6a735d5c0e37..b1386922d7a2 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.c @@ -150,7 +150,8 @@ void adf_get_arbiter_mapping(struct adf_accel_dev *accel_dev, *arb_map_config = thrd_to_arb_map_sku6; break; default: - pr_err("QAT: The configuration doesn't match any SKU"); + dev_err(&GET_DEV(accel_dev), + "The configuration doesn't match any SKU"); *arb_map_config = NULL; } } diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h index 01e0be21e93a..25269a9f24a2 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h +++ b/drivers/crypto/qat/qat_dh895xcc/adf_dh895xcc_hw_data.h @@ -73,11 +73,11 @@ /* Error detection and correction */ #define ADF_DH895XCC_AE_CTX_ENABLES(i) (i * 0x1000 + 0x20818) #define ADF_DH895XCC_AE_MISC_CONTROL(i) (i * 0x1000 + 0x20960) -#define ADF_DH895XCC_ENABLE_AE_ECC_ERR (1 << 28) -#define ADF_DH895XCC_ENABLE_AE_ECC_PARITY_CORR (1 << 24 | 1 << 12) +#define ADF_DH895XCC_ENABLE_AE_ECC_ERR BIT(28) +#define ADF_DH895XCC_ENABLE_AE_ECC_PARITY_CORR (BIT(24) | BIT(12)) #define ADF_DH895XCC_UERRSSMSH(i) (i * 0x4000 + 0x18) #define ADF_DH895XCC_CERRSSMSH(i) (i * 0x4000 + 0x10) -#define ADF_DH895XCC_ERRSSMSH_EN (1 << 3) +#define ADF_DH895XCC_ERRSSMSH_EN BIT(3) /* Admin Messages Registers */ #define ADF_DH895XCC_ADMINMSGUR_OFFSET (0x3A000 + 0x574) diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c index 8ffdb95c9804..9decea2779c6 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_drv.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_drv.c @@ -236,7 +236,7 @@ static int adf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } accel_dev = kzalloc_node(sizeof(*accel_dev), GFP_KERNEL, - dev_to_node(&pdev->dev)); + dev_to_node(&pdev->dev)); if (!accel_dev) return -ENOMEM; @@ -379,7 +379,7 @@ out_err: return ret; } -static void __exit adf_remove(struct pci_dev *pdev) +static void adf_remove(struct pci_dev *pdev) { struct adf_accel_dev *accel_dev = adf_devmgr_pci_to_accel_dev(pdev); diff --git a/drivers/crypto/qat/qat_dh895xcc/adf_isr.c b/drivers/crypto/qat/qat_dh895xcc/adf_isr.c index fe8f89697ad8..0d03c109c2d3 100644 --- a/drivers/crypto/qat/qat_dh895xcc/adf_isr.c +++ b/drivers/crypto/qat/qat_dh895xcc/adf_isr.c @@ -73,7 +73,7 @@ static int adf_enable_msix(struct adf_accel_dev *accel_dev) if (pci_enable_msix_exact(pci_dev_info->pci_dev, pci_dev_info->msix_entries.entries, msix_num_entries)) { - pr_err("QAT: Failed to enable MSIX IRQ\n"); + dev_err(&GET_DEV(accel_dev), "Failed to enable MSIX IRQ\n"); return -EFAULT; } return 0; @@ -97,7 +97,8 @@ static irqreturn_t adf_msix_isr_ae(int irq, void *dev_ptr) { struct adf_accel_dev *accel_dev = dev_ptr; - pr_info("QAT: qat_dev%d spurious AE interrupt\n", accel_dev->accel_id); + dev_info(&GET_DEV(accel_dev), "qat_dev%d spurious AE interrupt\n", + accel_dev->accel_id); return IRQ_HANDLED; } @@ -121,8 +122,9 @@ static int adf_request_irqs(struct adf_accel_dev *accel_dev) ret = request_irq(msixe[i].vector, adf_msix_isr_bundle, 0, name, bank); if (ret) { - pr_err("QAT: failed to enable irq %d for %s\n", - msixe[i].vector, name); + dev_err(&GET_DEV(accel_dev), + "failed to enable irq %d for %s\n", + msixe[i].vector, name); return ret; } @@ -136,8 +138,9 @@ static int adf_request_irqs(struct adf_accel_dev *accel_dev) "qat%d-ae-cluster", accel_dev->accel_id); ret = request_irq(msixe[i].vector, adf_msix_isr_ae, 0, name, accel_dev); if (ret) { - pr_err("QAT: failed to enable irq %d, for %s\n", - msixe[i].vector, name); + dev_err(&GET_DEV(accel_dev), + "failed to enable irq %d, for %s\n", + msixe[i].vector, name); return ret; } return ret; diff --git a/drivers/crypto/sahara.c b/drivers/crypto/sahara.c index 290a7f0a681f..6be377f6b9e7 100644 --- a/drivers/crypto/sahara.c +++ b/drivers/crypto/sahara.c @@ -479,6 +479,7 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev) struct scatterlist *sg; int ret; int i, j; + int idx = 0; /* Copy new key if necessary */ if (ctx->flags & FLAGS_NEW_KEY) { @@ -486,17 +487,20 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev) ctx->flags &= ~FLAGS_NEW_KEY; if (dev->flags & FLAGS_CBC) { - dev->hw_desc[0]->len1 = AES_BLOCK_SIZE; - dev->hw_desc[0]->p1 = dev->iv_phys_base; + dev->hw_desc[idx]->len1 = AES_BLOCK_SIZE; + dev->hw_desc[idx]->p1 = dev->iv_phys_base; } else { - dev->hw_desc[0]->len1 = 0; - dev->hw_desc[0]->p1 = 0; + dev->hw_desc[idx]->len1 = 0; + dev->hw_desc[idx]->p1 = 0; } - dev->hw_desc[0]->len2 = ctx->keylen; - dev->hw_desc[0]->p2 = dev->key_phys_base; - dev->hw_desc[0]->next = dev->hw_phys_desc[1]; + dev->hw_desc[idx]->len2 = ctx->keylen; + dev->hw_desc[idx]->p2 = dev->key_phys_base; + dev->hw_desc[idx]->next = dev->hw_phys_desc[1]; + + dev->hw_desc[idx]->hdr = sahara_aes_key_hdr(dev); + + idx++; } - dev->hw_desc[0]->hdr = sahara_aes_key_hdr(dev); dev->nb_in_sg = sahara_sg_length(dev->in_sg, dev->total); dev->nb_out_sg = sahara_sg_length(dev->out_sg, dev->total); @@ -520,7 +524,7 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev) } /* Create input links */ - dev->hw_desc[1]->p1 = dev->hw_phys_link[0]; + dev->hw_desc[idx]->p1 = dev->hw_phys_link[0]; sg = dev->in_sg; for (i = 0; i < dev->nb_in_sg; i++) { dev->hw_link[i]->len = sg->length; @@ -534,7 +538,7 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev) } /* Create output links */ - dev->hw_desc[1]->p2 = dev->hw_phys_link[i]; + dev->hw_desc[idx]->p2 = dev->hw_phys_link[i]; sg = dev->out_sg; for (j = i; j < dev->nb_out_sg + i; j++) { dev->hw_link[j]->len = sg->length; @@ -548,10 +552,10 @@ static int sahara_hw_descriptor_create(struct sahara_dev *dev) } /* Fill remaining fields of hw_desc[1] */ - dev->hw_desc[1]->hdr = sahara_aes_data_link_hdr(dev); - dev->hw_desc[1]->len1 = dev->total; - dev->hw_desc[1]->len2 = dev->total; - dev->hw_desc[1]->next = 0; + dev->hw_desc[idx]->hdr = sahara_aes_data_link_hdr(dev); + dev->hw_desc[idx]->len1 = dev->total; + dev->hw_desc[idx]->len2 = dev->total; + dev->hw_desc[idx]->next = 0; sahara_dump_descriptors(dev); sahara_dump_links(dev); @@ -576,6 +580,7 @@ static int sahara_aes_process(struct ablkcipher_request *req) struct sahara_ctx *ctx; struct sahara_aes_reqctx *rctx; int ret; + unsigned long timeout; /* Request is ready to be dispatched by the device */ dev_dbg(dev->device, @@ -601,10 +606,12 @@ static int sahara_aes_process(struct ablkcipher_request *req) reinit_completion(&dev->dma_completion); ret = sahara_hw_descriptor_create(dev); + if (ret) + return -EINVAL; - ret = wait_for_completion_timeout(&dev->dma_completion, + timeout = wait_for_completion_timeout(&dev->dma_completion, msecs_to_jiffies(SAHARA_TIMEOUT_MS)); - if (!ret) { + if (!timeout) { dev_err(dev->device, "AES timeout\n"); return -ETIMEDOUT; } @@ -1044,7 +1051,8 @@ static int sahara_sha_process(struct ahash_request *req) { struct sahara_dev *dev = dev_ptr; struct sahara_sha_reqctx *rctx = ahash_request_ctx(req); - int ret = -EINPROGRESS; + int ret; + unsigned long timeout; ret = sahara_sha_prepare_request(req); if (!ret) @@ -1070,9 +1078,9 @@ static int sahara_sha_process(struct ahash_request *req) sahara_write(dev, dev->hw_phys_desc[0], SAHARA_REG_DAR); - ret = wait_for_completion_timeout(&dev->dma_completion, + timeout = wait_for_completion_timeout(&dev->dma_completion, msecs_to_jiffies(SAHARA_TIMEOUT_MS)); - if (!ret) { + if (!timeout) { dev_err(dev->device, "SHA timeout\n"); return -ETIMEDOUT; } @@ -1092,15 +1100,20 @@ static int sahara_queue_manage(void *data) { struct sahara_dev *dev = (struct sahara_dev *)data; struct crypto_async_request *async_req; + struct crypto_async_request *backlog; int ret = 0; do { __set_current_state(TASK_INTERRUPTIBLE); mutex_lock(&dev->queue_mutex); + backlog = crypto_get_backlog(&dev->queue); async_req = crypto_dequeue_request(&dev->queue); mutex_unlock(&dev->queue_mutex); + if (backlog) + backlog->complete(backlog, -EINPROGRESS); + if (async_req) { if (crypto_tfm_alg_type(async_req->tfm) == CRYPTO_ALG_TYPE_AHASH) { diff --git a/drivers/crypto/talitos.c b/drivers/crypto/talitos.c index ebbae8d3ce0d..857414afa29a 100644 --- a/drivers/crypto/talitos.c +++ b/drivers/crypto/talitos.c @@ -637,8 +637,6 @@ static void talitos_unregister_rng(struct device *dev) #define TALITOS_MAX_KEY_SIZE 96 #define TALITOS_MAX_IV_LENGTH 16 /* max of AES_BLOCK_SIZE, DES3_EDE_BLOCK_SIZE */ -#define MD5_BLOCK_SIZE 64 - struct talitos_ctx { struct device *dev; int ch; @@ -2195,7 +2193,7 @@ static struct talitos_alg_template driver_algs[] = { .halg.base = { .cra_name = "md5", .cra_driver_name = "md5-talitos", - .cra_blocksize = MD5_BLOCK_SIZE, + .cra_blocksize = MD5_HMAC_BLOCK_SIZE, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, } @@ -2285,7 +2283,7 @@ static struct talitos_alg_template driver_algs[] = { .halg.base = { .cra_name = "hmac(md5)", .cra_driver_name = "hmac-md5-talitos", - .cra_blocksize = MD5_BLOCK_SIZE, + .cra_blocksize = MD5_HMAC_BLOCK_SIZE, .cra_flags = CRYPTO_ALG_TYPE_AHASH | CRYPTO_ALG_ASYNC, } @@ -2706,20 +2704,16 @@ static int talitos_probe(struct platform_device *ofdev) goto err_out; } + priv->fifo_len = roundup_pow_of_two(priv->chfifo_len); + for (i = 0; i < priv->num_channels; i++) { priv->chan[i].reg = priv->reg + TALITOS_CH_STRIDE * (i + 1); if (!priv->irq[1] || !(i & 1)) priv->chan[i].reg += TALITOS_CH_BASE_OFFSET; - } - for (i = 0; i < priv->num_channels; i++) { spin_lock_init(&priv->chan[i].head_lock); spin_lock_init(&priv->chan[i].tail_lock); - } - priv->fifo_len = roundup_pow_of_two(priv->chfifo_len); - - for (i = 0; i < priv->num_channels; i++) { priv->chan[i].fifo = kzalloc(sizeof(struct talitos_request) * priv->fifo_len, GFP_KERNEL); if (!priv->chan[i].fifo) { @@ -2727,11 +2721,10 @@ static int talitos_probe(struct platform_device *ofdev) err = -ENOMEM; goto err_out; } - } - for (i = 0; i < priv->num_channels; i++) atomic_set(&priv->chan[i].submit_count, -(priv->chfifo_len - 1)); + } dma_set_mask(dev, DMA_BIT_MASK(36)); diff --git a/drivers/crypto/ux500/hash/hash_core.c b/drivers/crypto/ux500/hash/hash_core.c index 187a8fd7eee7..5f5f360628fc 100644 --- a/drivers/crypto/ux500/hash/hash_core.c +++ b/drivers/crypto/ux500/hash/hash_core.c @@ -184,7 +184,7 @@ static int hash_set_dma_transfer(struct hash_ctx *ctx, struct scatterlist *sg, direction, DMA_CTRL_ACK | DMA_PREP_INTERRUPT); if (!desc) { dev_err(ctx->device->dev, - "%s: device_prep_slave_sg() failed!\n", __func__); + "%s: dmaengine_prep_slave_sg() failed!\n", __func__); return -EFAULT; } diff --git a/drivers/crypto/vmx/Kconfig b/drivers/crypto/vmx/Kconfig new file mode 100644 index 000000000000..771babf16aa0 --- /dev/null +++ b/drivers/crypto/vmx/Kconfig @@ -0,0 +1,8 @@ +config CRYPTO_DEV_VMX_ENCRYPT + tristate "Encryption acceleration support on P8 CPU" + depends on PPC64 && CRYPTO_DEV_VMX + default y + help + Support for VMX cryptographic acceleration instructions on Power8 CPU. + This module supports acceleration for AES and GHASH in hardware. If you + choose 'M' here, this module will be called vmx-crypto. diff --git a/drivers/crypto/vmx/Makefile b/drivers/crypto/vmx/Makefile new file mode 100644 index 000000000000..c699c6e6c82e --- /dev/null +++ b/drivers/crypto/vmx/Makefile @@ -0,0 +1,19 @@ +obj-$(CONFIG_CRYPTO_DEV_VMX_ENCRYPT) += vmx-crypto.o +vmx-crypto-objs := vmx.o aesp8-ppc.o ghashp8-ppc.o aes.o aes_cbc.o aes_ctr.o ghash.o + +ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y) +TARGET := linux-ppc64le +else +TARGET := linux-pcc64 +endif + +quiet_cmd_perl = PERL $@ + cmd_perl = $(PERL) $(<) $(TARGET) > $(@) + +$(src)/aesp8-ppc.S: $(src)/aesp8-ppc.pl + $(call cmd,perl) + +$(src)/ghashp8-ppc.S: $(src)/ghashp8-ppc.pl + $(call cmd,perl) + +.PRECIOUS: $(obj)/aesp8-ppc.S $(obj)/ghashp8-ppc.S diff --git a/drivers/crypto/vmx/aes.c b/drivers/crypto/vmx/aes.c new file mode 100644 index 000000000000..ab300ea19434 --- /dev/null +++ b/drivers/crypto/vmx/aes.c @@ -0,0 +1,139 @@ +/** + * AES routines supporting VMX instructions on the Power 8 + * + * Copyright (C) 2015 International Business Machines 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; version 2 only. + * + * 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. + * + * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + */ + +#include <linux/types.h> +#include <linux/err.h> +#include <linux/crypto.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <asm/switch_to.h> +#include <crypto/aes.h> + +#include "aesp8-ppc.h" + +struct p8_aes_ctx { + struct crypto_cipher *fallback; + struct aes_key enc_key; + struct aes_key dec_key; +}; + +static int p8_aes_init(struct crypto_tfm *tfm) +{ + const char *alg; + struct crypto_cipher *fallback; + struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + if (!(alg = crypto_tfm_alg_name(tfm))) { + printk(KERN_ERR "Failed to get algorithm name.\n"); + return -ENOENT; + } + + fallback = crypto_alloc_cipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback)) { + printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", + alg, PTR_ERR(fallback)); + return PTR_ERR(fallback); + } + printk(KERN_INFO "Using '%s' as fallback implementation.\n", + crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); + + crypto_cipher_set_flags(fallback, + crypto_cipher_get_flags((struct crypto_cipher *) tfm)); + ctx->fallback = fallback; + + return 0; +} + +static void p8_aes_exit(struct crypto_tfm *tfm) +{ + struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) { + crypto_free_cipher(ctx->fallback); + ctx->fallback = NULL; + } +} + +static int p8_aes_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + int ret; + struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + pagefault_disable(); + enable_kernel_altivec(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key); + pagefault_enable(); + + ret += crypto_cipher_setkey(ctx->fallback, key, keylen); + return ret; +} + +static void p8_aes_encrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) +{ + struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + if (in_interrupt()) { + crypto_cipher_encrypt_one(ctx->fallback, dst, src); + } else { + pagefault_disable(); + enable_kernel_altivec(); + aes_p8_encrypt(src, dst, &ctx->enc_key); + pagefault_enable(); + } +} + +static void p8_aes_decrypt(struct crypto_tfm *tfm, u8 *dst, const u8 *src) +{ + struct p8_aes_ctx *ctx = crypto_tfm_ctx(tfm); + + if (in_interrupt()) { + crypto_cipher_decrypt_one(ctx->fallback, dst, src); + } else { + pagefault_disable(); + enable_kernel_altivec(); + aes_p8_decrypt(src, dst, &ctx->dec_key); + pagefault_enable(); + } +} + +struct crypto_alg p8_aes_alg = { + .cra_name = "aes", + .cra_driver_name = "p8_aes", + .cra_module = THIS_MODULE, + .cra_priority = 1000, + .cra_type = NULL, + .cra_flags = CRYPTO_ALG_TYPE_CIPHER | CRYPTO_ALG_NEED_FALLBACK, + .cra_alignmask = 0, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct p8_aes_ctx), + .cra_init = p8_aes_init, + .cra_exit = p8_aes_exit, + .cra_cipher = { + .cia_min_keysize = AES_MIN_KEY_SIZE, + .cia_max_keysize = AES_MAX_KEY_SIZE, + .cia_setkey = p8_aes_setkey, + .cia_encrypt = p8_aes_encrypt, + .cia_decrypt = p8_aes_decrypt, + }, +}; + diff --git a/drivers/crypto/vmx/aes_cbc.c b/drivers/crypto/vmx/aes_cbc.c new file mode 100644 index 000000000000..1a559b7dddb5 --- /dev/null +++ b/drivers/crypto/vmx/aes_cbc.c @@ -0,0 +1,184 @@ +/** + * AES CBC routines supporting VMX instructions on the Power 8 + * + * Copyright (C) 2015 International Business Machines 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; version 2 only. + * + * 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. + * + * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + */ + +#include <linux/types.h> +#include <linux/err.h> +#include <linux/crypto.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <asm/switch_to.h> +#include <crypto/aes.h> +#include <crypto/scatterwalk.h> + +#include "aesp8-ppc.h" + +struct p8_aes_cbc_ctx { + struct crypto_blkcipher *fallback; + struct aes_key enc_key; + struct aes_key dec_key; +}; + +static int p8_aes_cbc_init(struct crypto_tfm *tfm) +{ + const char *alg; + struct crypto_blkcipher *fallback; + struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm); + + if (!(alg = crypto_tfm_alg_name(tfm))) { + printk(KERN_ERR "Failed to get algorithm name.\n"); + return -ENOENT; + } + + fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback)) { + printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", + alg, PTR_ERR(fallback)); + return PTR_ERR(fallback); + } + printk(KERN_INFO "Using '%s' as fallback implementation.\n", + crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); + + crypto_blkcipher_set_flags(fallback, + crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm)); + ctx->fallback = fallback; + + return 0; +} + +static void p8_aes_cbc_exit(struct crypto_tfm *tfm) +{ + struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) { + crypto_free_blkcipher(ctx->fallback); + ctx->fallback = NULL; + } +} + +static int p8_aes_cbc_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + int ret; + struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx(tfm); + + pagefault_disable(); + enable_kernel_altivec(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + ret += aes_p8_set_decrypt_key(key, keylen * 8, &ctx->dec_key); + pagefault_enable(); + + ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen); + return ret; +} + +static int p8_aes_cbc_encrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, struct scatterlist *src, + unsigned int nbytes) +{ + int ret; + struct blkcipher_walk walk; + struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx( + crypto_blkcipher_tfm(desc->tfm)); + struct blkcipher_desc fallback_desc = { + .tfm = ctx->fallback, + .info = desc->info, + .flags = desc->flags + }; + + if (in_interrupt()) { + ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes); + } else { + pagefault_disable(); + enable_kernel_altivec(); + + blkcipher_walk_init(&walk, dst, src, nbytes); + ret = blkcipher_walk_virt(desc, &walk); + while ((nbytes = walk.nbytes)) { + aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr, + nbytes & AES_BLOCK_MASK, &ctx->enc_key, walk.iv, 1); + nbytes &= AES_BLOCK_SIZE - 1; + ret = blkcipher_walk_done(desc, &walk, nbytes); + } + + pagefault_enable(); + } + + return ret; +} + +static int p8_aes_cbc_decrypt(struct blkcipher_desc *desc, + struct scatterlist *dst, struct scatterlist *src, + unsigned int nbytes) +{ + int ret; + struct blkcipher_walk walk; + struct p8_aes_cbc_ctx *ctx = crypto_tfm_ctx( + crypto_blkcipher_tfm(desc->tfm)); + struct blkcipher_desc fallback_desc = { + .tfm = ctx->fallback, + .info = desc->info, + .flags = desc->flags + }; + + if (in_interrupt()) { + ret = crypto_blkcipher_decrypt(&fallback_desc, dst, src, nbytes); + } else { + pagefault_disable(); + enable_kernel_altivec(); + + blkcipher_walk_init(&walk, dst, src, nbytes); + ret = blkcipher_walk_virt(desc, &walk); + while ((nbytes = walk.nbytes)) { + aes_p8_cbc_encrypt(walk.src.virt.addr, walk.dst.virt.addr, + nbytes & AES_BLOCK_MASK, &ctx->dec_key, walk.iv, 0); + nbytes &= AES_BLOCK_SIZE - 1; + ret = blkcipher_walk_done(desc, &walk, nbytes); + } + + pagefault_enable(); + } + + return ret; +} + + +struct crypto_alg p8_aes_cbc_alg = { + .cra_name = "cbc(aes)", + .cra_driver_name = "p8_aes_cbc", + .cra_module = THIS_MODULE, + .cra_priority = 1000, + .cra_type = &crypto_blkcipher_type, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK, + .cra_alignmask = 0, + .cra_blocksize = AES_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct p8_aes_cbc_ctx), + .cra_init = p8_aes_cbc_init, + .cra_exit = p8_aes_cbc_exit, + .cra_blkcipher = { + .ivsize = 0, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = p8_aes_cbc_setkey, + .encrypt = p8_aes_cbc_encrypt, + .decrypt = p8_aes_cbc_decrypt, + }, +}; + diff --git a/drivers/crypto/vmx/aes_ctr.c b/drivers/crypto/vmx/aes_ctr.c new file mode 100644 index 000000000000..96dbee4bf4a6 --- /dev/null +++ b/drivers/crypto/vmx/aes_ctr.c @@ -0,0 +1,167 @@ +/** + * AES CTR routines supporting VMX instructions on the Power 8 + * + * Copyright (C) 2015 International Business Machines 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; version 2 only. + * + * 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. + * + * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + */ + +#include <linux/types.h> +#include <linux/err.h> +#include <linux/crypto.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <asm/switch_to.h> +#include <crypto/aes.h> +#include <crypto/scatterwalk.h> +#include "aesp8-ppc.h" + +struct p8_aes_ctr_ctx { + struct crypto_blkcipher *fallback; + struct aes_key enc_key; +}; + +static int p8_aes_ctr_init(struct crypto_tfm *tfm) +{ + const char *alg; + struct crypto_blkcipher *fallback; + struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm); + + if (!(alg = crypto_tfm_alg_name(tfm))) { + printk(KERN_ERR "Failed to get algorithm name.\n"); + return -ENOENT; + } + + fallback = crypto_alloc_blkcipher(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback)) { + printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", + alg, PTR_ERR(fallback)); + return PTR_ERR(fallback); + } + printk(KERN_INFO "Using '%s' as fallback implementation.\n", + crypto_tfm_alg_driver_name((struct crypto_tfm *) fallback)); + + crypto_blkcipher_set_flags(fallback, + crypto_blkcipher_get_flags((struct crypto_blkcipher *) tfm)); + ctx->fallback = fallback; + + return 0; +} + +static void p8_aes_ctr_exit(struct crypto_tfm *tfm) +{ + struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) { + crypto_free_blkcipher(ctx->fallback); + ctx->fallback = NULL; + } +} + +static int p8_aes_ctr_setkey(struct crypto_tfm *tfm, const u8 *key, + unsigned int keylen) +{ + int ret; + struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx(tfm); + + pagefault_disable(); + enable_kernel_altivec(); + ret = aes_p8_set_encrypt_key(key, keylen * 8, &ctx->enc_key); + pagefault_enable(); + + ret += crypto_blkcipher_setkey(ctx->fallback, key, keylen); + return ret; +} + +static void p8_aes_ctr_final(struct p8_aes_ctr_ctx *ctx, + struct blkcipher_walk *walk) +{ + u8 *ctrblk = walk->iv; + u8 keystream[AES_BLOCK_SIZE]; + u8 *src = walk->src.virt.addr; + u8 *dst = walk->dst.virt.addr; + unsigned int nbytes = walk->nbytes; + + pagefault_disable(); + enable_kernel_altivec(); + aes_p8_encrypt(ctrblk, keystream, &ctx->enc_key); + pagefault_enable(); + + crypto_xor(keystream, src, nbytes); + memcpy(dst, keystream, nbytes); + crypto_inc(ctrblk, AES_BLOCK_SIZE); +} + +static int p8_aes_ctr_crypt(struct blkcipher_desc *desc, + struct scatterlist *dst, struct scatterlist *src, + unsigned int nbytes) +{ + int ret; + struct blkcipher_walk walk; + struct p8_aes_ctr_ctx *ctx = crypto_tfm_ctx( + crypto_blkcipher_tfm(desc->tfm)); + struct blkcipher_desc fallback_desc = { + .tfm = ctx->fallback, + .info = desc->info, + .flags = desc->flags + }; + + if (in_interrupt()) { + ret = crypto_blkcipher_encrypt(&fallback_desc, dst, src, nbytes); + } else { + blkcipher_walk_init(&walk, dst, src, nbytes); + ret = blkcipher_walk_virt_block(desc, &walk, AES_BLOCK_SIZE); + while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) { + pagefault_disable(); + enable_kernel_altivec(); + aes_p8_ctr32_encrypt_blocks(walk.src.virt.addr, walk.dst.virt.addr, + (nbytes & AES_BLOCK_MASK)/AES_BLOCK_SIZE, &ctx->enc_key, walk.iv); + pagefault_enable(); + + crypto_inc(walk.iv, AES_BLOCK_SIZE); + nbytes &= AES_BLOCK_SIZE - 1; + ret = blkcipher_walk_done(desc, &walk, nbytes); + } + if (walk.nbytes) { + p8_aes_ctr_final(ctx, &walk); + ret = blkcipher_walk_done(desc, &walk, 0); + } + } + + return ret; +} + +struct crypto_alg p8_aes_ctr_alg = { + .cra_name = "ctr(aes)", + .cra_driver_name = "p8_aes_ctr", + .cra_module = THIS_MODULE, + .cra_priority = 1000, + .cra_type = &crypto_blkcipher_type, + .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER | CRYPTO_ALG_NEED_FALLBACK, + .cra_alignmask = 0, + .cra_blocksize = 1, + .cra_ctxsize = sizeof(struct p8_aes_ctr_ctx), + .cra_init = p8_aes_ctr_init, + .cra_exit = p8_aes_ctr_exit, + .cra_blkcipher = { + .ivsize = 0, + .min_keysize = AES_MIN_KEY_SIZE, + .max_keysize = AES_MAX_KEY_SIZE, + .setkey = p8_aes_ctr_setkey, + .encrypt = p8_aes_ctr_crypt, + .decrypt = p8_aes_ctr_crypt, + }, +}; diff --git a/drivers/crypto/vmx/aesp8-ppc.h b/drivers/crypto/vmx/aesp8-ppc.h new file mode 100644 index 000000000000..e963945a83e1 --- /dev/null +++ b/drivers/crypto/vmx/aesp8-ppc.h @@ -0,0 +1,20 @@ +#include <linux/types.h> +#include <crypto/aes.h> + +#define AES_BLOCK_MASK (~(AES_BLOCK_SIZE-1)) + +struct aes_key { + u8 key[AES_MAX_KEYLENGTH]; + int rounds; +}; + +int aes_p8_set_encrypt_key(const u8 *userKey, const int bits, + struct aes_key *key); +int aes_p8_set_decrypt_key(const u8 *userKey, const int bits, + struct aes_key *key); +void aes_p8_encrypt(const u8 *in, u8 *out, const struct aes_key *key); +void aes_p8_decrypt(const u8 *in, u8 *out,const struct aes_key *key); +void aes_p8_cbc_encrypt(const u8 *in, u8 *out, size_t len, + const struct aes_key *key, u8 *iv, const int enc); +void aes_p8_ctr32_encrypt_blocks(const u8 *in, u8 *out, + size_t len, const struct aes_key *key, const u8 *iv); diff --git a/drivers/crypto/vmx/aesp8-ppc.pl b/drivers/crypto/vmx/aesp8-ppc.pl new file mode 100644 index 000000000000..6c5c20c6108e --- /dev/null +++ b/drivers/crypto/vmx/aesp8-ppc.pl @@ -0,0 +1,1930 @@ +#!/usr/bin/env perl +# +# ==================================================================== +# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# ==================================================================== +# +# This module implements support for AES instructions as per PowerISA +# specification version 2.07, first implemented by POWER8 processor. +# The module is endian-agnostic in sense that it supports both big- +# and little-endian cases. Data alignment in parallelizable modes is +# handled with VSX loads and stores, which implies MSR.VSX flag being +# set. It should also be noted that ISA specification doesn't prohibit +# alignment exceptions for these instructions on page boundaries. +# Initially alignment was handled in pure AltiVec/VMX way [when data +# is aligned programmatically, which in turn guarantees exception- +# free execution], but it turned to hamper performance when vcipher +# instructions are interleaved. It's reckoned that eventual +# misalignment penalties at page boundaries are in average lower +# than additional overhead in pure AltiVec approach. + +$flavour = shift; + +if ($flavour =~ /64/) { + $SIZE_T =8; + $LRSAVE =2*$SIZE_T; + $STU ="stdu"; + $POP ="ld"; + $PUSH ="std"; + $UCMP ="cmpld"; + $SHL ="sldi"; +} elsif ($flavour =~ /32/) { + $SIZE_T =4; + $LRSAVE =$SIZE_T; + $STU ="stwu"; + $POP ="lwz"; + $PUSH ="stw"; + $UCMP ="cmplw"; + $SHL ="slwi"; +} else { die "nonsense $flavour"; } + +$LITTLE_ENDIAN = ($flavour=~/le$/) ? $SIZE_T : 0; + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or +( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or +die "can't locate ppc-xlate.pl"; + +open STDOUT,"| $^X $xlate $flavour ".shift || die "can't call $xlate: $!"; + +$FRAME=8*$SIZE_T; +$prefix="aes_p8"; + +$sp="r1"; +$vrsave="r12"; + +######################################################################### +{{{ # Key setup procedures # +my ($inp,$bits,$out,$ptr,$cnt,$rounds)=map("r$_",(3..8)); +my ($zero,$in0,$in1,$key,$rcon,$mask,$tmp)=map("v$_",(0..6)); +my ($stage,$outperm,$outmask,$outhead,$outtail)=map("v$_",(7..11)); + +$code.=<<___; +.machine "any" + +.text + +.align 7 +rcon: +.long 0x01000000, 0x01000000, 0x01000000, 0x01000000 ?rev +.long 0x1b000000, 0x1b000000, 0x1b000000, 0x1b000000 ?rev +.long 0x0d0e0f0c, 0x0d0e0f0c, 0x0d0e0f0c, 0x0d0e0f0c ?rev +.long 0,0,0,0 ?asis +Lconsts: + mflr r0 + bcl 20,31,\$+4 + mflr $ptr #vvvvv "distance between . and rcon + addi $ptr,$ptr,-0x48 + mtlr r0 + blr + .long 0 + .byte 0,12,0x14,0,0,0,0,0 +.asciz "AES for PowerISA 2.07, CRYPTOGAMS by <appro\@openssl.org>" + +.globl .${prefix}_set_encrypt_key +Lset_encrypt_key: + mflr r11 + $PUSH r11,$LRSAVE($sp) + + li $ptr,-1 + ${UCMP}i $inp,0 + beq- Lenc_key_abort # if ($inp==0) return -1; + ${UCMP}i $out,0 + beq- Lenc_key_abort # if ($out==0) return -1; + li $ptr,-2 + cmpwi $bits,128 + blt- Lenc_key_abort + cmpwi $bits,256 + bgt- Lenc_key_abort + andi. r0,$bits,0x3f + bne- Lenc_key_abort + + lis r0,0xfff0 + mfspr $vrsave,256 + mtspr 256,r0 + + bl Lconsts + mtlr r11 + + neg r9,$inp + lvx $in0,0,$inp + addi $inp,$inp,15 # 15 is not typo + lvsr $key,0,r9 # borrow $key + li r8,0x20 + cmpwi $bits,192 + lvx $in1,0,$inp + le?vspltisb $mask,0x0f # borrow $mask + lvx $rcon,0,$ptr + le?vxor $key,$key,$mask # adjust for byte swap + lvx $mask,r8,$ptr + addi $ptr,$ptr,0x10 + vperm $in0,$in0,$in1,$key # align [and byte swap in LE] + li $cnt,8 + vxor $zero,$zero,$zero + mtctr $cnt + + ?lvsr $outperm,0,$out + vspltisb $outmask,-1 + lvx $outhead,0,$out + ?vperm $outmask,$zero,$outmask,$outperm + + blt Loop128 + addi $inp,$inp,8 + beq L192 + addi $inp,$inp,8 + b L256 + +.align 4 +Loop128: + vperm $key,$in0,$in0,$mask # rotate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vcipherlast $key,$key,$rcon + stvx $stage,0,$out + addi $out,$out,16 + + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vadduwm $rcon,$rcon,$rcon + vxor $in0,$in0,$key + bdnz Loop128 + + lvx $rcon,0,$ptr # last two round keys + + vperm $key,$in0,$in0,$mask # rotate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vcipherlast $key,$key,$rcon + stvx $stage,0,$out + addi $out,$out,16 + + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vadduwm $rcon,$rcon,$rcon + vxor $in0,$in0,$key + + vperm $key,$in0,$in0,$mask # rotate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vcipherlast $key,$key,$rcon + stvx $stage,0,$out + addi $out,$out,16 + + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vxor $in0,$in0,$key + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + stvx $stage,0,$out + + addi $inp,$out,15 # 15 is not typo + addi $out,$out,0x50 + + li $rounds,10 + b Ldone + +.align 4 +L192: + lvx $tmp,0,$inp + li $cnt,4 + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + stvx $stage,0,$out + addi $out,$out,16 + vperm $in1,$in1,$tmp,$key # align [and byte swap in LE] + vspltisb $key,8 # borrow $key + mtctr $cnt + vsububm $mask,$mask,$key # adjust the mask + +Loop192: + vperm $key,$in1,$in1,$mask # roate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vcipherlast $key,$key,$rcon + + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + + vsldoi $stage,$zero,$in1,8 + vspltw $tmp,$in0,3 + vxor $tmp,$tmp,$in1 + vsldoi $in1,$zero,$in1,12 # >>32 + vadduwm $rcon,$rcon,$rcon + vxor $in1,$in1,$tmp + vxor $in0,$in0,$key + vxor $in1,$in1,$key + vsldoi $stage,$stage,$in0,8 + + vperm $key,$in1,$in1,$mask # rotate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vperm $outtail,$stage,$stage,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vcipherlast $key,$key,$rcon + stvx $stage,0,$out + addi $out,$out,16 + + vsldoi $stage,$in0,$in1,8 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vperm $outtail,$stage,$stage,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + stvx $stage,0,$out + addi $out,$out,16 + + vspltw $tmp,$in0,3 + vxor $tmp,$tmp,$in1 + vsldoi $in1,$zero,$in1,12 # >>32 + vadduwm $rcon,$rcon,$rcon + vxor $in1,$in1,$tmp + vxor $in0,$in0,$key + vxor $in1,$in1,$key + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + stvx $stage,0,$out + addi $inp,$out,15 # 15 is not typo + addi $out,$out,16 + bdnz Loop192 + + li $rounds,12 + addi $out,$out,0x20 + b Ldone + +.align 4 +L256: + lvx $tmp,0,$inp + li $cnt,7 + li $rounds,14 + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + stvx $stage,0,$out + addi $out,$out,16 + vperm $in1,$in1,$tmp,$key # align [and byte swap in LE] + mtctr $cnt + +Loop256: + vperm $key,$in1,$in1,$mask # rotate-n-splat + vsldoi $tmp,$zero,$in0,12 # >>32 + vperm $outtail,$in1,$in1,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + vcipherlast $key,$key,$rcon + stvx $stage,0,$out + addi $out,$out,16 + + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in0,$in0,$tmp + vadduwm $rcon,$rcon,$rcon + vxor $in0,$in0,$key + vperm $outtail,$in0,$in0,$outperm # rotate + vsel $stage,$outhead,$outtail,$outmask + vmr $outhead,$outtail + stvx $stage,0,$out + addi $inp,$out,15 # 15 is not typo + addi $out,$out,16 + bdz Ldone + + vspltw $key,$in0,3 # just splat + vsldoi $tmp,$zero,$in1,12 # >>32 + vsbox $key,$key + + vxor $in1,$in1,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in1,$in1,$tmp + vsldoi $tmp,$zero,$tmp,12 # >>32 + vxor $in1,$in1,$tmp + + vxor $in1,$in1,$key + b Loop256 + +.align 4 +Ldone: + lvx $in1,0,$inp # redundant in aligned case + vsel $in1,$outhead,$in1,$outmask + stvx $in1,0,$inp + li $ptr,0 + mtspr 256,$vrsave + stw $rounds,0($out) + +Lenc_key_abort: + mr r3,$ptr + blr + .long 0 + .byte 0,12,0x14,1,0,0,3,0 + .long 0 +.size .${prefix}_set_encrypt_key,.-.${prefix}_set_encrypt_key + +.globl .${prefix}_set_decrypt_key + $STU $sp,-$FRAME($sp) + mflr r10 + $PUSH r10,$FRAME+$LRSAVE($sp) + bl Lset_encrypt_key + mtlr r10 + + cmpwi r3,0 + bne- Ldec_key_abort + + slwi $cnt,$rounds,4 + subi $inp,$out,240 # first round key + srwi $rounds,$rounds,1 + add $out,$inp,$cnt # last round key + mtctr $rounds + +Ldeckey: + lwz r0, 0($inp) + lwz r6, 4($inp) + lwz r7, 8($inp) + lwz r8, 12($inp) + addi $inp,$inp,16 + lwz r9, 0($out) + lwz r10,4($out) + lwz r11,8($out) + lwz r12,12($out) + stw r0, 0($out) + stw r6, 4($out) + stw r7, 8($out) + stw r8, 12($out) + subi $out,$out,16 + stw r9, -16($inp) + stw r10,-12($inp) + stw r11,-8($inp) + stw r12,-4($inp) + bdnz Ldeckey + + xor r3,r3,r3 # return value +Ldec_key_abort: + addi $sp,$sp,$FRAME + blr + .long 0 + .byte 0,12,4,1,0x80,0,3,0 + .long 0 +.size .${prefix}_set_decrypt_key,.-.${prefix}_set_decrypt_key +___ +}}} +######################################################################### +{{{ # Single block en- and decrypt procedures # +sub gen_block () { +my $dir = shift; +my $n = $dir eq "de" ? "n" : ""; +my ($inp,$out,$key,$rounds,$idx)=map("r$_",(3..7)); + +$code.=<<___; +.globl .${prefix}_${dir}crypt + lwz $rounds,240($key) + lis r0,0xfc00 + mfspr $vrsave,256 + li $idx,15 # 15 is not typo + mtspr 256,r0 + + lvx v0,0,$inp + neg r11,$out + lvx v1,$idx,$inp + lvsl v2,0,$inp # inpperm + le?vspltisb v4,0x0f + ?lvsl v3,0,r11 # outperm + le?vxor v2,v2,v4 + li $idx,16 + vperm v0,v0,v1,v2 # align [and byte swap in LE] + lvx v1,0,$key + ?lvsl v5,0,$key # keyperm + srwi $rounds,$rounds,1 + lvx v2,$idx,$key + addi $idx,$idx,16 + subi $rounds,$rounds,1 + ?vperm v1,v1,v2,v5 # align round key + + vxor v0,v0,v1 + lvx v1,$idx,$key + addi $idx,$idx,16 + mtctr $rounds + +Loop_${dir}c: + ?vperm v2,v2,v1,v5 + v${n}cipher v0,v0,v2 + lvx v2,$idx,$key + addi $idx,$idx,16 + ?vperm v1,v1,v2,v5 + v${n}cipher v0,v0,v1 + lvx v1,$idx,$key + addi $idx,$idx,16 + bdnz Loop_${dir}c + + ?vperm v2,v2,v1,v5 + v${n}cipher v0,v0,v2 + lvx v2,$idx,$key + ?vperm v1,v1,v2,v5 + v${n}cipherlast v0,v0,v1 + + vspltisb v2,-1 + vxor v1,v1,v1 + li $idx,15 # 15 is not typo + ?vperm v2,v1,v2,v3 # outmask + le?vxor v3,v3,v4 + lvx v1,0,$out # outhead + vperm v0,v0,v0,v3 # rotate [and byte swap in LE] + vsel v1,v1,v0,v2 + lvx v4,$idx,$out + stvx v1,0,$out + vsel v0,v0,v4,v2 + stvx v0,$idx,$out + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,3,0 + .long 0 +.size .${prefix}_${dir}crypt,.-.${prefix}_${dir}crypt +___ +} +&gen_block("en"); +&gen_block("de"); +}}} +######################################################################### +{{{ # CBC en- and decrypt procedures # +my ($inp,$out,$len,$key,$ivp,$enc,$rounds,$idx)=map("r$_",(3..10)); +my ($rndkey0,$rndkey1,$inout,$tmp)= map("v$_",(0..3)); +my ($ivec,$inptail,$inpperm,$outhead,$outperm,$outmask,$keyperm)= + map("v$_",(4..10)); +$code.=<<___; +.globl .${prefix}_cbc_encrypt + ${UCMP}i $len,16 + bltlr- + + cmpwi $enc,0 # test direction + lis r0,0xffe0 + mfspr $vrsave,256 + mtspr 256,r0 + + li $idx,15 + vxor $rndkey0,$rndkey0,$rndkey0 + le?vspltisb $tmp,0x0f + + lvx $ivec,0,$ivp # load [unaligned] iv + lvsl $inpperm,0,$ivp + lvx $inptail,$idx,$ivp + le?vxor $inpperm,$inpperm,$tmp + vperm $ivec,$ivec,$inptail,$inpperm + + neg r11,$inp + ?lvsl $keyperm,0,$key # prepare for unaligned key + lwz $rounds,240($key) + + lvsr $inpperm,0,r11 # prepare for unaligned load + lvx $inptail,0,$inp + addi $inp,$inp,15 # 15 is not typo + le?vxor $inpperm,$inpperm,$tmp + + ?lvsr $outperm,0,$out # prepare for unaligned store + vspltisb $outmask,-1 + lvx $outhead,0,$out + ?vperm $outmask,$rndkey0,$outmask,$outperm + le?vxor $outperm,$outperm,$tmp + + srwi $rounds,$rounds,1 + li $idx,16 + subi $rounds,$rounds,1 + beq Lcbc_dec + +Lcbc_enc: + vmr $inout,$inptail + lvx $inptail,0,$inp + addi $inp,$inp,16 + mtctr $rounds + subi $len,$len,16 # len-=16 + + lvx $rndkey0,0,$key + vperm $inout,$inout,$inptail,$inpperm + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vxor $inout,$inout,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + vxor $inout,$inout,$ivec + +Loop_cbc_enc: + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vcipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vcipher $inout,$inout,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + bdnz Loop_cbc_enc + + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vcipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + li $idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vcipherlast $ivec,$inout,$rndkey0 + ${UCMP}i $len,16 + + vperm $tmp,$ivec,$ivec,$outperm + vsel $inout,$outhead,$tmp,$outmask + vmr $outhead,$tmp + stvx $inout,0,$out + addi $out,$out,16 + bge Lcbc_enc + + b Lcbc_done + +.align 4 +Lcbc_dec: + ${UCMP}i $len,128 + bge _aesp8_cbc_decrypt8x + vmr $tmp,$inptail + lvx $inptail,0,$inp + addi $inp,$inp,16 + mtctr $rounds + subi $len,$len,16 # len-=16 + + lvx $rndkey0,0,$key + vperm $tmp,$tmp,$inptail,$inpperm + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vxor $inout,$tmp,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + +Loop_cbc_dec: + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vncipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vncipher $inout,$inout,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + bdnz Loop_cbc_dec + + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vncipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + li $idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vncipherlast $inout,$inout,$rndkey0 + ${UCMP}i $len,16 + + vxor $inout,$inout,$ivec + vmr $ivec,$tmp + vperm $tmp,$inout,$inout,$outperm + vsel $inout,$outhead,$tmp,$outmask + vmr $outhead,$tmp + stvx $inout,0,$out + addi $out,$out,16 + bge Lcbc_dec + +Lcbc_done: + addi $out,$out,-1 + lvx $inout,0,$out # redundant in aligned case + vsel $inout,$outhead,$inout,$outmask + stvx $inout,0,$out + + neg $enc,$ivp # write [unaligned] iv + li $idx,15 # 15 is not typo + vxor $rndkey0,$rndkey0,$rndkey0 + vspltisb $outmask,-1 + le?vspltisb $tmp,0x0f + ?lvsl $outperm,0,$enc + ?vperm $outmask,$rndkey0,$outmask,$outperm + le?vxor $outperm,$outperm,$tmp + lvx $outhead,0,$ivp + vperm $ivec,$ivec,$ivec,$outperm + vsel $inout,$outhead,$ivec,$outmask + lvx $inptail,$idx,$ivp + stvx $inout,0,$ivp + vsel $inout,$ivec,$inptail,$outmask + stvx $inout,$idx,$ivp + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,6,0 + .long 0 +___ +######################################################################### +{{ # Optimized CBC decrypt procedure # +my $key_="r11"; +my ($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,8,26..31)); +my ($in0, $in1, $in2, $in3, $in4, $in5, $in6, $in7 )=map("v$_",(0..3,10..13)); +my ($out0,$out1,$out2,$out3,$out4,$out5,$out6,$out7)=map("v$_",(14..21)); +my $rndkey0="v23"; # v24-v25 rotating buffer for first found keys + # v26-v31 last 6 round keys +my ($tmp,$keyperm)=($in3,$in4); # aliases with "caller", redundant assignment + +$code.=<<___; +.align 5 +_aesp8_cbc_decrypt8x: + $STU $sp,-`($FRAME+21*16+6*$SIZE_T)`($sp) + li r10,`$FRAME+8*16+15` + li r11,`$FRAME+8*16+31` + stvx v20,r10,$sp # ABI says so + addi r10,r10,32 + stvx v21,r11,$sp + addi r11,r11,32 + stvx v22,r10,$sp + addi r10,r10,32 + stvx v23,r11,$sp + addi r11,r11,32 + stvx v24,r10,$sp + addi r10,r10,32 + stvx v25,r11,$sp + addi r11,r11,32 + stvx v26,r10,$sp + addi r10,r10,32 + stvx v27,r11,$sp + addi r11,r11,32 + stvx v28,r10,$sp + addi r10,r10,32 + stvx v29,r11,$sp + addi r11,r11,32 + stvx v30,r10,$sp + stvx v31,r11,$sp + li r0,-1 + stw $vrsave,`$FRAME+21*16-4`($sp) # save vrsave + li $x10,0x10 + $PUSH r26,`$FRAME+21*16+0*$SIZE_T`($sp) + li $x20,0x20 + $PUSH r27,`$FRAME+21*16+1*$SIZE_T`($sp) + li $x30,0x30 + $PUSH r28,`$FRAME+21*16+2*$SIZE_T`($sp) + li $x40,0x40 + $PUSH r29,`$FRAME+21*16+3*$SIZE_T`($sp) + li $x50,0x50 + $PUSH r30,`$FRAME+21*16+4*$SIZE_T`($sp) + li $x60,0x60 + $PUSH r31,`$FRAME+21*16+5*$SIZE_T`($sp) + li $x70,0x70 + mtspr 256,r0 + + subi $rounds,$rounds,3 # -4 in total + subi $len,$len,128 # bias + + lvx $rndkey0,$x00,$key # load key schedule + lvx v30,$x10,$key + addi $key,$key,0x20 + lvx v31,$x00,$key + ?vperm $rndkey0,$rndkey0,v30,$keyperm + addi $key_,$sp,$FRAME+15 + mtctr $rounds + +Load_cbc_dec_key: + ?vperm v24,v30,v31,$keyperm + lvx v30,$x10,$key + addi $key,$key,0x20 + stvx v24,$x00,$key_ # off-load round[1] + ?vperm v25,v31,v30,$keyperm + lvx v31,$x00,$key + stvx v25,$x10,$key_ # off-load round[2] + addi $key_,$key_,0x20 + bdnz Load_cbc_dec_key + + lvx v26,$x10,$key + ?vperm v24,v30,v31,$keyperm + lvx v27,$x20,$key + stvx v24,$x00,$key_ # off-load round[3] + ?vperm v25,v31,v26,$keyperm + lvx v28,$x30,$key + stvx v25,$x10,$key_ # off-load round[4] + addi $key_,$sp,$FRAME+15 # rewind $key_ + ?vperm v26,v26,v27,$keyperm + lvx v29,$x40,$key + ?vperm v27,v27,v28,$keyperm + lvx v30,$x50,$key + ?vperm v28,v28,v29,$keyperm + lvx v31,$x60,$key + ?vperm v29,v29,v30,$keyperm + lvx $out0,$x70,$key # borrow $out0 + ?vperm v30,v30,v31,$keyperm + lvx v24,$x00,$key_ # pre-load round[1] + ?vperm v31,v31,$out0,$keyperm + lvx v25,$x10,$key_ # pre-load round[2] + + #lvx $inptail,0,$inp # "caller" already did this + #addi $inp,$inp,15 # 15 is not typo + subi $inp,$inp,15 # undo "caller" + + le?li $idx,8 + lvx_u $in0,$x00,$inp # load first 8 "words" + le?lvsl $inpperm,0,$idx + le?vspltisb $tmp,0x0f + lvx_u $in1,$x10,$inp + le?vxor $inpperm,$inpperm,$tmp # transform for lvx_u/stvx_u + lvx_u $in2,$x20,$inp + le?vperm $in0,$in0,$in0,$inpperm + lvx_u $in3,$x30,$inp + le?vperm $in1,$in1,$in1,$inpperm + lvx_u $in4,$x40,$inp + le?vperm $in2,$in2,$in2,$inpperm + vxor $out0,$in0,$rndkey0 + lvx_u $in5,$x50,$inp + le?vperm $in3,$in3,$in3,$inpperm + vxor $out1,$in1,$rndkey0 + lvx_u $in6,$x60,$inp + le?vperm $in4,$in4,$in4,$inpperm + vxor $out2,$in2,$rndkey0 + lvx_u $in7,$x70,$inp + addi $inp,$inp,0x80 + le?vperm $in5,$in5,$in5,$inpperm + vxor $out3,$in3,$rndkey0 + le?vperm $in6,$in6,$in6,$inpperm + vxor $out4,$in4,$rndkey0 + le?vperm $in7,$in7,$in7,$inpperm + vxor $out5,$in5,$rndkey0 + vxor $out6,$in6,$rndkey0 + vxor $out7,$in7,$rndkey0 + + mtctr $rounds + b Loop_cbc_dec8x +.align 5 +Loop_cbc_dec8x: + vncipher $out0,$out0,v24 + vncipher $out1,$out1,v24 + vncipher $out2,$out2,v24 + vncipher $out3,$out3,v24 + vncipher $out4,$out4,v24 + vncipher $out5,$out5,v24 + vncipher $out6,$out6,v24 + vncipher $out7,$out7,v24 + lvx v24,$x20,$key_ # round[3] + addi $key_,$key_,0x20 + + vncipher $out0,$out0,v25 + vncipher $out1,$out1,v25 + vncipher $out2,$out2,v25 + vncipher $out3,$out3,v25 + vncipher $out4,$out4,v25 + vncipher $out5,$out5,v25 + vncipher $out6,$out6,v25 + vncipher $out7,$out7,v25 + lvx v25,$x10,$key_ # round[4] + bdnz Loop_cbc_dec8x + + subic $len,$len,128 # $len-=128 + vncipher $out0,$out0,v24 + vncipher $out1,$out1,v24 + vncipher $out2,$out2,v24 + vncipher $out3,$out3,v24 + vncipher $out4,$out4,v24 + vncipher $out5,$out5,v24 + vncipher $out6,$out6,v24 + vncipher $out7,$out7,v24 + + subfe. r0,r0,r0 # borrow?-1:0 + vncipher $out0,$out0,v25 + vncipher $out1,$out1,v25 + vncipher $out2,$out2,v25 + vncipher $out3,$out3,v25 + vncipher $out4,$out4,v25 + vncipher $out5,$out5,v25 + vncipher $out6,$out6,v25 + vncipher $out7,$out7,v25 + + and r0,r0,$len + vncipher $out0,$out0,v26 + vncipher $out1,$out1,v26 + vncipher $out2,$out2,v26 + vncipher $out3,$out3,v26 + vncipher $out4,$out4,v26 + vncipher $out5,$out5,v26 + vncipher $out6,$out6,v26 + vncipher $out7,$out7,v26 + + add $inp,$inp,r0 # $inp is adjusted in such + # way that at exit from the + # loop inX-in7 are loaded + # with last "words" + vncipher $out0,$out0,v27 + vncipher $out1,$out1,v27 + vncipher $out2,$out2,v27 + vncipher $out3,$out3,v27 + vncipher $out4,$out4,v27 + vncipher $out5,$out5,v27 + vncipher $out6,$out6,v27 + vncipher $out7,$out7,v27 + + addi $key_,$sp,$FRAME+15 # rewind $key_ + vncipher $out0,$out0,v28 + vncipher $out1,$out1,v28 + vncipher $out2,$out2,v28 + vncipher $out3,$out3,v28 + vncipher $out4,$out4,v28 + vncipher $out5,$out5,v28 + vncipher $out6,$out6,v28 + vncipher $out7,$out7,v28 + lvx v24,$x00,$key_ # re-pre-load round[1] + + vncipher $out0,$out0,v29 + vncipher $out1,$out1,v29 + vncipher $out2,$out2,v29 + vncipher $out3,$out3,v29 + vncipher $out4,$out4,v29 + vncipher $out5,$out5,v29 + vncipher $out6,$out6,v29 + vncipher $out7,$out7,v29 + lvx v25,$x10,$key_ # re-pre-load round[2] + + vncipher $out0,$out0,v30 + vxor $ivec,$ivec,v31 # xor with last round key + vncipher $out1,$out1,v30 + vxor $in0,$in0,v31 + vncipher $out2,$out2,v30 + vxor $in1,$in1,v31 + vncipher $out3,$out3,v30 + vxor $in2,$in2,v31 + vncipher $out4,$out4,v30 + vxor $in3,$in3,v31 + vncipher $out5,$out5,v30 + vxor $in4,$in4,v31 + vncipher $out6,$out6,v30 + vxor $in5,$in5,v31 + vncipher $out7,$out7,v30 + vxor $in6,$in6,v31 + + vncipherlast $out0,$out0,$ivec + vncipherlast $out1,$out1,$in0 + lvx_u $in0,$x00,$inp # load next input block + vncipherlast $out2,$out2,$in1 + lvx_u $in1,$x10,$inp + vncipherlast $out3,$out3,$in2 + le?vperm $in0,$in0,$in0,$inpperm + lvx_u $in2,$x20,$inp + vncipherlast $out4,$out4,$in3 + le?vperm $in1,$in1,$in1,$inpperm + lvx_u $in3,$x30,$inp + vncipherlast $out5,$out5,$in4 + le?vperm $in2,$in2,$in2,$inpperm + lvx_u $in4,$x40,$inp + vncipherlast $out6,$out6,$in5 + le?vperm $in3,$in3,$in3,$inpperm + lvx_u $in5,$x50,$inp + vncipherlast $out7,$out7,$in6 + le?vperm $in4,$in4,$in4,$inpperm + lvx_u $in6,$x60,$inp + vmr $ivec,$in7 + le?vperm $in5,$in5,$in5,$inpperm + lvx_u $in7,$x70,$inp + addi $inp,$inp,0x80 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $in6,$in6,$in6,$inpperm + vxor $out0,$in0,$rndkey0 + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $in7,$in7,$in7,$inpperm + vxor $out1,$in1,$rndkey0 + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + vxor $out2,$in2,$rndkey0 + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x30,$out + vxor $out3,$in3,$rndkey0 + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x40,$out + vxor $out4,$in4,$rndkey0 + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x50,$out + vxor $out5,$in5,$rndkey0 + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x60,$out + vxor $out6,$in6,$rndkey0 + stvx_u $out7,$x70,$out + addi $out,$out,0x80 + vxor $out7,$in7,$rndkey0 + + mtctr $rounds + beq Loop_cbc_dec8x # did $len-=128 borrow? + + addic. $len,$len,128 + beq Lcbc_dec8x_done + nop + nop + +Loop_cbc_dec8x_tail: # up to 7 "words" tail... + vncipher $out1,$out1,v24 + vncipher $out2,$out2,v24 + vncipher $out3,$out3,v24 + vncipher $out4,$out4,v24 + vncipher $out5,$out5,v24 + vncipher $out6,$out6,v24 + vncipher $out7,$out7,v24 + lvx v24,$x20,$key_ # round[3] + addi $key_,$key_,0x20 + + vncipher $out1,$out1,v25 + vncipher $out2,$out2,v25 + vncipher $out3,$out3,v25 + vncipher $out4,$out4,v25 + vncipher $out5,$out5,v25 + vncipher $out6,$out6,v25 + vncipher $out7,$out7,v25 + lvx v25,$x10,$key_ # round[4] + bdnz Loop_cbc_dec8x_tail + + vncipher $out1,$out1,v24 + vncipher $out2,$out2,v24 + vncipher $out3,$out3,v24 + vncipher $out4,$out4,v24 + vncipher $out5,$out5,v24 + vncipher $out6,$out6,v24 + vncipher $out7,$out7,v24 + + vncipher $out1,$out1,v25 + vncipher $out2,$out2,v25 + vncipher $out3,$out3,v25 + vncipher $out4,$out4,v25 + vncipher $out5,$out5,v25 + vncipher $out6,$out6,v25 + vncipher $out7,$out7,v25 + + vncipher $out1,$out1,v26 + vncipher $out2,$out2,v26 + vncipher $out3,$out3,v26 + vncipher $out4,$out4,v26 + vncipher $out5,$out5,v26 + vncipher $out6,$out6,v26 + vncipher $out7,$out7,v26 + + vncipher $out1,$out1,v27 + vncipher $out2,$out2,v27 + vncipher $out3,$out3,v27 + vncipher $out4,$out4,v27 + vncipher $out5,$out5,v27 + vncipher $out6,$out6,v27 + vncipher $out7,$out7,v27 + + vncipher $out1,$out1,v28 + vncipher $out2,$out2,v28 + vncipher $out3,$out3,v28 + vncipher $out4,$out4,v28 + vncipher $out5,$out5,v28 + vncipher $out6,$out6,v28 + vncipher $out7,$out7,v28 + + vncipher $out1,$out1,v29 + vncipher $out2,$out2,v29 + vncipher $out3,$out3,v29 + vncipher $out4,$out4,v29 + vncipher $out5,$out5,v29 + vncipher $out6,$out6,v29 + vncipher $out7,$out7,v29 + + vncipher $out1,$out1,v30 + vxor $ivec,$ivec,v31 # last round key + vncipher $out2,$out2,v30 + vxor $in1,$in1,v31 + vncipher $out3,$out3,v30 + vxor $in2,$in2,v31 + vncipher $out4,$out4,v30 + vxor $in3,$in3,v31 + vncipher $out5,$out5,v30 + vxor $in4,$in4,v31 + vncipher $out6,$out6,v30 + vxor $in5,$in5,v31 + vncipher $out7,$out7,v30 + vxor $in6,$in6,v31 + + cmplwi $len,32 # switch($len) + blt Lcbc_dec8x_one + nop + beq Lcbc_dec8x_two + cmplwi $len,64 + blt Lcbc_dec8x_three + nop + beq Lcbc_dec8x_four + cmplwi $len,96 + blt Lcbc_dec8x_five + nop + beq Lcbc_dec8x_six + +Lcbc_dec8x_seven: + vncipherlast $out1,$out1,$ivec + vncipherlast $out2,$out2,$in1 + vncipherlast $out3,$out3,$in2 + vncipherlast $out4,$out4,$in3 + vncipherlast $out5,$out5,$in4 + vncipherlast $out6,$out6,$in5 + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out1,$out1,$out1,$inpperm + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x00,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x10,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x20,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x30,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x40,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x50,$out + stvx_u $out7,$x60,$out + addi $out,$out,0x70 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_six: + vncipherlast $out2,$out2,$ivec + vncipherlast $out3,$out3,$in2 + vncipherlast $out4,$out4,$in3 + vncipherlast $out5,$out5,$in4 + vncipherlast $out6,$out6,$in5 + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out2,$out2,$out2,$inpperm + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x00,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x10,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x20,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x30,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x40,$out + stvx_u $out7,$x50,$out + addi $out,$out,0x60 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_five: + vncipherlast $out3,$out3,$ivec + vncipherlast $out4,$out4,$in3 + vncipherlast $out5,$out5,$in4 + vncipherlast $out6,$out6,$in5 + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out3,$out3,$out3,$inpperm + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x00,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x10,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x20,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x30,$out + stvx_u $out7,$x40,$out + addi $out,$out,0x50 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_four: + vncipherlast $out4,$out4,$ivec + vncipherlast $out5,$out5,$in4 + vncipherlast $out6,$out6,$in5 + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out4,$out4,$out4,$inpperm + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x00,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x10,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x20,$out + stvx_u $out7,$x30,$out + addi $out,$out,0x40 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_three: + vncipherlast $out5,$out5,$ivec + vncipherlast $out6,$out6,$in5 + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out5,$out5,$out5,$inpperm + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x00,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x10,$out + stvx_u $out7,$x20,$out + addi $out,$out,0x30 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_two: + vncipherlast $out6,$out6,$ivec + vncipherlast $out7,$out7,$in6 + vmr $ivec,$in7 + + le?vperm $out6,$out6,$out6,$inpperm + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x00,$out + stvx_u $out7,$x10,$out + addi $out,$out,0x20 + b Lcbc_dec8x_done + +.align 5 +Lcbc_dec8x_one: + vncipherlast $out7,$out7,$ivec + vmr $ivec,$in7 + + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out7,0,$out + addi $out,$out,0x10 + +Lcbc_dec8x_done: + le?vperm $ivec,$ivec,$ivec,$inpperm + stvx_u $ivec,0,$ivp # write [unaligned] iv + + li r10,`$FRAME+15` + li r11,`$FRAME+31` + stvx $inpperm,r10,$sp # wipe copies of round keys + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + + mtspr 256,$vrsave + lvx v20,r10,$sp # ABI says so + addi r10,r10,32 + lvx v21,r11,$sp + addi r11,r11,32 + lvx v22,r10,$sp + addi r10,r10,32 + lvx v23,r11,$sp + addi r11,r11,32 + lvx v24,r10,$sp + addi r10,r10,32 + lvx v25,r11,$sp + addi r11,r11,32 + lvx v26,r10,$sp + addi r10,r10,32 + lvx v27,r11,$sp + addi r11,r11,32 + lvx v28,r10,$sp + addi r10,r10,32 + lvx v29,r11,$sp + addi r11,r11,32 + lvx v30,r10,$sp + lvx v31,r11,$sp + $POP r26,`$FRAME+21*16+0*$SIZE_T`($sp) + $POP r27,`$FRAME+21*16+1*$SIZE_T`($sp) + $POP r28,`$FRAME+21*16+2*$SIZE_T`($sp) + $POP r29,`$FRAME+21*16+3*$SIZE_T`($sp) + $POP r30,`$FRAME+21*16+4*$SIZE_T`($sp) + $POP r31,`$FRAME+21*16+5*$SIZE_T`($sp) + addi $sp,$sp,`$FRAME+21*16+6*$SIZE_T` + blr + .long 0 + .byte 0,12,0x14,0,0x80,6,6,0 + .long 0 +.size .${prefix}_cbc_encrypt,.-.${prefix}_cbc_encrypt +___ +}} }}} + +######################################################################### +{{{ # CTR procedure[s] # +my ($inp,$out,$len,$key,$ivp,$x10,$rounds,$idx)=map("r$_",(3..10)); +my ($rndkey0,$rndkey1,$inout,$tmp)= map("v$_",(0..3)); +my ($ivec,$inptail,$inpperm,$outhead,$outperm,$outmask,$keyperm,$one)= + map("v$_",(4..11)); +my $dat=$tmp; + +$code.=<<___; +.globl .${prefix}_ctr32_encrypt_blocks + ${UCMP}i $len,1 + bltlr- + + lis r0,0xfff0 + mfspr $vrsave,256 + mtspr 256,r0 + + li $idx,15 + vxor $rndkey0,$rndkey0,$rndkey0 + le?vspltisb $tmp,0x0f + + lvx $ivec,0,$ivp # load [unaligned] iv + lvsl $inpperm,0,$ivp + lvx $inptail,$idx,$ivp + vspltisb $one,1 + le?vxor $inpperm,$inpperm,$tmp + vperm $ivec,$ivec,$inptail,$inpperm + vsldoi $one,$rndkey0,$one,1 + + neg r11,$inp + ?lvsl $keyperm,0,$key # prepare for unaligned key + lwz $rounds,240($key) + + lvsr $inpperm,0,r11 # prepare for unaligned load + lvx $inptail,0,$inp + addi $inp,$inp,15 # 15 is not typo + le?vxor $inpperm,$inpperm,$tmp + + srwi $rounds,$rounds,1 + li $idx,16 + subi $rounds,$rounds,1 + + ${UCMP}i $len,8 + bge _aesp8_ctr32_encrypt8x + + ?lvsr $outperm,0,$out # prepare for unaligned store + vspltisb $outmask,-1 + lvx $outhead,0,$out + ?vperm $outmask,$rndkey0,$outmask,$outperm + le?vxor $outperm,$outperm,$tmp + + lvx $rndkey0,0,$key + mtctr $rounds + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vxor $inout,$ivec,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + b Loop_ctr32_enc + +.align 5 +Loop_ctr32_enc: + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vcipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vcipher $inout,$inout,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + bdnz Loop_ctr32_enc + + vadduwm $ivec,$ivec,$one + vmr $dat,$inptail + lvx $inptail,0,$inp + addi $inp,$inp,16 + subic. $len,$len,1 # blocks-- + + ?vperm $rndkey1,$rndkey1,$rndkey0,$keyperm + vcipher $inout,$inout,$rndkey1 + lvx $rndkey1,$idx,$key + vperm $dat,$dat,$inptail,$inpperm + li $idx,16 + ?vperm $rndkey1,$rndkey0,$rndkey1,$keyperm + lvx $rndkey0,0,$key + vxor $dat,$dat,$rndkey1 # last round key + vcipherlast $inout,$inout,$dat + + lvx $rndkey1,$idx,$key + addi $idx,$idx,16 + vperm $inout,$inout,$inout,$outperm + vsel $dat,$outhead,$inout,$outmask + mtctr $rounds + ?vperm $rndkey0,$rndkey0,$rndkey1,$keyperm + vmr $outhead,$inout + vxor $inout,$ivec,$rndkey0 + lvx $rndkey0,$idx,$key + addi $idx,$idx,16 + stvx $dat,0,$out + addi $out,$out,16 + bne Loop_ctr32_enc + + addi $out,$out,-1 + lvx $inout,0,$out # redundant in aligned case + vsel $inout,$outhead,$inout,$outmask + stvx $inout,0,$out + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,6,0 + .long 0 +___ +######################################################################### +{{ # Optimized CTR procedure # +my $key_="r11"; +my ($x00,$x10,$x20,$x30,$x40,$x50,$x60,$x70)=map("r$_",(0,8,26..31)); +my ($in0, $in1, $in2, $in3, $in4, $in5, $in6, $in7 )=map("v$_",(0..3,10,12..14)); +my ($out0,$out1,$out2,$out3,$out4,$out5,$out6,$out7)=map("v$_",(15..22)); +my $rndkey0="v23"; # v24-v25 rotating buffer for first found keys + # v26-v31 last 6 round keys +my ($tmp,$keyperm)=($in3,$in4); # aliases with "caller", redundant assignment +my ($two,$three,$four)=($outhead,$outperm,$outmask); + +$code.=<<___; +.align 5 +_aesp8_ctr32_encrypt8x: + $STU $sp,-`($FRAME+21*16+6*$SIZE_T)`($sp) + li r10,`$FRAME+8*16+15` + li r11,`$FRAME+8*16+31` + stvx v20,r10,$sp # ABI says so + addi r10,r10,32 + stvx v21,r11,$sp + addi r11,r11,32 + stvx v22,r10,$sp + addi r10,r10,32 + stvx v23,r11,$sp + addi r11,r11,32 + stvx v24,r10,$sp + addi r10,r10,32 + stvx v25,r11,$sp + addi r11,r11,32 + stvx v26,r10,$sp + addi r10,r10,32 + stvx v27,r11,$sp + addi r11,r11,32 + stvx v28,r10,$sp + addi r10,r10,32 + stvx v29,r11,$sp + addi r11,r11,32 + stvx v30,r10,$sp + stvx v31,r11,$sp + li r0,-1 + stw $vrsave,`$FRAME+21*16-4`($sp) # save vrsave + li $x10,0x10 + $PUSH r26,`$FRAME+21*16+0*$SIZE_T`($sp) + li $x20,0x20 + $PUSH r27,`$FRAME+21*16+1*$SIZE_T`($sp) + li $x30,0x30 + $PUSH r28,`$FRAME+21*16+2*$SIZE_T`($sp) + li $x40,0x40 + $PUSH r29,`$FRAME+21*16+3*$SIZE_T`($sp) + li $x50,0x50 + $PUSH r30,`$FRAME+21*16+4*$SIZE_T`($sp) + li $x60,0x60 + $PUSH r31,`$FRAME+21*16+5*$SIZE_T`($sp) + li $x70,0x70 + mtspr 256,r0 + + subi $rounds,$rounds,3 # -4 in total + + lvx $rndkey0,$x00,$key # load key schedule + lvx v30,$x10,$key + addi $key,$key,0x20 + lvx v31,$x00,$key + ?vperm $rndkey0,$rndkey0,v30,$keyperm + addi $key_,$sp,$FRAME+15 + mtctr $rounds + +Load_ctr32_enc_key: + ?vperm v24,v30,v31,$keyperm + lvx v30,$x10,$key + addi $key,$key,0x20 + stvx v24,$x00,$key_ # off-load round[1] + ?vperm v25,v31,v30,$keyperm + lvx v31,$x00,$key + stvx v25,$x10,$key_ # off-load round[2] + addi $key_,$key_,0x20 + bdnz Load_ctr32_enc_key + + lvx v26,$x10,$key + ?vperm v24,v30,v31,$keyperm + lvx v27,$x20,$key + stvx v24,$x00,$key_ # off-load round[3] + ?vperm v25,v31,v26,$keyperm + lvx v28,$x30,$key + stvx v25,$x10,$key_ # off-load round[4] + addi $key_,$sp,$FRAME+15 # rewind $key_ + ?vperm v26,v26,v27,$keyperm + lvx v29,$x40,$key + ?vperm v27,v27,v28,$keyperm + lvx v30,$x50,$key + ?vperm v28,v28,v29,$keyperm + lvx v31,$x60,$key + ?vperm v29,v29,v30,$keyperm + lvx $out0,$x70,$key # borrow $out0 + ?vperm v30,v30,v31,$keyperm + lvx v24,$x00,$key_ # pre-load round[1] + ?vperm v31,v31,$out0,$keyperm + lvx v25,$x10,$key_ # pre-load round[2] + + vadduwm $two,$one,$one + subi $inp,$inp,15 # undo "caller" + $SHL $len,$len,4 + + vadduwm $out1,$ivec,$one # counter values ... + vadduwm $out2,$ivec,$two + vxor $out0,$ivec,$rndkey0 # ... xored with rndkey[0] + le?li $idx,8 + vadduwm $out3,$out1,$two + vxor $out1,$out1,$rndkey0 + le?lvsl $inpperm,0,$idx + vadduwm $out4,$out2,$two + vxor $out2,$out2,$rndkey0 + le?vspltisb $tmp,0x0f + vadduwm $out5,$out3,$two + vxor $out3,$out3,$rndkey0 + le?vxor $inpperm,$inpperm,$tmp # transform for lvx_u/stvx_u + vadduwm $out6,$out4,$two + vxor $out4,$out4,$rndkey0 + vadduwm $out7,$out5,$two + vxor $out5,$out5,$rndkey0 + vadduwm $ivec,$out6,$two # next counter value + vxor $out6,$out6,$rndkey0 + vxor $out7,$out7,$rndkey0 + + mtctr $rounds + b Loop_ctr32_enc8x +.align 5 +Loop_ctr32_enc8x: + vcipher $out0,$out0,v24 + vcipher $out1,$out1,v24 + vcipher $out2,$out2,v24 + vcipher $out3,$out3,v24 + vcipher $out4,$out4,v24 + vcipher $out5,$out5,v24 + vcipher $out6,$out6,v24 + vcipher $out7,$out7,v24 +Loop_ctr32_enc8x_middle: + lvx v24,$x20,$key_ # round[3] + addi $key_,$key_,0x20 + + vcipher $out0,$out0,v25 + vcipher $out1,$out1,v25 + vcipher $out2,$out2,v25 + vcipher $out3,$out3,v25 + vcipher $out4,$out4,v25 + vcipher $out5,$out5,v25 + vcipher $out6,$out6,v25 + vcipher $out7,$out7,v25 + lvx v25,$x10,$key_ # round[4] + bdnz Loop_ctr32_enc8x + + subic r11,$len,256 # $len-256, borrow $key_ + vcipher $out0,$out0,v24 + vcipher $out1,$out1,v24 + vcipher $out2,$out2,v24 + vcipher $out3,$out3,v24 + vcipher $out4,$out4,v24 + vcipher $out5,$out5,v24 + vcipher $out6,$out6,v24 + vcipher $out7,$out7,v24 + + subfe r0,r0,r0 # borrow?-1:0 + vcipher $out0,$out0,v25 + vcipher $out1,$out1,v25 + vcipher $out2,$out2,v25 + vcipher $out3,$out3,v25 + vcipher $out4,$out4,v25 + vcipher $out5,$out5,v25 + vcipher $out6,$out6,v25 + vcipher $out7,$out7,v25 + + and r0,r0,r11 + addi $key_,$sp,$FRAME+15 # rewind $key_ + vcipher $out0,$out0,v26 + vcipher $out1,$out1,v26 + vcipher $out2,$out2,v26 + vcipher $out3,$out3,v26 + vcipher $out4,$out4,v26 + vcipher $out5,$out5,v26 + vcipher $out6,$out6,v26 + vcipher $out7,$out7,v26 + lvx v24,$x00,$key_ # re-pre-load round[1] + + subic $len,$len,129 # $len-=129 + vcipher $out0,$out0,v27 + addi $len,$len,1 # $len-=128 really + vcipher $out1,$out1,v27 + vcipher $out2,$out2,v27 + vcipher $out3,$out3,v27 + vcipher $out4,$out4,v27 + vcipher $out5,$out5,v27 + vcipher $out6,$out6,v27 + vcipher $out7,$out7,v27 + lvx v25,$x10,$key_ # re-pre-load round[2] + + vcipher $out0,$out0,v28 + lvx_u $in0,$x00,$inp # load input + vcipher $out1,$out1,v28 + lvx_u $in1,$x10,$inp + vcipher $out2,$out2,v28 + lvx_u $in2,$x20,$inp + vcipher $out3,$out3,v28 + lvx_u $in3,$x30,$inp + vcipher $out4,$out4,v28 + lvx_u $in4,$x40,$inp + vcipher $out5,$out5,v28 + lvx_u $in5,$x50,$inp + vcipher $out6,$out6,v28 + lvx_u $in6,$x60,$inp + vcipher $out7,$out7,v28 + lvx_u $in7,$x70,$inp + addi $inp,$inp,0x80 + + vcipher $out0,$out0,v29 + le?vperm $in0,$in0,$in0,$inpperm + vcipher $out1,$out1,v29 + le?vperm $in1,$in1,$in1,$inpperm + vcipher $out2,$out2,v29 + le?vperm $in2,$in2,$in2,$inpperm + vcipher $out3,$out3,v29 + le?vperm $in3,$in3,$in3,$inpperm + vcipher $out4,$out4,v29 + le?vperm $in4,$in4,$in4,$inpperm + vcipher $out5,$out5,v29 + le?vperm $in5,$in5,$in5,$inpperm + vcipher $out6,$out6,v29 + le?vperm $in6,$in6,$in6,$inpperm + vcipher $out7,$out7,v29 + le?vperm $in7,$in7,$in7,$inpperm + + add $inp,$inp,r0 # $inp is adjusted in such + # way that at exit from the + # loop inX-in7 are loaded + # with last "words" + subfe. r0,r0,r0 # borrow?-1:0 + vcipher $out0,$out0,v30 + vxor $in0,$in0,v31 # xor with last round key + vcipher $out1,$out1,v30 + vxor $in1,$in1,v31 + vcipher $out2,$out2,v30 + vxor $in2,$in2,v31 + vcipher $out3,$out3,v30 + vxor $in3,$in3,v31 + vcipher $out4,$out4,v30 + vxor $in4,$in4,v31 + vcipher $out5,$out5,v30 + vxor $in5,$in5,v31 + vcipher $out6,$out6,v30 + vxor $in6,$in6,v31 + vcipher $out7,$out7,v30 + vxor $in7,$in7,v31 + + bne Lctr32_enc8x_break # did $len-129 borrow? + + vcipherlast $in0,$out0,$in0 + vcipherlast $in1,$out1,$in1 + vadduwm $out1,$ivec,$one # counter values ... + vcipherlast $in2,$out2,$in2 + vadduwm $out2,$ivec,$two + vxor $out0,$ivec,$rndkey0 # ... xored with rndkey[0] + vcipherlast $in3,$out3,$in3 + vadduwm $out3,$out1,$two + vxor $out1,$out1,$rndkey0 + vcipherlast $in4,$out4,$in4 + vadduwm $out4,$out2,$two + vxor $out2,$out2,$rndkey0 + vcipherlast $in5,$out5,$in5 + vadduwm $out5,$out3,$two + vxor $out3,$out3,$rndkey0 + vcipherlast $in6,$out6,$in6 + vadduwm $out6,$out4,$two + vxor $out4,$out4,$rndkey0 + vcipherlast $in7,$out7,$in7 + vadduwm $out7,$out5,$two + vxor $out5,$out5,$rndkey0 + le?vperm $in0,$in0,$in0,$inpperm + vadduwm $ivec,$out6,$two # next counter value + vxor $out6,$out6,$rndkey0 + le?vperm $in1,$in1,$in1,$inpperm + vxor $out7,$out7,$rndkey0 + mtctr $rounds + + vcipher $out0,$out0,v24 + stvx_u $in0,$x00,$out + le?vperm $in2,$in2,$in2,$inpperm + vcipher $out1,$out1,v24 + stvx_u $in1,$x10,$out + le?vperm $in3,$in3,$in3,$inpperm + vcipher $out2,$out2,v24 + stvx_u $in2,$x20,$out + le?vperm $in4,$in4,$in4,$inpperm + vcipher $out3,$out3,v24 + stvx_u $in3,$x30,$out + le?vperm $in5,$in5,$in5,$inpperm + vcipher $out4,$out4,v24 + stvx_u $in4,$x40,$out + le?vperm $in6,$in6,$in6,$inpperm + vcipher $out5,$out5,v24 + stvx_u $in5,$x50,$out + le?vperm $in7,$in7,$in7,$inpperm + vcipher $out6,$out6,v24 + stvx_u $in6,$x60,$out + vcipher $out7,$out7,v24 + stvx_u $in7,$x70,$out + addi $out,$out,0x80 + + b Loop_ctr32_enc8x_middle + +.align 5 +Lctr32_enc8x_break: + cmpwi $len,-0x60 + blt Lctr32_enc8x_one + nop + beq Lctr32_enc8x_two + cmpwi $len,-0x40 + blt Lctr32_enc8x_three + nop + beq Lctr32_enc8x_four + cmpwi $len,-0x20 + blt Lctr32_enc8x_five + nop + beq Lctr32_enc8x_six + cmpwi $len,0x00 + blt Lctr32_enc8x_seven + +Lctr32_enc8x_eight: + vcipherlast $out0,$out0,$in0 + vcipherlast $out1,$out1,$in1 + vcipherlast $out2,$out2,$in2 + vcipherlast $out3,$out3,$in3 + vcipherlast $out4,$out4,$in4 + vcipherlast $out5,$out5,$in5 + vcipherlast $out6,$out6,$in6 + vcipherlast $out7,$out7,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x30,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x40,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x50,$out + le?vperm $out7,$out7,$out7,$inpperm + stvx_u $out6,$x60,$out + stvx_u $out7,$x70,$out + addi $out,$out,0x80 + b Lctr32_enc8x_done + +.align 5 +Lctr32_enc8x_seven: + vcipherlast $out0,$out0,$in1 + vcipherlast $out1,$out1,$in2 + vcipherlast $out2,$out2,$in3 + vcipherlast $out3,$out3,$in4 + vcipherlast $out4,$out4,$in5 + vcipherlast $out5,$out5,$in6 + vcipherlast $out6,$out6,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x30,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x40,$out + le?vperm $out6,$out6,$out6,$inpperm + stvx_u $out5,$x50,$out + stvx_u $out6,$x60,$out + addi $out,$out,0x70 + b Lctr32_enc8x_done + +.align 5 +Lctr32_enc8x_six: + vcipherlast $out0,$out0,$in2 + vcipherlast $out1,$out1,$in3 + vcipherlast $out2,$out2,$in4 + vcipherlast $out3,$out3,$in5 + vcipherlast $out4,$out4,$in6 + vcipherlast $out5,$out5,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x30,$out + le?vperm $out5,$out5,$out5,$inpperm + stvx_u $out4,$x40,$out + stvx_u $out5,$x50,$out + addi $out,$out,0x60 + b Lctr32_enc8x_done + +.align 5 +Lctr32_enc8x_five: + vcipherlast $out0,$out0,$in3 + vcipherlast $out1,$out1,$in4 + vcipherlast $out2,$out2,$in5 + vcipherlast $out3,$out3,$in6 + vcipherlast $out4,$out4,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + le?vperm $out4,$out4,$out4,$inpperm + stvx_u $out3,$x30,$out + stvx_u $out4,$x40,$out + addi $out,$out,0x50 + b Lctr32_enc8x_done + +.align 5 +Lctr32_enc8x_four: + vcipherlast $out0,$out0,$in4 + vcipherlast $out1,$out1,$in5 + vcipherlast $out2,$out2,$in6 + vcipherlast $out3,$out3,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + le?vperm $out3,$out3,$out3,$inpperm + stvx_u $out2,$x20,$out + stvx_u $out3,$x30,$out + addi $out,$out,0x40 + b Lctr32_enc8x_done + +.align 5 +Lctr32_enc8x_three: + vcipherlast $out0,$out0,$in5 + vcipherlast $out1,$out1,$in6 + vcipherlast $out2,$out2,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + le?vperm $out2,$out2,$out2,$inpperm + stvx_u $out1,$x10,$out + stvx_u $out2,$x20,$out + addi $out,$out,0x30 + b Lcbc_dec8x_done + +.align 5 +Lctr32_enc8x_two: + vcipherlast $out0,$out0,$in6 + vcipherlast $out1,$out1,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + le?vperm $out1,$out1,$out1,$inpperm + stvx_u $out0,$x00,$out + stvx_u $out1,$x10,$out + addi $out,$out,0x20 + b Lcbc_dec8x_done + +.align 5 +Lctr32_enc8x_one: + vcipherlast $out0,$out0,$in7 + + le?vperm $out0,$out0,$out0,$inpperm + stvx_u $out0,0,$out + addi $out,$out,0x10 + +Lctr32_enc8x_done: + li r10,`$FRAME+15` + li r11,`$FRAME+31` + stvx $inpperm,r10,$sp # wipe copies of round keys + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + stvx $inpperm,r10,$sp + addi r10,r10,32 + stvx $inpperm,r11,$sp + addi r11,r11,32 + + mtspr 256,$vrsave + lvx v20,r10,$sp # ABI says so + addi r10,r10,32 + lvx v21,r11,$sp + addi r11,r11,32 + lvx v22,r10,$sp + addi r10,r10,32 + lvx v23,r11,$sp + addi r11,r11,32 + lvx v24,r10,$sp + addi r10,r10,32 + lvx v25,r11,$sp + addi r11,r11,32 + lvx v26,r10,$sp + addi r10,r10,32 + lvx v27,r11,$sp + addi r11,r11,32 + lvx v28,r10,$sp + addi r10,r10,32 + lvx v29,r11,$sp + addi r11,r11,32 + lvx v30,r10,$sp + lvx v31,r11,$sp + $POP r26,`$FRAME+21*16+0*$SIZE_T`($sp) + $POP r27,`$FRAME+21*16+1*$SIZE_T`($sp) + $POP r28,`$FRAME+21*16+2*$SIZE_T`($sp) + $POP r29,`$FRAME+21*16+3*$SIZE_T`($sp) + $POP r30,`$FRAME+21*16+4*$SIZE_T`($sp) + $POP r31,`$FRAME+21*16+5*$SIZE_T`($sp) + addi $sp,$sp,`$FRAME+21*16+6*$SIZE_T` + blr + .long 0 + .byte 0,12,0x14,0,0x80,6,6,0 + .long 0 +.size .${prefix}_ctr32_encrypt_blocks,.-.${prefix}_ctr32_encrypt_blocks +___ +}} }}} + +my $consts=1; +foreach(split("\n",$code)) { + s/\`([^\`]*)\`/eval($1)/geo; + + # constants table endian-specific conversion + if ($consts && m/\.(long|byte)\s+(.+)\s+(\?[a-z]*)$/o) { + my $conv=$3; + my @bytes=(); + + # convert to endian-agnostic format + if ($1 eq "long") { + foreach (split(/,\s*/,$2)) { + my $l = /^0/?oct:int; + push @bytes,($l>>24)&0xff,($l>>16)&0xff,($l>>8)&0xff,$l&0xff; + } + } else { + @bytes = map(/^0/?oct:int,split(/,\s*/,$2)); + } + + # little-endian conversion + if ($flavour =~ /le$/o) { + SWITCH: for($conv) { + /\?inv/ && do { @bytes=map($_^0xf,@bytes); last; }; + /\?rev/ && do { @bytes=reverse(@bytes); last; }; + } + } + + #emit + print ".byte\t",join(',',map (sprintf("0x%02x",$_),@bytes)),"\n"; + next; + } + $consts=0 if (m/Lconsts:/o); # end of table + + # instructions prefixed with '?' are endian-specific and need + # to be adjusted accordingly... + if ($flavour =~ /le$/o) { # little-endian + s/le\?//o or + s/be\?/#be#/o or + s/\?lvsr/lvsl/o or + s/\?lvsl/lvsr/o or + s/\?(vperm\s+v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+,\s*)(v[0-9]+)/$1$3$2$4/o or + s/\?(vsldoi\s+v[0-9]+,\s*)(v[0-9]+,)\s*(v[0-9]+,\s*)([0-9]+)/$1$3$2 16-$4/o or + s/\?(vspltw\s+v[0-9]+,\s*)(v[0-9]+,)\s*([0-9])/$1$2 3-$3/o; + } else { # big-endian + s/le\?/#le#/o or + s/be\?//o or + s/\?([a-z]+)/$1/o; + } + + print $_,"\n"; +} + +close STDOUT; diff --git a/drivers/crypto/vmx/ghash.c b/drivers/crypto/vmx/ghash.c new file mode 100644 index 000000000000..d0ffe277af5c --- /dev/null +++ b/drivers/crypto/vmx/ghash.c @@ -0,0 +1,214 @@ +/** + * GHASH routines supporting VMX instructions on the Power 8 + * + * Copyright (C) 2015 International Business Machines 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; version 2 only. + * + * 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. + * + * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + */ + +#include <linux/types.h> +#include <linux/err.h> +#include <linux/crypto.h> +#include <linux/delay.h> +#include <linux/hardirq.h> +#include <asm/switch_to.h> +#include <crypto/aes.h> +#include <crypto/scatterwalk.h> +#include <crypto/internal/hash.h> +#include <crypto/b128ops.h> + +#define IN_INTERRUPT in_interrupt() + +#define GHASH_BLOCK_SIZE (16) +#define GHASH_DIGEST_SIZE (16) +#define GHASH_KEY_LEN (16) + +void gcm_init_p8(u128 htable[16], const u64 Xi[2]); +void gcm_gmult_p8(u64 Xi[2], const u128 htable[16]); +void gcm_ghash_p8(u64 Xi[2], const u128 htable[16], + const u8 *in,size_t len); + +struct p8_ghash_ctx { + u128 htable[16]; + struct crypto_shash *fallback; +}; + +struct p8_ghash_desc_ctx { + u64 shash[2]; + u8 buffer[GHASH_DIGEST_SIZE]; + int bytes; + struct shash_desc fallback_desc; +}; + +static int p8_ghash_init_tfm(struct crypto_tfm *tfm) +{ + const char *alg; + struct crypto_shash *fallback; + struct crypto_shash *shash_tfm = __crypto_shash_cast(tfm); + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm); + + if (!(alg = crypto_tfm_alg_name(tfm))) { + printk(KERN_ERR "Failed to get algorithm name.\n"); + return -ENOENT; + } + + fallback = crypto_alloc_shash(alg, 0 ,CRYPTO_ALG_NEED_FALLBACK); + if (IS_ERR(fallback)) { + printk(KERN_ERR "Failed to allocate transformation for '%s': %ld\n", + alg, PTR_ERR(fallback)); + return PTR_ERR(fallback); + } + printk(KERN_INFO "Using '%s' as fallback implementation.\n", + crypto_tfm_alg_driver_name(crypto_shash_tfm(fallback))); + + crypto_shash_set_flags(fallback, + crypto_shash_get_flags((struct crypto_shash *) tfm)); + ctx->fallback = fallback; + + shash_tfm->descsize = sizeof(struct p8_ghash_desc_ctx) + + crypto_shash_descsize(fallback); + + return 0; +} + +static void p8_ghash_exit_tfm(struct crypto_tfm *tfm) +{ + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(tfm); + + if (ctx->fallback) { + crypto_free_shash(ctx->fallback); + ctx->fallback = NULL; + } +} + +static int p8_ghash_init(struct shash_desc *desc) +{ + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); + struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + dctx->bytes = 0; + memset(dctx->shash, 0, GHASH_DIGEST_SIZE); + dctx->fallback_desc.tfm = ctx->fallback; + dctx->fallback_desc.flags = desc->flags; + return crypto_shash_init(&dctx->fallback_desc); +} + +static int p8_ghash_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen) +{ + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(tfm)); + + if (keylen != GHASH_KEY_LEN) + return -EINVAL; + + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_fp(); + gcm_init_p8(ctx->htable, (const u64 *) key); + pagefault_enable(); + return crypto_shash_setkey(ctx->fallback, key, keylen); +} + +static int p8_ghash_update(struct shash_desc *desc, + const u8 *src, unsigned int srclen) +{ + unsigned int len; + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); + struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + if (IN_INTERRUPT) { + return crypto_shash_update(&dctx->fallback_desc, src, srclen); + } else { + if (dctx->bytes) { + if (dctx->bytes + srclen < GHASH_DIGEST_SIZE) { + memcpy(dctx->buffer + dctx->bytes, src, srclen); + dctx->bytes += srclen; + return 0; + } + memcpy(dctx->buffer + dctx->bytes, src, + GHASH_DIGEST_SIZE - dctx->bytes); + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer, + GHASH_DIGEST_SIZE); + pagefault_enable(); + src += GHASH_DIGEST_SIZE - dctx->bytes; + srclen -= GHASH_DIGEST_SIZE - dctx->bytes; + dctx->bytes = 0; + } + len = srclen & ~(GHASH_DIGEST_SIZE - 1); + if (len) { + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, src, len); + pagefault_enable(); + src += len; + srclen -= len; + } + if (srclen) { + memcpy(dctx->buffer, src, srclen); + dctx->bytes = srclen; + } + return 0; + } +} + +static int p8_ghash_final(struct shash_desc *desc, u8 *out) +{ + int i; + struct p8_ghash_ctx *ctx = crypto_tfm_ctx(crypto_shash_tfm(desc->tfm)); + struct p8_ghash_desc_ctx *dctx = shash_desc_ctx(desc); + + if (IN_INTERRUPT) { + return crypto_shash_final(&dctx->fallback_desc, out); + } else { + if (dctx->bytes) { + for (i = dctx->bytes; i < GHASH_DIGEST_SIZE; i++) + dctx->buffer[i] = 0; + pagefault_disable(); + enable_kernel_altivec(); + enable_kernel_fp(); + gcm_ghash_p8(dctx->shash, ctx->htable, dctx->buffer, + GHASH_DIGEST_SIZE); + pagefault_enable(); + dctx->bytes = 0; + } + memcpy(out, dctx->shash, GHASH_DIGEST_SIZE); + return 0; + } +} + +struct shash_alg p8_ghash_alg = { + .digestsize = GHASH_DIGEST_SIZE, + .init = p8_ghash_init, + .update = p8_ghash_update, + .final = p8_ghash_final, + .setkey = p8_ghash_setkey, + .descsize = sizeof(struct p8_ghash_desc_ctx), + .base = { + .cra_name = "ghash", + .cra_driver_name = "p8_ghash", + .cra_priority = 1000, + .cra_flags = CRYPTO_ALG_TYPE_SHASH | CRYPTO_ALG_NEED_FALLBACK, + .cra_blocksize = GHASH_BLOCK_SIZE, + .cra_ctxsize = sizeof(struct p8_ghash_ctx), + .cra_module = THIS_MODULE, + .cra_init = p8_ghash_init_tfm, + .cra_exit = p8_ghash_exit_tfm, + }, +}; diff --git a/drivers/crypto/vmx/ghashp8-ppc.pl b/drivers/crypto/vmx/ghashp8-ppc.pl new file mode 100644 index 000000000000..0a6f899839dd --- /dev/null +++ b/drivers/crypto/vmx/ghashp8-ppc.pl @@ -0,0 +1,228 @@ +#!/usr/bin/env perl +# +# ==================================================================== +# Written by Andy Polyakov <appro@openssl.org> for the OpenSSL +# project. The module is, however, dual licensed under OpenSSL and +# CRYPTOGAMS licenses depending on where you obtain it. For further +# details see http://www.openssl.org/~appro/cryptogams/. +# ==================================================================== +# +# GHASH for for PowerISA v2.07. +# +# July 2014 +# +# Accurate performance measurements are problematic, because it's +# always virtualized setup with possibly throttled processor. +# Relative comparison is therefore more informative. This initial +# version is ~2.1x slower than hardware-assisted AES-128-CTR, ~12x +# faster than "4-bit" integer-only compiler-generated 64-bit code. +# "Initial version" means that there is room for futher improvement. + +$flavour=shift; +$output =shift; + +if ($flavour =~ /64/) { + $SIZE_T=8; + $LRSAVE=2*$SIZE_T; + $STU="stdu"; + $POP="ld"; + $PUSH="std"; +} elsif ($flavour =~ /32/) { + $SIZE_T=4; + $LRSAVE=$SIZE_T; + $STU="stwu"; + $POP="lwz"; + $PUSH="stw"; +} else { die "nonsense $flavour"; } + +$0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1; +( $xlate="${dir}ppc-xlate.pl" and -f $xlate ) or +( $xlate="${dir}../../perlasm/ppc-xlate.pl" and -f $xlate) or +die "can't locate ppc-xlate.pl"; + +open STDOUT,"| $^X $xlate $flavour $output" || die "can't call $xlate: $!"; + +my ($Xip,$Htbl,$inp,$len)=map("r$_",(3..6)); # argument block + +my ($Xl,$Xm,$Xh,$IN)=map("v$_",(0..3)); +my ($zero,$t0,$t1,$t2,$xC2,$H,$Hh,$Hl,$lemask)=map("v$_",(4..12)); +my $vrsave="r12"; + +$code=<<___; +.machine "any" + +.text + +.globl .gcm_init_p8 + lis r0,0xfff0 + li r8,0x10 + mfspr $vrsave,256 + li r9,0x20 + mtspr 256,r0 + li r10,0x30 + lvx_u $H,0,r4 # load H + + vspltisb $xC2,-16 # 0xf0 + vspltisb $t0,1 # one + vaddubm $xC2,$xC2,$xC2 # 0xe0 + vxor $zero,$zero,$zero + vor $xC2,$xC2,$t0 # 0xe1 + vsldoi $xC2,$xC2,$zero,15 # 0xe1... + vsldoi $t1,$zero,$t0,1 # ...1 + vaddubm $xC2,$xC2,$xC2 # 0xc2... + vspltisb $t2,7 + vor $xC2,$xC2,$t1 # 0xc2....01 + vspltb $t1,$H,0 # most significant byte + vsl $H,$H,$t0 # H<<=1 + vsrab $t1,$t1,$t2 # broadcast carry bit + vand $t1,$t1,$xC2 + vxor $H,$H,$t1 # twisted H + + vsldoi $H,$H,$H,8 # twist even more ... + vsldoi $xC2,$zero,$xC2,8 # 0xc2.0 + vsldoi $Hl,$zero,$H,8 # ... and split + vsldoi $Hh,$H,$zero,8 + + stvx_u $xC2,0,r3 # save pre-computed table + stvx_u $Hl,r8,r3 + stvx_u $H, r9,r3 + stvx_u $Hh,r10,r3 + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,2,0 + .long 0 +.size .gcm_init_p8,.-.gcm_init_p8 + +.globl .gcm_gmult_p8 + lis r0,0xfff8 + li r8,0x10 + mfspr $vrsave,256 + li r9,0x20 + mtspr 256,r0 + li r10,0x30 + lvx_u $IN,0,$Xip # load Xi + + lvx_u $Hl,r8,$Htbl # load pre-computed table + le?lvsl $lemask,r0,r0 + lvx_u $H, r9,$Htbl + le?vspltisb $t0,0x07 + lvx_u $Hh,r10,$Htbl + le?vxor $lemask,$lemask,$t0 + lvx_u $xC2,0,$Htbl + le?vperm $IN,$IN,$IN,$lemask + vxor $zero,$zero,$zero + + vpmsumd $Xl,$IN,$Hl # H.lo·Xi.lo + vpmsumd $Xm,$IN,$H # H.hi·Xi.lo+H.lo·Xi.hi + vpmsumd $Xh,$IN,$Hh # H.hi·Xi.hi + + vpmsumd $t2,$Xl,$xC2 # 1st phase + + vsldoi $t0,$Xm,$zero,8 + vsldoi $t1,$zero,$Xm,8 + vxor $Xl,$Xl,$t0 + vxor $Xh,$Xh,$t1 + + vsldoi $Xl,$Xl,$Xl,8 + vxor $Xl,$Xl,$t2 + + vsldoi $t1,$Xl,$Xl,8 # 2nd phase + vpmsumd $Xl,$Xl,$xC2 + vxor $t1,$t1,$Xh + vxor $Xl,$Xl,$t1 + + le?vperm $Xl,$Xl,$Xl,$lemask + stvx_u $Xl,0,$Xip # write out Xi + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,2,0 + .long 0 +.size .gcm_gmult_p8,.-.gcm_gmult_p8 + +.globl .gcm_ghash_p8 + lis r0,0xfff8 + li r8,0x10 + mfspr $vrsave,256 + li r9,0x20 + mtspr 256,r0 + li r10,0x30 + lvx_u $Xl,0,$Xip # load Xi + + lvx_u $Hl,r8,$Htbl # load pre-computed table + le?lvsl $lemask,r0,r0 + lvx_u $H, r9,$Htbl + le?vspltisb $t0,0x07 + lvx_u $Hh,r10,$Htbl + le?vxor $lemask,$lemask,$t0 + lvx_u $xC2,0,$Htbl + le?vperm $Xl,$Xl,$Xl,$lemask + vxor $zero,$zero,$zero + + lvx_u $IN,0,$inp + addi $inp,$inp,16 + subi $len,$len,16 + le?vperm $IN,$IN,$IN,$lemask + vxor $IN,$IN,$Xl + b Loop + +.align 5 +Loop: + subic $len,$len,16 + vpmsumd $Xl,$IN,$Hl # H.lo·Xi.lo + subfe. r0,r0,r0 # borrow?-1:0 + vpmsumd $Xm,$IN,$H # H.hi·Xi.lo+H.lo·Xi.hi + and r0,r0,$len + vpmsumd $Xh,$IN,$Hh # H.hi·Xi.hi + add $inp,$inp,r0 + + vpmsumd $t2,$Xl,$xC2 # 1st phase + + vsldoi $t0,$Xm,$zero,8 + vsldoi $t1,$zero,$Xm,8 + vxor $Xl,$Xl,$t0 + vxor $Xh,$Xh,$t1 + + vsldoi $Xl,$Xl,$Xl,8 + vxor $Xl,$Xl,$t2 + lvx_u $IN,0,$inp + addi $inp,$inp,16 + + vsldoi $t1,$Xl,$Xl,8 # 2nd phase + vpmsumd $Xl,$Xl,$xC2 + le?vperm $IN,$IN,$IN,$lemask + vxor $t1,$t1,$Xh + vxor $IN,$IN,$t1 + vxor $IN,$IN,$Xl + beq Loop # did $len-=16 borrow? + + vxor $Xl,$Xl,$t1 + le?vperm $Xl,$Xl,$Xl,$lemask + stvx_u $Xl,0,$Xip # write out Xi + + mtspr 256,$vrsave + blr + .long 0 + .byte 0,12,0x14,0,0,0,4,0 + .long 0 +.size .gcm_ghash_p8,.-.gcm_ghash_p8 + +.asciz "GHASH for PowerISA 2.07, CRYPTOGAMS by <appro\@openssl.org>" +.align 2 +___ + +foreach (split("\n",$code)) { + if ($flavour =~ /le$/o) { # little-endian + s/le\?//o or + s/be\?/#be#/o; + } else { + s/le\?/#le#/o or + s/be\?//o; + } + print $_,"\n"; +} + +close STDOUT; # enforce flush diff --git a/drivers/crypto/vmx/ppc-xlate.pl b/drivers/crypto/vmx/ppc-xlate.pl new file mode 100644 index 000000000000..a59188494af8 --- /dev/null +++ b/drivers/crypto/vmx/ppc-xlate.pl @@ -0,0 +1,207 @@ +#!/usr/bin/env perl + +# PowerPC assembler distiller by <appro>. + +my $flavour = shift; +my $output = shift; +open STDOUT,">$output" || die "can't open $output: $!"; + +my %GLOBALS; +my $dotinlocallabels=($flavour=~/linux/)?1:0; + +################################################################ +# directives which need special treatment on different platforms +################################################################ +my $globl = sub { + my $junk = shift; + my $name = shift; + my $global = \$GLOBALS{$name}; + my $ret; + + $name =~ s|^[\.\_]||; + + SWITCH: for ($flavour) { + /aix/ && do { $name = ".$name"; + last; + }; + /osx/ && do { $name = "_$name"; + last; + }; + /linux/ + && do { $ret = "_GLOBAL($name)"; + last; + }; + } + + $ret = ".globl $name\nalign 5\n$name:" if (!$ret); + $$global = $name; + $ret; +}; +my $text = sub { + my $ret = ($flavour =~ /aix/) ? ".csect\t.text[PR],7" : ".text"; + $ret = ".abiversion 2\n".$ret if ($flavour =~ /linux.*64le/); + $ret; +}; +my $machine = sub { + my $junk = shift; + my $arch = shift; + if ($flavour =~ /osx/) + { $arch =~ s/\"//g; + $arch = ($flavour=~/64/) ? "ppc970-64" : "ppc970" if ($arch eq "any"); + } + ".machine $arch"; +}; +my $size = sub { + if ($flavour =~ /linux/) + { shift; + my $name = shift; $name =~ s|^[\.\_]||; + my $ret = ".size $name,.-".($flavour=~/64$/?".":"").$name; + $ret .= "\n.size .$name,.-.$name" if ($flavour=~/64$/); + $ret; + } + else + { ""; } +}; +my $asciz = sub { + shift; + my $line = join(",",@_); + if ($line =~ /^"(.*)"$/) + { ".byte " . join(",",unpack("C*",$1),0) . "\n.align 2"; } + else + { ""; } +}; +my $quad = sub { + shift; + my @ret; + my ($hi,$lo); + for (@_) { + if (/^0x([0-9a-f]*?)([0-9a-f]{1,8})$/io) + { $hi=$1?"0x$1":"0"; $lo="0x$2"; } + elsif (/^([0-9]+)$/o) + { $hi=$1>>32; $lo=$1&0xffffffff; } # error-prone with 32-bit perl + else + { $hi=undef; $lo=$_; } + + if (defined($hi)) + { push(@ret,$flavour=~/le$/o?".long\t$lo,$hi":".long\t$hi,$lo"); } + else + { push(@ret,".quad $lo"); } + } + join("\n",@ret); +}; + +################################################################ +# simplified mnemonics not handled by at least one assembler +################################################################ +my $cmplw = sub { + my $f = shift; + my $cr = 0; $cr = shift if ($#_>1); + # Some out-of-date 32-bit GNU assembler just can't handle cmplw... + ($flavour =~ /linux.*32/) ? + " .long ".sprintf "0x%x",31<<26|$cr<<23|$_[0]<<16|$_[1]<<11|64 : + " cmplw ".join(',',$cr,@_); +}; +my $bdnz = sub { + my $f = shift; + my $bo = $f=~/[\+\-]/ ? 16+9 : 16; # optional "to be taken" hint + " bc $bo,0,".shift; +} if ($flavour!~/linux/); +my $bltlr = sub { + my $f = shift; + my $bo = $f=~/\-/ ? 12+2 : 12; # optional "not to be taken" hint + ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints + " .long ".sprintf "0x%x",19<<26|$bo<<21|16<<1 : + " bclr $bo,0"; +}; +my $bnelr = sub { + my $f = shift; + my $bo = $f=~/\-/ ? 4+2 : 4; # optional "not to be taken" hint + ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints + " .long ".sprintf "0x%x",19<<26|$bo<<21|2<<16|16<<1 : + " bclr $bo,2"; +}; +my $beqlr = sub { + my $f = shift; + my $bo = $f=~/-/ ? 12+2 : 12; # optional "not to be taken" hint + ($flavour =~ /linux/) ? # GNU as doesn't allow most recent hints + " .long ".sprintf "0x%X",19<<26|$bo<<21|2<<16|16<<1 : + " bclr $bo,2"; +}; +# GNU assembler can't handle extrdi rA,rS,16,48, or when sum of last two +# arguments is 64, with "operand out of range" error. +my $extrdi = sub { + my ($f,$ra,$rs,$n,$b) = @_; + $b = ($b+$n)&63; $n = 64-$n; + " rldicl $ra,$rs,$b,$n"; +}; +my $vmr = sub { + my ($f,$vx,$vy) = @_; + " vor $vx,$vy,$vy"; +}; + +# PowerISA 2.06 stuff +sub vsxmem_op { + my ($f, $vrt, $ra, $rb, $op) = @_; + " .long ".sprintf "0x%X",(31<<26)|($vrt<<21)|($ra<<16)|($rb<<11)|($op*2+1); +} +# made-up unaligned memory reference AltiVec/VMX instructions +my $lvx_u = sub { vsxmem_op(@_, 844); }; # lxvd2x +my $stvx_u = sub { vsxmem_op(@_, 972); }; # stxvd2x +my $lvdx_u = sub { vsxmem_op(@_, 588); }; # lxsdx +my $stvdx_u = sub { vsxmem_op(@_, 716); }; # stxsdx +my $lvx_4w = sub { vsxmem_op(@_, 780); }; # lxvw4x +my $stvx_4w = sub { vsxmem_op(@_, 908); }; # stxvw4x + +# PowerISA 2.07 stuff +sub vcrypto_op { + my ($f, $vrt, $vra, $vrb, $op) = @_; + " .long ".sprintf "0x%X",(4<<26)|($vrt<<21)|($vra<<16)|($vrb<<11)|$op; +} +my $vcipher = sub { vcrypto_op(@_, 1288); }; +my $vcipherlast = sub { vcrypto_op(@_, 1289); }; +my $vncipher = sub { vcrypto_op(@_, 1352); }; +my $vncipherlast= sub { vcrypto_op(@_, 1353); }; +my $vsbox = sub { vcrypto_op(@_, 0, 1480); }; +my $vshasigmad = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1730); }; +my $vshasigmaw = sub { my ($st,$six)=splice(@_,-2); vcrypto_op(@_, $st<<4|$six, 1666); }; +my $vpmsumb = sub { vcrypto_op(@_, 1032); }; +my $vpmsumd = sub { vcrypto_op(@_, 1224); }; +my $vpmsubh = sub { vcrypto_op(@_, 1096); }; +my $vpmsumw = sub { vcrypto_op(@_, 1160); }; +my $vaddudm = sub { vcrypto_op(@_, 192); }; + +my $mtsle = sub { + my ($f, $arg) = @_; + " .long ".sprintf "0x%X",(31<<26)|($arg<<21)|(147*2); +}; + +print "#include <asm/ppc_asm.h>\n" if $flavour =~ /linux/; + +while($line=<>) { + + $line =~ s|[#!;].*$||; # get rid of asm-style comments... + $line =~ s|/\*.*\*/||; # ... and C-style comments... + $line =~ s|^\s+||; # ... and skip white spaces in beginning... + $line =~ s|\s+$||; # ... and at the end + + { + $line =~ s|\b\.L(\w+)|L$1|g; # common denominator for Locallabel + $line =~ s|\bL(\w+)|\.L$1|g if ($dotinlocallabels); + } + + { + $line =~ s|^\s*(\.?)(\w+)([\.\+\-]?)\s*||; + my $c = $1; $c = "\t" if ($c eq ""); + my $mnemonic = $2; + my $f = $3; + my $opcode = eval("\$$mnemonic"); + $line =~ s/\b(c?[rf]|v|vs)([0-9]+)\b/$2/g if ($c ne "." and $flavour !~ /osx/); + if (ref($opcode) eq 'CODE') { $line = &$opcode($f,split(',',$line)); } + elsif ($mnemonic) { $line = $c.$mnemonic.$f."\t".$line; } + } + + print $line if ($line); + print "\n"; +} + +close STDOUT; diff --git a/drivers/crypto/vmx/vmx.c b/drivers/crypto/vmx/vmx.c new file mode 100644 index 000000000000..44d8d5cfe40d --- /dev/null +++ b/drivers/crypto/vmx/vmx.c @@ -0,0 +1,88 @@ +/** + * Routines supporting VMX instructions on the Power 8 + * + * Copyright (C) 2015 International Business Machines 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; version 2 only. + * + * 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. + * + * Author: Marcelo Henrique Cerri <mhcerri@br.ibm.com> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/crypto.h> +#include <asm/cputable.h> +#include <crypto/internal/hash.h> + +extern struct shash_alg p8_ghash_alg; +extern struct crypto_alg p8_aes_alg; +extern struct crypto_alg p8_aes_cbc_alg; +extern struct crypto_alg p8_aes_ctr_alg; +static struct crypto_alg *algs[] = { + &p8_aes_alg, + &p8_aes_cbc_alg, + &p8_aes_ctr_alg, + NULL, +}; + +int __init p8_init(void) +{ + int ret = 0; + struct crypto_alg **alg_it; + + if (!(cur_cpu_spec->cpu_user_features2 & PPC_FEATURE2_VEC_CRYPTO)) + return -ENODEV; + + for (alg_it = algs; *alg_it; alg_it++) { + ret = crypto_register_alg(*alg_it); + printk(KERN_INFO "crypto_register_alg '%s' = %d\n", + (*alg_it)->cra_name, ret); + if (ret) { + for (alg_it--; alg_it >= algs; alg_it--) + crypto_unregister_alg(*alg_it); + break; + } + } + if (ret) + return ret; + + ret = crypto_register_shash(&p8_ghash_alg); + if (ret) { + for (alg_it = algs; *alg_it; alg_it++) + crypto_unregister_alg(*alg_it); + } + return ret; +} + +void __exit p8_exit(void) +{ + struct crypto_alg **alg_it; + + for (alg_it = algs; *alg_it; alg_it++) { + printk(KERN_INFO "Removing '%s'\n", (*alg_it)->cra_name); + crypto_unregister_alg(*alg_it); + } + crypto_unregister_shash(&p8_ghash_alg); +} + +module_init(p8_init); +module_exit(p8_exit); + +MODULE_AUTHOR("Marcelo Cerri<mhcerri@br.ibm.com>"); +MODULE_DESCRIPTION("IBM VMX cryptogaphic acceleration instructions support on Power 8"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.0"); + diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c index ca31f1b45366..cbd4a8aff120 100644 --- a/drivers/dma/of-dma.c +++ b/drivers/dma/of-dma.c @@ -194,6 +194,7 @@ struct dma_chan *of_dma_request_slave_channel(struct device_node *np, return ERR_PTR(ret_no_channel); } +EXPORT_SYMBOL_GPL(of_dma_request_slave_channel); /** * of_dma_simple_xlate - Simple DMA engine translation function diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index aed8afee56da..450d15965005 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -42,6 +42,7 @@ #include <linux/io.h> #include <linux/jiffies.h> #include <linux/cpu.h> +#include <linux/uio.h> #include <asm/pgtable.h> #include "ipath_kernel.h" @@ -52,15 +53,19 @@ static int ipath_open(struct inode *, struct file *); static int ipath_close(struct inode *, struct file *); static ssize_t ipath_write(struct file *, const char __user *, size_t, loff_t *); -static ssize_t ipath_writev(struct kiocb *, const struct iovec *, - unsigned long , loff_t); +static ssize_t ipath_write_iter(struct kiocb *, struct iov_iter *from); static unsigned int ipath_poll(struct file *, struct poll_table_struct *); static int ipath_mmap(struct file *, struct vm_area_struct *); +/* + * This is really, really weird shit - write() and writev() here + * have completely unrelated semantics. Sucky userland ABI, + * film at 11. + */ static const struct file_operations ipath_file_ops = { .owner = THIS_MODULE, .write = ipath_write, - .aio_write = ipath_writev, + .write_iter = ipath_write_iter, .open = ipath_open, .release = ipath_close, .poll = ipath_poll, @@ -2413,18 +2418,17 @@ bail: return ret; } -static ssize_t ipath_writev(struct kiocb *iocb, const struct iovec *iov, - unsigned long dim, loff_t off) +static ssize_t ipath_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct file *filp = iocb->ki_filp; struct ipath_filedata *fp = filp->private_data; struct ipath_portdata *pd = port_fp(filp); struct ipath_user_sdma_queue *pq = fp->pq; - if (!dim) + if (!iter_is_iovec(from) || !from->nr_segs) return -EINVAL; - return ipath_user_sdma_writev(pd->port_dd, pq, iov, dim); + return ipath_user_sdma_writev(pd->port_dd, pq, from->iov, from->nr_segs); } static struct class *ipath_class; diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index 14046f5a37fa..9ea6c440a00c 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -43,6 +43,7 @@ #include <asm/pgtable.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/uio.h> #include "qib.h" #include "qib_common.h" @@ -54,15 +55,19 @@ static int qib_open(struct inode *, struct file *); static int qib_close(struct inode *, struct file *); static ssize_t qib_write(struct file *, const char __user *, size_t, loff_t *); -static ssize_t qib_aio_write(struct kiocb *, const struct iovec *, - unsigned long, loff_t); +static ssize_t qib_write_iter(struct kiocb *, struct iov_iter *); static unsigned int qib_poll(struct file *, struct poll_table_struct *); static int qib_mmapf(struct file *, struct vm_area_struct *); +/* + * This is really, really weird shit - write() and writev() here + * have completely unrelated semantics. Sucky userland ABI, + * film at 11. + */ static const struct file_operations qib_file_ops = { .owner = THIS_MODULE, .write = qib_write, - .aio_write = qib_aio_write, + .write_iter = qib_write_iter, .open = qib_open, .release = qib_close, .poll = qib_poll, @@ -2248,17 +2253,16 @@ bail: return ret; } -static ssize_t qib_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long dim, loff_t off) +static ssize_t qib_write_iter(struct kiocb *iocb, struct iov_iter *from) { struct qib_filedata *fp = iocb->ki_filp->private_data; struct qib_ctxtdata *rcd = ctxt_fp(iocb->ki_filp); struct qib_user_sdma_queue *pq = fp->pq; - if (!dim || !pq) + if (!iter_is_iovec(from) || !from->nr_segs || !pq) return -EINVAL; - - return qib_user_sdma_writev(rcd, pq, iov, dim); + + return qib_user_sdma_writev(rcd, pq, from->iov, from->nr_segs); } static struct class *qib_class; diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 25b320d64e26..966b9605f5f0 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -526,6 +526,14 @@ config LEDS_VERSATILE This option enabled support for the LEDs on the ARM Versatile and RealView boards. Say Y to enabled these. +config LEDS_PM8941_WLED + tristate "LED support for the Qualcomm PM8941 WLED block" + depends on LEDS_CLASS + select REGMAP + help + This option enables support for the 'White' LED block + on Qualcomm PM8941 PMICs. + comment "LED Triggers" source "drivers/leds/trigger/Kconfig" diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index cbba921b6f1c..bf4609338e10 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -58,6 +58,7 @@ obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o obj-$(CONFIG_LEDS_SYSCON) += leds-syscon.o obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o obj-$(CONFIG_LEDS_MENF21BMC) += leds-menf21bmc.o +obj-$(CONFIG_LEDS_PM8941_WLED) += leds-pm8941-wled.o # LED SPI Drivers obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o diff --git a/drivers/leds/led-class-flash.c b/drivers/leds/led-class-flash.c index 4a19fd44f93f..3b2573411a37 100644 --- a/drivers/leds/led-class-flash.c +++ b/drivers/leds/led-class-flash.c @@ -216,75 +216,6 @@ static ssize_t flash_fault_show(struct device *dev, } static DEVICE_ATTR_RO(flash_fault); -static ssize_t available_sync_leds_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - char *pbuf = buf; - int i, buf_len; - - buf_len = sprintf(pbuf, "[0: none] "); - pbuf += buf_len; - - for (i = 0; i < fled_cdev->num_sync_leds; ++i) { - buf_len = sprintf(pbuf, "[%d: %s] ", i + 1, - fled_cdev->sync_leds[i]->led_cdev.name); - pbuf += buf_len; - } - - return sprintf(buf, "%s\n", buf); -} -static DEVICE_ATTR_RO(available_sync_leds); - -static ssize_t flash_sync_strobe_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t size) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - unsigned long led_id; - ssize_t ret; - - mutex_lock(&led_cdev->led_access); - - if (led_sysfs_is_disabled(led_cdev)) { - ret = -EBUSY; - goto unlock; - } - - ret = kstrtoul(buf, 10, &led_id); - if (ret) - goto unlock; - - if (led_id > fled_cdev->num_sync_leds) { - ret = -ERANGE; - goto unlock; - } - - fled_cdev->sync_led_id = led_id; - - ret = size; -unlock: - mutex_unlock(&led_cdev->led_access); - return ret; -} - -static ssize_t flash_sync_strobe_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct led_classdev *led_cdev = dev_get_drvdata(dev); - struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); - int sled_id = fled_cdev->sync_led_id; - char *sync_led_name = "none"; - - if (fled_cdev->sync_led_id > 0) - sync_led_name = (char *) - fled_cdev->sync_leds[sled_id - 1]->led_cdev.name; - - return sprintf(buf, "[%d: %s]\n", sled_id, sync_led_name); -} -static DEVICE_ATTR_RW(flash_sync_strobe); - static struct attribute *led_flash_strobe_attrs[] = { &dev_attr_flash_strobe.attr, NULL, @@ -307,12 +238,6 @@ static struct attribute *led_flash_fault_attrs[] = { NULL, }; -static struct attribute *led_flash_sync_strobe_attrs[] = { - &dev_attr_available_sync_leds.attr, - &dev_attr_flash_sync_strobe.attr, - NULL, -}; - static const struct attribute_group led_flash_strobe_group = { .attrs = led_flash_strobe_attrs, }; @@ -329,10 +254,6 @@ static const struct attribute_group led_flash_fault_group = { .attrs = led_flash_fault_attrs, }; -static const struct attribute_group led_flash_sync_strobe_group = { - .attrs = led_flash_sync_strobe_attrs, -}; - static void led_flash_resume(struct led_classdev *led_cdev) { struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(led_cdev); @@ -361,9 +282,6 @@ static void led_flash_init_sysfs_groups(struct led_classdev_flash *fled_cdev) if (ops->fault_get) flash_groups[num_sysfs_groups++] = &led_flash_fault_group; - if (led_cdev->flags & LED_DEV_CAP_SYNC_STROBE) - flash_groups[num_sysfs_groups++] = &led_flash_sync_strobe_group; - led_cdev->groups = flash_groups; } diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c index 795ec994c663..728681debdbe 100644 --- a/drivers/leds/led-class.c +++ b/drivers/leds/led-class.c @@ -212,6 +212,31 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { .resume = led_resume, }; +static int match_name(struct device *dev, const void *data) +{ + if (!dev_name(dev)) + return 0; + return !strcmp(dev_name(dev), (char *)data); +} + +static int led_classdev_next_name(const char *init_name, char *name, + size_t len) +{ + unsigned int i = 0; + int ret = 0; + + strlcpy(name, init_name, len); + + while (class_find_device(leds_class, NULL, name, match_name) && + (ret < len)) + ret = snprintf(name, len, "%s_%u", init_name, ++i); + + if (ret >= len) + return -ENOMEM; + + return i; +} + /** * led_classdev_register - register a new object of led_classdev class. * @parent: The device to register. @@ -219,12 +244,22 @@ static const struct dev_pm_ops leds_class_dev_pm_ops = { */ int led_classdev_register(struct device *parent, struct led_classdev *led_cdev) { + char name[64]; + int ret; + + ret = led_classdev_next_name(led_cdev->name, name, sizeof(name)); + if (ret < 0) + return ret; + led_cdev->dev = device_create_with_groups(leds_class, parent, 0, - led_cdev, led_cdev->groups, - "%s", led_cdev->name); + led_cdev, led_cdev->groups, "%s", name); if (IS_ERR(led_cdev->dev)) return PTR_ERR(led_cdev->dev); + if (ret) + dev_warn(parent, "Led %s renamed to %s due to name collision", + led_cdev->name, dev_name(led_cdev->dev)); + #ifdef CONFIG_LEDS_TRIGGERS init_rwsem(&led_cdev->trigger_lock); #endif @@ -288,6 +323,63 @@ void led_classdev_unregister(struct led_classdev *led_cdev) } EXPORT_SYMBOL_GPL(led_classdev_unregister); +static void devm_led_classdev_release(struct device *dev, void *res) +{ + led_classdev_unregister(*(struct led_classdev **)res); +} + +/** + * devm_led_classdev_register - resource managed led_classdev_register() + * @parent: The device to register. + * @led_cdev: the led_classdev structure for this device. + */ +int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev) +{ + struct led_classdev **dr; + int rc; + + dr = devres_alloc(devm_led_classdev_release, sizeof(*dr), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + rc = led_classdev_register(parent, led_cdev); + if (rc) { + devres_free(dr); + return rc; + } + + *dr = led_cdev; + devres_add(parent, dr); + + return 0; +} +EXPORT_SYMBOL_GPL(devm_led_classdev_register); + +static int devm_led_classdev_match(struct device *dev, void *res, void *data) +{ + struct led_cdev **p = res; + + if (WARN_ON(!p || !*p)) + return 0; + + return *p == data; +} + +/** + * devm_led_classdev_unregister() - resource managed led_classdev_unregister() + * @parent: The device to unregister. + * @led_cdev: the led_classdev structure for this device. + */ +void devm_led_classdev_unregister(struct device *dev, + struct led_classdev *led_cdev) +{ + WARN_ON(devres_release(dev, + devm_led_classdev_release, + devm_led_classdev_match, led_cdev)); +} +EXPORT_SYMBOL_GPL(devm_led_classdev_unregister); + static int __init leds_init(void) { leds_class = class_create(THIS_MODULE, "leds"); diff --git a/drivers/leds/leds-lp8501.c b/drivers/leds/leds-lp8501.c index 00f068b0fa6f..d3098e395fff 100644 --- a/drivers/leds/leds-lp8501.c +++ b/drivers/leds/leds-lp8501.c @@ -406,6 +406,6 @@ static struct i2c_driver lp8501_driver = { module_i2c_driver(lp8501_driver); -MODULE_DESCRIPTION("Texas Instruments LP8501 LED drvier"); +MODULE_DESCRIPTION("Texas Instruments LP8501 LED driver"); MODULE_AUTHOR("Milo Kim"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-lp8860.c b/drivers/leds/leds-lp8860.c index 840e93f3ab3e..8c2b7fbe2392 100644 --- a/drivers/leds/leds-lp8860.c +++ b/drivers/leds/leds-lp8860.c @@ -391,11 +391,13 @@ static int lp8860_probe(struct i2c_client *client, } } - led->enable_gpio = devm_gpiod_get(&client->dev, "enable"); - if (IS_ERR(led->enable_gpio)) - led->enable_gpio = NULL; - else - gpiod_direction_output(led->enable_gpio, 0); + led->enable_gpio = devm_gpiod_get_optional(&client->dev, + "enable", GPIOD_OUT_LOW); + if (IS_ERR(led->enable_gpio)) { + ret = PTR_ERR(led->enable_gpio); + dev_err(&client->dev, "Failed to get enable gpio: %d\n", ret); + return ret; + } led->regulator = devm_regulator_get(&client->dev, "vled"); if (IS_ERR(led->regulator)) @@ -486,6 +488,6 @@ static struct i2c_driver lp8860_driver = { }; module_i2c_driver(lp8860_driver); -MODULE_DESCRIPTION("Texas Instruments LP8860 LED drvier"); +MODULE_DESCRIPTION("Texas Instruments LP8860 LED driver"); MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/leds/leds-pca963x.c b/drivers/leds/leds-pca963x.c index f110b4c456ba..bee3e1ab27fd 100644 --- a/drivers/leds/leds-pca963x.c +++ b/drivers/leds/leds-pca963x.c @@ -289,7 +289,7 @@ pca963x_dt_init(struct i2c_client *client, struct pca963x_chipdef *chip) return ERR_PTR(-ENOMEM); for_each_child_of_node(np, child) { - struct led_info led; + struct led_info led = {}; u32 reg; int res; diff --git a/drivers/leds/leds-pm8941-wled.c b/drivers/leds/leds-pm8941-wled.c new file mode 100644 index 000000000000..bf64a593fbf1 --- /dev/null +++ b/drivers/leds/leds-pm8941-wled.c @@ -0,0 +1,435 @@ +/* Copyright (c) 2015, Sony Mobile Communications, AB. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/regmap.h> + +#define PM8941_WLED_REG_VAL_BASE 0x40 +#define PM8941_WLED_REG_VAL_MAX 0xFFF + +#define PM8941_WLED_REG_MOD_EN 0x46 +#define PM8941_WLED_REG_MOD_EN_BIT BIT(7) +#define PM8941_WLED_REG_MOD_EN_MASK BIT(7) + +#define PM8941_WLED_REG_SYNC 0x47 +#define PM8941_WLED_REG_SYNC_MASK 0x07 +#define PM8941_WLED_REG_SYNC_LED1 BIT(0) +#define PM8941_WLED_REG_SYNC_LED2 BIT(1) +#define PM8941_WLED_REG_SYNC_LED3 BIT(2) +#define PM8941_WLED_REG_SYNC_ALL 0x07 +#define PM8941_WLED_REG_SYNC_CLEAR 0x00 + +#define PM8941_WLED_REG_FREQ 0x4c +#define PM8941_WLED_REG_FREQ_MASK 0x0f + +#define PM8941_WLED_REG_OVP 0x4d +#define PM8941_WLED_REG_OVP_MASK 0x03 + +#define PM8941_WLED_REG_BOOST 0x4e +#define PM8941_WLED_REG_BOOST_MASK 0x07 + +#define PM8941_WLED_REG_SINK 0x4f +#define PM8941_WLED_REG_SINK_MASK 0xe0 +#define PM8941_WLED_REG_SINK_SHFT 0x05 + +/* Per-'string' registers below */ +#define PM8941_WLED_REG_STR_OFFSET 0x10 + +#define PM8941_WLED_REG_STR_MOD_EN_BASE 0x60 +#define PM8941_WLED_REG_STR_MOD_MASK BIT(7) +#define PM8941_WLED_REG_STR_MOD_EN BIT(7) + +#define PM8941_WLED_REG_STR_SCALE_BASE 0x62 +#define PM8941_WLED_REG_STR_SCALE_MASK 0x1f + +#define PM8941_WLED_REG_STR_MOD_SRC_BASE 0x63 +#define PM8941_WLED_REG_STR_MOD_SRC_MASK 0x01 +#define PM8941_WLED_REG_STR_MOD_SRC_INT 0x00 +#define PM8941_WLED_REG_STR_MOD_SRC_EXT 0x01 + +#define PM8941_WLED_REG_STR_CABC_BASE 0x66 +#define PM8941_WLED_REG_STR_CABC_MASK BIT(7) +#define PM8941_WLED_REG_STR_CABC_EN BIT(7) + +struct pm8941_wled_config { + u32 i_boost_limit; + u32 ovp; + u32 switch_freq; + u32 num_strings; + u32 i_limit; + bool cs_out_en; + bool ext_gen; + bool cabc_en; +}; + +struct pm8941_wled { + struct regmap *regmap; + u16 addr; + + struct led_classdev cdev; + + struct pm8941_wled_config cfg; +}; + +static int pm8941_wled_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct pm8941_wled *wled; + u8 ctrl = 0; + u16 val; + int rc; + int i; + + wled = container_of(cdev, struct pm8941_wled, cdev); + + if (value != 0) + ctrl = PM8941_WLED_REG_MOD_EN_BIT; + + val = value * PM8941_WLED_REG_VAL_MAX / LED_FULL; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_MOD_EN, + PM8941_WLED_REG_MOD_EN_MASK, ctrl); + if (rc) + return rc; + + for (i = 0; i < wled->cfg.num_strings; ++i) { + u8 v[2] = { val & 0xff, (val >> 8) & 0xf }; + + rc = regmap_bulk_write(wled->regmap, + wled->addr + PM8941_WLED_REG_VAL_BASE + 2 * i, + v, 2); + if (rc) + return rc; + } + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SYNC, + PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_ALL); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SYNC, + PM8941_WLED_REG_SYNC_MASK, PM8941_WLED_REG_SYNC_CLEAR); + return rc; +} + +static void pm8941_wled_set_brightness(struct led_classdev *cdev, + enum led_brightness value) +{ + if (pm8941_wled_set(cdev, value)) { + dev_err(cdev->dev, "Unable to set brightness\n"); + return; + } + cdev->brightness = value; +} + +static int pm8941_wled_setup(struct pm8941_wled *wled) +{ + int rc; + int i; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_OVP, + PM8941_WLED_REG_OVP_MASK, wled->cfg.ovp); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_BOOST, + PM8941_WLED_REG_BOOST_MASK, wled->cfg.i_boost_limit); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_FREQ, + PM8941_WLED_REG_FREQ_MASK, wled->cfg.switch_freq); + if (rc) + return rc; + + if (wled->cfg.cs_out_en) { + u8 all = (BIT(wled->cfg.num_strings) - 1) + << PM8941_WLED_REG_SINK_SHFT; + + rc = regmap_update_bits(wled->regmap, + wled->addr + PM8941_WLED_REG_SINK, + PM8941_WLED_REG_SINK_MASK, all); + if (rc) + return rc; + } + + for (i = 0; i < wled->cfg.num_strings; ++i) { + u16 addr = wled->addr + PM8941_WLED_REG_STR_OFFSET * i; + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_MOD_EN_BASE, + PM8941_WLED_REG_STR_MOD_MASK, + PM8941_WLED_REG_STR_MOD_EN); + if (rc) + return rc; + + if (wled->cfg.ext_gen) { + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_MOD_SRC_BASE, + PM8941_WLED_REG_STR_MOD_SRC_MASK, + PM8941_WLED_REG_STR_MOD_SRC_EXT); + if (rc) + return rc; + } + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_SCALE_BASE, + PM8941_WLED_REG_STR_SCALE_MASK, + wled->cfg.i_limit); + if (rc) + return rc; + + rc = regmap_update_bits(wled->regmap, + addr + PM8941_WLED_REG_STR_CABC_BASE, + PM8941_WLED_REG_STR_CABC_MASK, + wled->cfg.cabc_en ? + PM8941_WLED_REG_STR_CABC_EN : 0); + if (rc) + return rc; + } + + return 0; +} + +static const struct pm8941_wled_config pm8941_wled_config_defaults = { + .i_boost_limit = 3, + .i_limit = 20, + .ovp = 2, + .switch_freq = 5, + .num_strings = 0, + .cs_out_en = false, + .ext_gen = false, + .cabc_en = false, +}; + +struct pm8941_wled_var_cfg { + const u32 *values; + u32 (*fn)(u32); + int size; +}; + +static const u32 pm8941_wled_i_boost_limit_values[] = { + 105, 385, 525, 805, 980, 1260, 1400, 1680, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_i_boost_limit_cfg = { + .values = pm8941_wled_i_boost_limit_values, + .size = ARRAY_SIZE(pm8941_wled_i_boost_limit_values), +}; + +static const u32 pm8941_wled_ovp_values[] = { + 35, 32, 29, 27, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_ovp_cfg = { + .values = pm8941_wled_ovp_values, + .size = ARRAY_SIZE(pm8941_wled_ovp_values), +}; + +static u32 pm8941_wled_num_strings_values_fn(u32 idx) +{ + return idx + 1; +} + +static const struct pm8941_wled_var_cfg pm8941_wled_num_strings_cfg = { + .fn = pm8941_wled_num_strings_values_fn, + .size = 3, +}; + +static u32 pm8941_wled_switch_freq_values_fn(u32 idx) +{ + return 19200 / (2 * (1 + idx)); +} + +static const struct pm8941_wled_var_cfg pm8941_wled_switch_freq_cfg = { + .fn = pm8941_wled_switch_freq_values_fn, + .size = 16, +}; + +static const struct pm8941_wled_var_cfg pm8941_wled_i_limit_cfg = { + .size = 26, +}; + +static u32 pm8941_wled_values(const struct pm8941_wled_var_cfg *cfg, u32 idx) +{ + if (idx >= cfg->size) + return UINT_MAX; + if (cfg->fn) + return cfg->fn(idx); + if (cfg->values) + return cfg->values[idx]; + return idx; +} + +static int pm8941_wled_configure(struct pm8941_wled *wled, struct device *dev) +{ + struct pm8941_wled_config *cfg = &wled->cfg; + u32 val; + int rc; + u32 c; + int i; + int j; + + const struct { + const char *name; + u32 *val_ptr; + const struct pm8941_wled_var_cfg *cfg; + } u32_opts[] = { + { + "qcom,current-boost-limit", + &cfg->i_boost_limit, + .cfg = &pm8941_wled_i_boost_limit_cfg, + }, + { + "qcom,current-limit", + &cfg->i_limit, + .cfg = &pm8941_wled_i_limit_cfg, + }, + { + "qcom,ovp", + &cfg->ovp, + .cfg = &pm8941_wled_ovp_cfg, + }, + { + "qcom,switching-freq", + &cfg->switch_freq, + .cfg = &pm8941_wled_switch_freq_cfg, + }, + { + "qcom,num-strings", + &cfg->num_strings, + .cfg = &pm8941_wled_num_strings_cfg, + }, + }; + const struct { + const char *name; + bool *val_ptr; + } bool_opts[] = { + { "qcom,cs-out", &cfg->cs_out_en, }, + { "qcom,ext-gen", &cfg->ext_gen, }, + { "qcom,cabc", &cfg->cabc_en, }, + }; + + rc = of_property_read_u32(dev->of_node, "reg", &val); + if (rc || val > 0xffff) { + dev_err(dev, "invalid IO resources\n"); + return rc ? rc : -EINVAL; + } + wled->addr = val; + + rc = of_property_read_string(dev->of_node, "label", &wled->cdev.name); + if (rc) + wled->cdev.name = dev->of_node->name; + + wled->cdev.default_trigger = of_get_property(dev->of_node, + "linux,default-trigger", NULL); + + *cfg = pm8941_wled_config_defaults; + for (i = 0; i < ARRAY_SIZE(u32_opts); ++i) { + rc = of_property_read_u32(dev->of_node, u32_opts[i].name, &val); + if (rc == -EINVAL) { + continue; + } else if (rc) { + dev_err(dev, "error reading '%s'\n", u32_opts[i].name); + return rc; + } + + c = UINT_MAX; + for (j = 0; c != val; j++) { + c = pm8941_wled_values(u32_opts[i].cfg, j); + if (c == UINT_MAX) { + dev_err(dev, "invalid value for '%s'\n", + u32_opts[i].name); + return -EINVAL; + } + } + + dev_dbg(dev, "'%s' = %u\n", u32_opts[i].name, c); + *u32_opts[i].val_ptr = j; + } + + for (i = 0; i < ARRAY_SIZE(bool_opts); ++i) { + if (of_property_read_bool(dev->of_node, bool_opts[i].name)) + *bool_opts[i].val_ptr = true; + } + + cfg->num_strings = cfg->num_strings + 1; + + return 0; +} + +static int pm8941_wled_probe(struct platform_device *pdev) +{ + struct pm8941_wled *wled; + struct regmap *regmap; + int rc; + + regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!regmap) { + dev_err(&pdev->dev, "Unable to get regmap\n"); + return -EINVAL; + } + + wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL); + if (!wled) + return -ENOMEM; + + wled->regmap = regmap; + + rc = pm8941_wled_configure(wled, &pdev->dev); + if (rc) + return rc; + + rc = pm8941_wled_setup(wled); + if (rc) + return rc; + + wled->cdev.brightness_set = pm8941_wled_set_brightness; + + rc = devm_led_classdev_register(&pdev->dev, &wled->cdev); + if (rc) + return rc; + + platform_set_drvdata(pdev, wled); + + return 0; +}; + +static const struct of_device_id pm8941_wled_match_table[] = { + { .compatible = "qcom,pm8941-wled" }, + {} +}; +MODULE_DEVICE_TABLE(of, pm8941_wled_match_table); + +static struct platform_driver pm8941_wled_driver = { + .probe = pm8941_wled_probe, + .driver = { + .name = "pm8941-wled", + .of_match_table = pm8941_wled_match_table, + }, +}; + +module_platform_driver(pm8941_wled_driver); + +MODULE_DESCRIPTION("pm8941 wled driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:pm8941-wled"); diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index f668500a2157..1d07e3e83d29 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -121,9 +121,6 @@ static int led_pwm_add(struct device *dev, struct led_pwm_priv *priv, return ret; } - if (child) - led_data->period = pwm_get_period(led_data->pwm); - led_data->can_sleep = pwm_can_sleep(led_data->pwm); if (led_data->can_sleep) INIT_WORK(&led_data->work, led_pwm_work); diff --git a/drivers/mtd/ubi/attach.c b/drivers/mtd/ubi/attach.c index 9d2e16f3150a..68eea5befaf1 100644 --- a/drivers/mtd/ubi/attach.c +++ b/drivers/mtd/ubi/attach.c @@ -410,7 +410,7 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, second_is_newer = !second_is_newer; } else { dbg_bld("PEB %d CRC is OK", pnum); - bitflips = !!err; + bitflips |= !!err; } mutex_unlock(&ubi->buf_mutex); @@ -1301,6 +1301,30 @@ out_ech: return err; } +static struct ubi_attach_info *alloc_ai(void) +{ + struct ubi_attach_info *ai; + + ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL); + if (!ai) + return ai; + + INIT_LIST_HEAD(&ai->corr); + INIT_LIST_HEAD(&ai->free); + INIT_LIST_HEAD(&ai->erase); + INIT_LIST_HEAD(&ai->alien); + ai->volumes = RB_ROOT; + ai->aeb_slab_cache = kmem_cache_create("ubi_aeb_slab_cache", + sizeof(struct ubi_ainf_peb), + 0, 0, NULL); + if (!ai->aeb_slab_cache) { + kfree(ai); + ai = NULL; + } + + return ai; +} + #ifdef CONFIG_MTD_UBI_FASTMAP /** @@ -1313,7 +1337,7 @@ out_ech: * UBI_NO_FASTMAP denotes that no fastmap was found. * UBI_BAD_FASTMAP denotes that the found fastmap was invalid. */ -static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai) +static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info **ai) { int err, pnum, fm_anchor = -1; unsigned long long max_sqnum = 0; @@ -1334,7 +1358,7 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai) cond_resched(); dbg_gen("process PEB %d", pnum); - err = scan_peb(ubi, ai, pnum, &vol_id, &sqnum); + err = scan_peb(ubi, *ai, pnum, &vol_id, &sqnum); if (err < 0) goto out_vidh; @@ -1350,7 +1374,12 @@ static int scan_fast(struct ubi_device *ubi, struct ubi_attach_info *ai) if (fm_anchor < 0) return UBI_NO_FASTMAP; - return ubi_scan_fastmap(ubi, ai, fm_anchor); + destroy_ai(*ai); + *ai = alloc_ai(); + if (!*ai) + return -ENOMEM; + + return ubi_scan_fastmap(ubi, *ai, fm_anchor); out_vidh: ubi_free_vid_hdr(ubi, vidh); @@ -1362,30 +1391,6 @@ out: #endif -static struct ubi_attach_info *alloc_ai(const char *slab_name) -{ - struct ubi_attach_info *ai; - - ai = kzalloc(sizeof(struct ubi_attach_info), GFP_KERNEL); - if (!ai) - return ai; - - INIT_LIST_HEAD(&ai->corr); - INIT_LIST_HEAD(&ai->free); - INIT_LIST_HEAD(&ai->erase); - INIT_LIST_HEAD(&ai->alien); - ai->volumes = RB_ROOT; - ai->aeb_slab_cache = kmem_cache_create(slab_name, - sizeof(struct ubi_ainf_peb), - 0, 0, NULL); - if (!ai->aeb_slab_cache) { - kfree(ai); - ai = NULL; - } - - return ai; -} - /** * ubi_attach - attach an MTD device. * @ubi: UBI device descriptor @@ -1399,7 +1404,7 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) int err; struct ubi_attach_info *ai; - ai = alloc_ai("ubi_aeb_slab_cache"); + ai = alloc_ai(); if (!ai) return -ENOMEM; @@ -1413,11 +1418,11 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) if (force_scan) err = scan_all(ubi, ai, 0); else { - err = scan_fast(ubi, ai); - if (err > 0) { + err = scan_fast(ubi, &ai); + if (err > 0 || mtd_is_eccerr(err)) { if (err != UBI_NO_FASTMAP) { destroy_ai(ai); - ai = alloc_ai("ubi_aeb_slab_cache2"); + ai = alloc_ai(); if (!ai) return -ENOMEM; @@ -1453,10 +1458,10 @@ int ubi_attach(struct ubi_device *ubi, int force_scan) goto out_wl; #ifdef CONFIG_MTD_UBI_FASTMAP - if (ubi->fm && ubi_dbg_chk_gen(ubi)) { + if (ubi->fm && ubi_dbg_chk_fastmap(ubi)) { struct ubi_attach_info *scan_ai; - scan_ai = alloc_ai("ubi_ckh_aeb_slab_cache"); + scan_ai = alloc_ai(); if (!scan_ai) { err = -ENOMEM; goto out_wl; diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index ba01a8d22d28..9690cf9aaef5 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -81,6 +81,7 @@ static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES]; #ifdef CONFIG_MTD_UBI_FASTMAP /* UBI module parameter to enable fastmap automatically on non-fastmap images */ static bool fm_autoconvert; +static bool fm_debug; #endif /* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ struct class *ubi_class; @@ -154,23 +155,22 @@ static struct device_attribute dev_mtd_num = */ int ubi_volume_notify(struct ubi_device *ubi, struct ubi_volume *vol, int ntype) { + int ret; struct ubi_notification nt; ubi_do_get_device_info(ubi, &nt.di); ubi_do_get_volume_info(ubi, vol, &nt.vi); -#ifdef CONFIG_MTD_UBI_FASTMAP switch (ntype) { case UBI_VOLUME_ADDED: case UBI_VOLUME_REMOVED: case UBI_VOLUME_RESIZED: case UBI_VOLUME_RENAMED: - if (ubi_update_fastmap(ubi)) { - ubi_err(ubi, "Unable to update fastmap!"); - ubi_ro_mode(ubi); - } + ret = ubi_update_fastmap(ubi); + if (ret) + ubi_msg(ubi, "Unable to write a new fastmap: %i", ret); } -#endif + return blocking_notifier_call_chain(&ubi_notifiers, ntype, &nt); } @@ -950,8 +950,10 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, if (ubi->fm_pool.max_size < UBI_FM_MIN_POOL_SIZE) ubi->fm_pool.max_size = UBI_FM_MIN_POOL_SIZE; - ubi->fm_wl_pool.max_size = UBI_FM_WL_POOL_SIZE; + ubi->fm_wl_pool.max_size = ubi->fm_pool.max_size / 2; ubi->fm_disabled = !fm_autoconvert; + if (fm_debug) + ubi_enable_dbg_chk_fastmap(ubi); if (!ubi->fm_disabled && (int)mtd_div_by_eb(ubi->mtd->size, ubi->mtd) <= UBI_FM_MAX_START) { @@ -970,8 +972,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, mutex_init(&ubi->ckvol_mutex); mutex_init(&ubi->device_mutex); spin_lock_init(&ubi->volumes_lock); - mutex_init(&ubi->fm_mutex); - init_rwsem(&ubi->fm_sem); + init_rwsem(&ubi->fm_protect); + init_rwsem(&ubi->fm_eba_sem); ubi_msg(ubi, "attaching mtd%d", mtd->index); @@ -1115,8 +1117,11 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ubi_msg(ubi, "detaching mtd%d", ubi->mtd->index); #ifdef CONFIG_MTD_UBI_FASTMAP /* If we don't write a new fastmap at detach time we lose all - * EC updates that have been made since the last written fastmap. */ - ubi_update_fastmap(ubi); + * EC updates that have been made since the last written fastmap. + * In case of fastmap debugging we omit the update to simulate an + * unclean shutdown. */ + if (!ubi_dbg_chk_fastmap(ubi)) + ubi_update_fastmap(ubi); #endif /* * Before freeing anything, we have to stop the background thread to @@ -1501,6 +1506,8 @@ MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|pa #ifdef CONFIG_MTD_UBI_FASTMAP module_param(fm_autoconvert, bool, 0644); MODULE_PARM_DESC(fm_autoconvert, "Set this parameter to enable fastmap automatically on images without a fastmap."); +module_param(fm_debug, bool, 0); +MODULE_PARM_DESC(fm_debug, "Set this parameter to enable fastmap debugging by default. Warning, this will make fastmap slow!"); #endif MODULE_VERSION(__stringify(UBI_VERSION)); MODULE_DESCRIPTION("UBI - Unsorted Block Images"); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index d647e504f9b1..d16fccf79179 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -455,7 +455,7 @@ static long vol_cdev_ioctl(struct file *file, unsigned int cmd, /* Validate the request */ err = -EINVAL; if (req.lnum < 0 || req.lnum >= vol->reserved_pebs || - req.bytes < 0 || req.lnum >= vol->usable_leb_size) + req.bytes < 0 || req.bytes > vol->usable_leb_size) break; err = get_exclusive(desc); diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 7335c9ff9d99..b077e43b5ba9 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -263,7 +263,7 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, struct dentry *dent = file->f_path.dentry; struct ubi_device *ubi; struct ubi_debug_info *d; - char buf[3]; + char buf[8]; int val; ubi = ubi_get_device(ubi_num); @@ -275,12 +275,30 @@ static ssize_t dfs_file_read(struct file *file, char __user *user_buf, val = d->chk_gen; else if (dent == d->dfs_chk_io) val = d->chk_io; + else if (dent == d->dfs_chk_fastmap) + val = d->chk_fastmap; else if (dent == d->dfs_disable_bgt) val = d->disable_bgt; else if (dent == d->dfs_emulate_bitflips) val = d->emulate_bitflips; else if (dent == d->dfs_emulate_io_failures) val = d->emulate_io_failures; + else if (dent == d->dfs_emulate_power_cut) { + snprintf(buf, sizeof(buf), "%u\n", d->emulate_power_cut); + count = simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); + goto out; + } else if (dent == d->dfs_power_cut_min) { + snprintf(buf, sizeof(buf), "%u\n", d->power_cut_min); + count = simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); + goto out; + } else if (dent == d->dfs_power_cut_max) { + snprintf(buf, sizeof(buf), "%u\n", d->power_cut_max); + count = simple_read_from_buffer(user_buf, count, ppos, + buf, strlen(buf)); + goto out; + } else { count = -EINVAL; goto out; @@ -309,7 +327,7 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, struct ubi_device *ubi; struct ubi_debug_info *d; size_t buf_size; - char buf[8]; + char buf[8] = {0}; int val; ubi = ubi_get_device(ubi_num); @@ -323,6 +341,21 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, goto out; } + if (dent == d->dfs_power_cut_min) { + if (kstrtouint(buf, 0, &d->power_cut_min) != 0) + count = -EINVAL; + goto out; + } else if (dent == d->dfs_power_cut_max) { + if (kstrtouint(buf, 0, &d->power_cut_max) != 0) + count = -EINVAL; + goto out; + } else if (dent == d->dfs_emulate_power_cut) { + if (kstrtoint(buf, 0, &val) != 0) + count = -EINVAL; + d->emulate_power_cut = val; + goto out; + } + if (buf[0] == '1') val = 1; else if (buf[0] == '0') @@ -336,6 +369,8 @@ static ssize_t dfs_file_write(struct file *file, const char __user *user_buf, d->chk_gen = val; else if (dent == d->dfs_chk_io) d->chk_io = val; + else if (dent == d->dfs_chk_fastmap) + d->chk_fastmap = val; else if (dent == d->dfs_disable_bgt) d->disable_bgt = val; else if (dent == d->dfs_emulate_bitflips) @@ -406,6 +441,13 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi) goto out_remove; d->dfs_chk_io = dent; + fname = "chk_fastmap"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_chk_fastmap = dent; + fname = "tst_disable_bgt"; dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, &dfs_fops); @@ -427,6 +469,27 @@ int ubi_debugfs_init_dev(struct ubi_device *ubi) goto out_remove; d->dfs_emulate_io_failures = dent; + fname = "tst_emulate_power_cut"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_emulate_power_cut = dent; + + fname = "tst_emulate_power_cut_min"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_power_cut_min = dent; + + fname = "tst_emulate_power_cut_max"; + dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num, + &dfs_fops); + if (IS_ERR_OR_NULL(dent)) + goto out_remove; + d->dfs_power_cut_max = dent; + return 0; out_remove: @@ -447,3 +510,36 @@ void ubi_debugfs_exit_dev(struct ubi_device *ubi) if (IS_ENABLED(CONFIG_DEBUG_FS)) debugfs_remove_recursive(ubi->dbg.dfs_dir); } + +/** + * ubi_dbg_power_cut - emulate a power cut if it is time to do so + * @ubi: UBI device description object + * @caller: Flags set to indicate from where the function is being called + * + * Returns non-zero if a power cut was emulated, zero if not. + */ +int ubi_dbg_power_cut(struct ubi_device *ubi, int caller) +{ + unsigned int range; + + if ((ubi->dbg.emulate_power_cut & caller) == 0) + return 0; + + if (ubi->dbg.power_cut_counter == 0) { + ubi->dbg.power_cut_counter = ubi->dbg.power_cut_min; + + if (ubi->dbg.power_cut_max > ubi->dbg.power_cut_min) { + range = ubi->dbg.power_cut_max - ubi->dbg.power_cut_min; + ubi->dbg.power_cut_counter += prandom_u32() % range; + } + return 0; + } + + ubi->dbg.power_cut_counter--; + if (ubi->dbg.power_cut_counter) + return 0; + + ubi_msg(ubi, "XXXXXXXXXXXXXXX emulating a power cut XXXXXXXXXXXXXXXX"); + ubi_ro_mode(ubi); + return 1; +} diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index cba89fcd1587..eb8985e5c178 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -127,4 +127,16 @@ static inline int ubi_dbg_chk_gen(const struct ubi_device *ubi) { return ubi->dbg.chk_gen; } + +static inline int ubi_dbg_chk_fastmap(const struct ubi_device *ubi) +{ + return ubi->dbg.chk_fastmap; +} + +static inline void ubi_enable_dbg_chk_fastmap(struct ubi_device *ubi) +{ + ubi->dbg.chk_fastmap = 1; +} + +int ubi_dbg_power_cut(struct ubi_device *ubi, int caller); #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 16e34b37d134..51bca035cd83 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -340,9 +340,9 @@ int ubi_eba_unmap_leb(struct ubi_device *ubi, struct ubi_volume *vol, dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); - down_read(&ubi->fm_sem); + down_read(&ubi->fm_eba_sem); vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; - up_read(&ubi->fm_sem); + up_read(&ubi->fm_eba_sem); err = ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 0); out_unlock: @@ -567,6 +567,7 @@ retry: new_pnum = ubi_wl_get_peb(ubi); if (new_pnum < 0) { ubi_free_vid_hdr(ubi, vid_hdr); + up_read(&ubi->fm_eba_sem); return new_pnum; } @@ -577,13 +578,16 @@ retry: if (err && err != UBI_IO_BITFLIPS) { if (err > 0) err = -EIO; + up_read(&ubi->fm_eba_sem); goto out_put; } vid_hdr->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); - if (err) + if (err) { + up_read(&ubi->fm_eba_sem); goto write_error; + } data_size = offset + len; mutex_lock(&ubi->buf_mutex); @@ -592,8 +596,10 @@ retry: /* Read everything before the area where the write failure happened */ if (offset > 0) { err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset); - if (err && err != UBI_IO_BITFLIPS) + if (err && err != UBI_IO_BITFLIPS) { + up_read(&ubi->fm_eba_sem); goto out_unlock; + } } memcpy(ubi->peb_buf + offset, buf, len); @@ -601,15 +607,15 @@ retry: err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size); if (err) { mutex_unlock(&ubi->buf_mutex); + up_read(&ubi->fm_eba_sem); goto write_error; } mutex_unlock(&ubi->buf_mutex); ubi_free_vid_hdr(ubi, vid_hdr); - down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = new_pnum; - up_read(&ubi->fm_sem); + up_read(&ubi->fm_eba_sem); ubi_wl_put_peb(ubi, vol_id, lnum, pnum, 1); ubi_msg(ubi, "data was successfully recovered"); @@ -704,6 +710,7 @@ retry: if (pnum < 0) { ubi_free_vid_hdr(ubi, vid_hdr); leb_write_unlock(ubi, vol_id, lnum); + up_read(&ubi->fm_eba_sem); return pnum; } @@ -714,6 +721,7 @@ retry: if (err) { ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", vol_id, lnum, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } @@ -722,13 +730,13 @@ retry: if (err) { ubi_warn(ubi, "failed to write %d bytes at offset %d of LEB %d:%d, PEB %d", len, offset, vol_id, lnum, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } } - down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_sem); + up_read(&ubi->fm_eba_sem); leb_write_unlock(ubi, vol_id, lnum); ubi_free_vid_hdr(ubi, vid_hdr); @@ -825,6 +833,7 @@ retry: if (pnum < 0) { ubi_free_vid_hdr(ubi, vid_hdr); leb_write_unlock(ubi, vol_id, lnum); + up_read(&ubi->fm_eba_sem); return pnum; } @@ -835,6 +844,7 @@ retry: if (err) { ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", vol_id, lnum, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } @@ -842,13 +852,13 @@ retry: if (err) { ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", len, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } ubi_assert(vol->eba_tbl[lnum] < 0); - down_read(&ubi->fm_sem); vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_sem); + up_read(&ubi->fm_eba_sem); leb_write_unlock(ubi, vol_id, lnum); ubi_free_vid_hdr(ubi, vid_hdr); @@ -900,7 +910,7 @@ write_error: int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol, int lnum, const void *buf, int len) { - int err, pnum, tries = 0, vol_id = vol->vol_id; + int err, pnum, old_pnum, tries = 0, vol_id = vol->vol_id; struct ubi_vid_hdr *vid_hdr; uint32_t crc; @@ -943,6 +953,7 @@ retry: pnum = ubi_wl_get_peb(ubi); if (pnum < 0) { err = pnum; + up_read(&ubi->fm_eba_sem); goto out_leb_unlock; } @@ -953,6 +964,7 @@ retry: if (err) { ubi_warn(ubi, "failed to write VID header to LEB %d:%d, PEB %d", vol_id, lnum, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } @@ -960,19 +972,20 @@ retry: if (err) { ubi_warn(ubi, "failed to write %d bytes of data to PEB %d", len, pnum); + up_read(&ubi->fm_eba_sem); goto write_error; } - if (vol->eba_tbl[lnum] >= 0) { - err = ubi_wl_put_peb(ubi, vol_id, lnum, vol->eba_tbl[lnum], 0); + old_pnum = vol->eba_tbl[lnum]; + vol->eba_tbl[lnum] = pnum; + up_read(&ubi->fm_eba_sem); + + if (old_pnum >= 0) { + err = ubi_wl_put_peb(ubi, vol_id, lnum, old_pnum, 0); if (err) goto out_leb_unlock; } - down_read(&ubi->fm_sem); - vol->eba_tbl[lnum] = pnum; - up_read(&ubi->fm_sem); - out_leb_unlock: leb_write_unlock(ubi, vol_id, lnum); out_mutex: @@ -1218,9 +1231,9 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, } ubi_assert(vol->eba_tbl[lnum] == from); - down_read(&ubi->fm_sem); + down_read(&ubi->fm_eba_sem); vol->eba_tbl[lnum] = to; - up_read(&ubi->fm_sem); + up_read(&ubi->fm_eba_sem); out_unlock_buf: mutex_unlock(&ubi->buf_mutex); @@ -1419,7 +1432,8 @@ int ubi_eba_init(struct ubi_device *ubi, struct ubi_attach_info *ai) * during re-size. */ ubi_move_aeb_to_list(av, aeb, &ai->erase); - vol->eba_tbl[aeb->lnum] = aeb->pnum; + else + vol->eba_tbl[aeb->lnum] = aeb->pnum; } } diff --git a/drivers/mtd/ubi/fastmap-wl.c b/drivers/mtd/ubi/fastmap-wl.c new file mode 100644 index 000000000000..b2a665398bca --- /dev/null +++ b/drivers/mtd/ubi/fastmap-wl.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Linutronix GmbH + * Copyright (c) 2014 sigma star gmbh + * Author: Richard Weinberger <richard@nod.at> + * + * 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. + * + * 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. + * + */ + +/** + * update_fastmap_work_fn - calls ubi_update_fastmap from a work queue + * @wrk: the work description object + */ +static void update_fastmap_work_fn(struct work_struct *wrk) +{ + struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work); + + ubi_update_fastmap(ubi); + spin_lock(&ubi->wl_lock); + ubi->fm_work_scheduled = 0; + spin_unlock(&ubi->wl_lock); +} + +/** + * find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB. + * @root: the RB-tree where to look for + */ +static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root) +{ + struct rb_node *p; + struct ubi_wl_entry *e, *victim = NULL; + int max_ec = UBI_MAX_ERASECOUNTER; + + ubi_rb_for_each_entry(p, e, root, u.rb) { + if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) { + victim = e; + max_ec = e->ec; + } + } + + return victim; +} + +/** + * return_unused_pool_pebs - returns unused PEB to the free tree. + * @ubi: UBI device description object + * @pool: fastmap pool description object + */ +static void return_unused_pool_pebs(struct ubi_device *ubi, + struct ubi_fm_pool *pool) +{ + int i; + struct ubi_wl_entry *e; + + for (i = pool->used; i < pool->size; i++) { + e = ubi->lookuptbl[pool->pebs[i]]; + wl_tree_add(e, &ubi->free); + ubi->free_count++; + } +} + +static int anchor_pebs_avalible(struct rb_root *root) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + ubi_rb_for_each_entry(p, e, root, u.rb) + if (e->pnum < UBI_FM_MAX_START) + return 1; + + return 0; +} + +/** + * ubi_wl_get_fm_peb - find a physical erase block with a given maximal number. + * @ubi: UBI device description object + * @anchor: This PEB will be used as anchor PEB by fastmap + * + * The function returns a physical erase block with a given maximal number + * and removes it from the wl subsystem. + * Must be called with wl_lock held! + */ +struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor) +{ + struct ubi_wl_entry *e = NULL; + + if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1)) + goto out; + + if (anchor) + e = find_anchor_wl_entry(&ubi->free); + else + e = find_mean_wl_entry(ubi, &ubi->free); + + if (!e) + goto out; + + self_check_in_wl_tree(ubi, e, &ubi->free); + + /* remove it from the free list, + * the wl subsystem does no longer know this erase block */ + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; +out: + return e; +} + +/** + * ubi_refill_pools - refills all fastmap PEB pools. + * @ubi: UBI device description object + */ +void ubi_refill_pools(struct ubi_device *ubi) +{ + struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool; + struct ubi_fm_pool *pool = &ubi->fm_pool; + struct ubi_wl_entry *e; + int enough; + + spin_lock(&ubi->wl_lock); + + return_unused_pool_pebs(ubi, wl_pool); + return_unused_pool_pebs(ubi, pool); + + wl_pool->size = 0; + pool->size = 0; + + for (;;) { + enough = 0; + if (pool->size < pool->max_size) { + if (!ubi->free.rb_node) + break; + + e = wl_get_wle(ubi); + if (!e) + break; + + pool->pebs[pool->size] = e->pnum; + pool->size++; + } else + enough++; + + if (wl_pool->size < wl_pool->max_size) { + if (!ubi->free.rb_node || + (ubi->free_count - ubi->beb_rsvd_pebs < 5)) + break; + + e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + self_check_in_wl_tree(ubi, e, &ubi->free); + rb_erase(&e->u.rb, &ubi->free); + ubi->free_count--; + + wl_pool->pebs[wl_pool->size] = e->pnum; + wl_pool->size++; + } else + enough++; + + if (enough == 2) + break; + } + + wl_pool->used = 0; + pool->used = 0; + + spin_unlock(&ubi->wl_lock); +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. + * Returns with ubi->fm_eba_sem held in read mode! + */ +int ubi_wl_get_peb(struct ubi_device *ubi) +{ + int ret, retried = 0; + struct ubi_fm_pool *pool = &ubi->fm_pool; + struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool; + +again: + down_read(&ubi->fm_eba_sem); + spin_lock(&ubi->wl_lock); + + /* We check here also for the WL pool because at this point we can + * refill the WL pool synchronous. */ + if (pool->used == pool->size || wl_pool->used == wl_pool->size) { + spin_unlock(&ubi->wl_lock); + up_read(&ubi->fm_eba_sem); + ret = ubi_update_fastmap(ubi); + if (ret) { + ubi_msg(ubi, "Unable to write a new fastmap: %i", ret); + down_read(&ubi->fm_eba_sem); + return -ENOSPC; + } + down_read(&ubi->fm_eba_sem); + spin_lock(&ubi->wl_lock); + } + + if (pool->used == pool->size) { + spin_unlock(&ubi->wl_lock); + if (retried) { + ubi_err(ubi, "Unable to get a free PEB from user WL pool"); + ret = -ENOSPC; + goto out; + } + retried = 1; + up_read(&ubi->fm_eba_sem); + goto again; + } + + ubi_assert(pool->used < pool->size); + ret = pool->pebs[pool->used++]; + prot_queue_add(ubi, ubi->lookuptbl[ret]); + spin_unlock(&ubi->wl_lock); +out: + return ret; +} + +/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system. + * + * @ubi: UBI device description object + */ +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) +{ + struct ubi_fm_pool *pool = &ubi->fm_wl_pool; + int pnum; + + if (pool->used == pool->size) { + /* We cannot update the fastmap here because this + * function is called in atomic context. + * Let's fail here and refill/update it as soon as possible. */ + if (!ubi->fm_work_scheduled) { + ubi->fm_work_scheduled = 1; + schedule_work(&ubi->fm_work); + } + return NULL; + } + + pnum = pool->pebs[pool->used++]; + return ubi->lookuptbl[pnum]; +} + +/** + * ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB. + * @ubi: UBI device description object + */ +int ubi_ensure_anchor_pebs(struct ubi_device *ubi) +{ + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) { + spin_unlock(&ubi->wl_lock); + return 0; + } + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); + if (!wrk) { + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + return -ENOMEM; + } + + wrk->anchor = 1; + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return 0; +} + +/** + * ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling + * sub-system. + * see: ubi_wl_put_peb() + * + * @ubi: UBI device description object + * @fm_e: physical eraseblock to return + * @lnum: the last used logical eraseblock number for the PEB + * @torture: if this physical eraseblock has to be tortured + */ +int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, + int lnum, int torture) +{ + struct ubi_wl_entry *e; + int vol_id, pnum = fm_e->pnum; + + dbg_wl("PEB %d", pnum); + + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + + /* This can happen if we recovered from a fastmap the very + * first time and writing now a new one. In this case the wl system + * has never seen any PEB used by the original fastmap. + */ + if (!e) { + e = fm_e; + ubi_assert(e->ec >= 0); + ubi->lookuptbl[pnum] = e; + } + + spin_unlock(&ubi->wl_lock); + + vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; + return schedule_erase(ubi, e, vol_id, lnum, torture); +} + +/** + * ubi_is_erase_work - checks whether a work is erase work. + * @wrk: The work object to be checked + */ +int ubi_is_erase_work(struct ubi_work *wrk) +{ + return wrk->func == erase_worker; +} + +static void ubi_fastmap_close(struct ubi_device *ubi) +{ + int i; + + flush_work(&ubi->fm_work); + return_unused_pool_pebs(ubi, &ubi->fm_pool); + return_unused_pool_pebs(ubi, &ubi->fm_wl_pool); + + if (ubi->fm) { + for (i = 0; i < ubi->fm->used_blocks; i++) + kfree(ubi->fm->e[i]); + } + kfree(ubi->fm); +} + +/** + * may_reserve_for_fm - tests whether a PEB shall be reserved for fastmap. + * See find_mean_wl_entry() + * + * @ubi: UBI device description object + * @e: physical eraseblock to return + * @root: RB tree to test against. + */ +static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi, + struct ubi_wl_entry *e, + struct rb_root *root) { + if (e && !ubi->fm_disabled && !ubi->fm && + e->pnum < UBI_FM_MAX_START) + e = rb_entry(rb_next(root->rb_node), + struct ubi_wl_entry, u.rb); + + return e; +} diff --git a/drivers/mtd/ubi/fastmap.c b/drivers/mtd/ubi/fastmap.c index db3defdfc3c0..02a6de2f53ee 100644 --- a/drivers/mtd/ubi/fastmap.c +++ b/drivers/mtd/ubi/fastmap.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2012 Linutronix GmbH + * Copyright (c) 2014 sigma star gmbh * Author: Richard Weinberger <richard@nod.at> * * This program is free software; you can redistribute it and/or modify @@ -17,6 +18,69 @@ #include "ubi.h" /** + * init_seen - allocate memory for used for debugging. + * @ubi: UBI device description object + */ +static inline int *init_seen(struct ubi_device *ubi) +{ + int *ret; + + if (!ubi_dbg_chk_fastmap(ubi)) + return NULL; + + ret = kcalloc(ubi->peb_count, sizeof(int), GFP_KERNEL); + if (!ret) + return ERR_PTR(-ENOMEM); + + return ret; +} + +/** + * free_seen - free the seen logic integer array. + * @seen: integer array of @ubi->peb_count size + */ +static inline void free_seen(int *seen) +{ + kfree(seen); +} + +/** + * set_seen - mark a PEB as seen. + * @ubi: UBI device description object + * @pnum: The PEB to be makred as seen + * @seen: integer array of @ubi->peb_count size + */ +static inline void set_seen(struct ubi_device *ubi, int pnum, int *seen) +{ + if (!ubi_dbg_chk_fastmap(ubi) || !seen) + return; + + seen[pnum] = 1; +} + +/** + * self_check_seen - check whether all PEB have been seen by fastmap. + * @ubi: UBI device description object + * @seen: integer array of @ubi->peb_count size + */ +static int self_check_seen(struct ubi_device *ubi, int *seen) +{ + int pnum, ret = 0; + + if (!ubi_dbg_chk_fastmap(ubi) || !seen) + return 0; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + if (!seen[pnum] && ubi->lookuptbl[pnum]) { + ubi_err(ubi, "self-check failed for PEB %d, fastmap didn't see it", pnum); + ret = -EINVAL; + } + } + + return ret; +} + +/** * ubi_calc_fm_size - calculates the fastmap size in bytes for an UBI device. * @ubi: UBI device description object */ @@ -136,14 +200,15 @@ static struct ubi_ainf_volume *add_vol(struct ubi_attach_info *ai, int vol_id, if (!av) goto out; - av->highest_lnum = av->leb_count = 0; + av->highest_lnum = av->leb_count = av->used_ebs = 0; av->vol_id = vol_id; - av->used_ebs = used_ebs; av->data_pad = data_pad; av->last_data_size = last_eb_bytes; av->compat = 0; av->vol_type = vol_type; av->root = RB_ROOT; + if (av->vol_type == UBI_STATIC_VOLUME) + av->used_ebs = used_ebs; dbg_bld("found volume (ID %i)", vol_id); @@ -362,6 +427,7 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum) aeb = rb_entry(node2, struct ubi_ainf_peb, u.rb); if (aeb->pnum == pnum) { rb_erase(&aeb->u.rb, &av->root); + av->leb_count--; kmem_cache_free(ai->aeb_slab_cache, aeb); return; } @@ -376,7 +442,6 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum) * @pebs: an array of all PEB numbers in the to be scanned pool * @pool_size: size of the pool (number of entries in @pebs) * @max_sqnum: pointer to the maximal sequence number - * @eba_orphans: list of PEBs which need to be scanned * @free: list of PEBs which are most likely free (and go into @ai->free) * * Returns 0 on success, if the pool is unusable UBI_BAD_FASTMAP is returned. @@ -384,12 +449,12 @@ static void unmap_peb(struct ubi_attach_info *ai, int pnum) */ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, int *pebs, int pool_size, unsigned long long *max_sqnum, - struct list_head *eba_orphans, struct list_head *free) + struct list_head *free) { struct ubi_vid_hdr *vh; struct ubi_ec_hdr *ech; - struct ubi_ainf_peb *new_aeb, *tmp_aeb; - int i, pnum, err, found_orphan, ret = 0; + struct ubi_ainf_peb *new_aeb; + int i, pnum, err, ret = 0; ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); if (!ech) @@ -457,18 +522,6 @@ static int scan_pool(struct ubi_device *ubi, struct ubi_attach_info *ai, if (err == UBI_IO_BITFLIPS) scrub = 1; - found_orphan = 0; - list_for_each_entry(tmp_aeb, eba_orphans, u.list) { - if (tmp_aeb->pnum == pnum) { - found_orphan = 1; - break; - } - } - if (found_orphan) { - list_del(&tmp_aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); - } - new_aeb = kmem_cache_alloc(ai->aeb_slab_cache, GFP_KERNEL); if (!new_aeb) { @@ -543,10 +596,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, struct ubi_fastmap_layout *fm) { - struct list_head used, eba_orphans, free; + struct list_head used, free; struct ubi_ainf_volume *av; struct ubi_ainf_peb *aeb, *tmp_aeb, *_tmp_aeb; - struct ubi_ec_hdr *ech; struct ubi_fm_sb *fmsb; struct ubi_fm_hdr *fmhdr; struct ubi_fm_scan_pool *fmpl1, *fmpl2; @@ -560,22 +612,8 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, INIT_LIST_HEAD(&used); INIT_LIST_HEAD(&free); - INIT_LIST_HEAD(&eba_orphans); - INIT_LIST_HEAD(&ai->corr); - INIT_LIST_HEAD(&ai->free); - INIT_LIST_HEAD(&ai->erase); - INIT_LIST_HEAD(&ai->alien); - ai->volumes = RB_ROOT; ai->min_ec = UBI_MAX_ERASECOUNTER; - ai->aeb_slab_cache = kmem_cache_create("ubi_ainf_peb_slab", - sizeof(struct ubi_ainf_peb), - 0, 0, NULL); - if (!ai->aeb_slab_cache) { - ret = -ENOMEM; - goto fail; - } - fmsb = (struct ubi_fm_sb *)(fm_raw); ai->max_sqnum = fmsb->sqnum; fm_pos += sizeof(struct ubi_fm_sb); @@ -741,28 +779,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, } } - /* This can happen if a PEB is already in an EBA known - * by this fastmap but the PEB itself is not in the used - * list. - * In this case the PEB can be within the fastmap pool - * or while writing the fastmap it was in the protection - * queue. - */ if (!aeb) { - aeb = kmem_cache_alloc(ai->aeb_slab_cache, - GFP_KERNEL); - if (!aeb) { - ret = -ENOMEM; - - goto fail; - } - - aeb->lnum = j; - aeb->pnum = be32_to_cpu(fm_eba->pnum[j]); - aeb->ec = -1; - aeb->scrub = aeb->copy_flag = aeb->sqnum = 0; - list_add_tail(&aeb->u.list, &eba_orphans); - continue; + ubi_err(ubi, "PEB %i is in EBA but not in used list", pnum); + goto fail_bad; } aeb->lnum = j; @@ -775,49 +794,13 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, dbg_bld("inserting PEB:%i (LEB %i) to vol %i", aeb->pnum, aeb->lnum, av->vol_id); } - - ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); - if (!ech) { - ret = -ENOMEM; - goto fail; - } - - list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, - u.list) { - int err; - - if (ubi_io_is_bad(ubi, tmp_aeb->pnum)) { - ubi_err(ubi, "bad PEB in fastmap EBA orphan list"); - ret = UBI_BAD_FASTMAP; - kfree(ech); - goto fail; - } - - err = ubi_io_read_ec_hdr(ubi, tmp_aeb->pnum, ech, 0); - if (err && err != UBI_IO_BITFLIPS) { - ubi_err(ubi, "unable to read EC header! PEB:%i err:%i", - tmp_aeb->pnum, err); - ret = err > 0 ? UBI_BAD_FASTMAP : err; - kfree(ech); - - goto fail; - } else if (err == UBI_IO_BITFLIPS) - tmp_aeb->scrub = 1; - - tmp_aeb->ec = be64_to_cpu(ech->ec); - assign_aeb_to_av(ai, tmp_aeb, av); - } - - kfree(ech); } - ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum, - &eba_orphans, &free); + ret = scan_pool(ubi, ai, fmpl1->pebs, pool_size, &max_sqnum, &free); if (ret) goto fail; - ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum, - &eba_orphans, &free); + ret = scan_pool(ubi, ai, fmpl2->pebs, wl_pool_size, &max_sqnum, &free); if (ret) goto fail; @@ -827,8 +810,9 @@ static int ubi_attach_fastmap(struct ubi_device *ubi, list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) list_move_tail(&tmp_aeb->u.list, &ai->free); - ubi_assert(list_empty(&used)); - ubi_assert(list_empty(&eba_orphans)); + list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &used, u.list) + list_move_tail(&tmp_aeb->u.list, &ai->erase); + ubi_assert(list_empty(&free)); /* @@ -850,10 +834,6 @@ fail: list_del(&tmp_aeb->u.list); kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); } - list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &eba_orphans, u.list) { - list_del(&tmp_aeb->u.list); - kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); - } list_for_each_entry_safe(tmp_aeb, _tmp_aeb, &free, u.list) { list_del(&tmp_aeb->u.list); kmem_cache_free(ai->aeb_slab_cache, tmp_aeb); @@ -884,7 +864,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, __be32 crc, tmp_crc; unsigned long long sqnum = 0; - mutex_lock(&ubi->fm_mutex); + down_write(&ubi->fm_protect); memset(ubi->fm_buf, 0, ubi->fm_size); fmsb = kmalloc(sizeof(*fmsb), GFP_KERNEL); @@ -1075,7 +1055,7 @@ int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, ubi_free_vid_hdr(ubi, vh); kfree(ech); out: - mutex_unlock(&ubi->fm_mutex); + up_write(&ubi->fm_protect); if (ret == UBI_BAD_FASTMAP) ubi_err(ubi, "Attach by fastmap failed, doing a full scan!"); return ret; @@ -1107,13 +1087,14 @@ static int ubi_write_fastmap(struct ubi_device *ubi, struct ubi_fm_ec *fec; struct ubi_fm_volhdr *fvh; struct ubi_fm_eba *feba; - struct rb_node *node; struct ubi_wl_entry *wl_e; struct ubi_volume *vol; struct ubi_vid_hdr *avhdr, *dvhdr; struct ubi_work *ubi_wrk; + struct rb_node *tmp_rb; int ret, i, j, free_peb_count, used_peb_count, vol_count; int scrub_peb_count, erase_peb_count; + int *seen_pebs = NULL; fm_raw = ubi->fm_buf; memset(ubi->fm_buf, 0, ubi->fm_size); @@ -1130,6 +1111,12 @@ static int ubi_write_fastmap(struct ubi_device *ubi, goto out_kfree; } + seen_pebs = init_seen(ubi); + if (IS_ERR(seen_pebs)) { + ret = PTR_ERR(seen_pebs); + goto out_kfree; + } + spin_lock(&ubi->volumes_lock); spin_lock(&ubi->wl_lock); @@ -1160,8 +1147,10 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fmpl1->size = cpu_to_be16(ubi->fm_pool.size); fmpl1->max_size = cpu_to_be16(ubi->fm_pool.max_size); - for (i = 0; i < ubi->fm_pool.size; i++) + for (i = 0; i < ubi->fm_pool.size; i++) { fmpl1->pebs[i] = cpu_to_be32(ubi->fm_pool.pebs[i]); + set_seen(ubi, ubi->fm_pool.pebs[i], seen_pebs); + } fmpl2 = (struct ubi_fm_scan_pool *)(fm_raw + fm_pos); fm_pos += sizeof(*fmpl2); @@ -1169,14 +1158,16 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fmpl2->size = cpu_to_be16(ubi->fm_wl_pool.size); fmpl2->max_size = cpu_to_be16(ubi->fm_wl_pool.max_size); - for (i = 0; i < ubi->fm_wl_pool.size; i++) + for (i = 0; i < ubi->fm_wl_pool.size; i++) { fmpl2->pebs[i] = cpu_to_be32(ubi->fm_wl_pool.pebs[i]); + set_seen(ubi, ubi->fm_wl_pool.pebs[i], seen_pebs); + } - for (node = rb_first(&ubi->free); node; node = rb_next(node)) { - wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + ubi_for_each_free_peb(ubi, wl_e, tmp_rb) { fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); fec->pnum = cpu_to_be32(wl_e->pnum); + set_seen(ubi, wl_e->pnum, seen_pebs); fec->ec = cpu_to_be32(wl_e->ec); free_peb_count++; @@ -1185,11 +1176,11 @@ static int ubi_write_fastmap(struct ubi_device *ubi, } fmh->free_peb_count = cpu_to_be32(free_peb_count); - for (node = rb_first(&ubi->used); node; node = rb_next(node)) { - wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + ubi_for_each_used_peb(ubi, wl_e, tmp_rb) { fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); fec->pnum = cpu_to_be32(wl_e->pnum); + set_seen(ubi, wl_e->pnum, seen_pebs); fec->ec = cpu_to_be32(wl_e->ec); used_peb_count++; @@ -1197,25 +1188,24 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ubi_assert(fm_pos <= ubi->fm_size); } - for (i = 0; i < UBI_PROT_QUEUE_LEN; i++) { - list_for_each_entry(wl_e, &ubi->pq[i], u.list) { - fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); + ubi_for_each_protected_peb(ubi, i, wl_e) { + fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); - fec->pnum = cpu_to_be32(wl_e->pnum); - fec->ec = cpu_to_be32(wl_e->ec); + fec->pnum = cpu_to_be32(wl_e->pnum); + set_seen(ubi, wl_e->pnum, seen_pebs); + fec->ec = cpu_to_be32(wl_e->ec); - used_peb_count++; - fm_pos += sizeof(*fec); - ubi_assert(fm_pos <= ubi->fm_size); - } + used_peb_count++; + fm_pos += sizeof(*fec); + ubi_assert(fm_pos <= ubi->fm_size); } fmh->used_peb_count = cpu_to_be32(used_peb_count); - for (node = rb_first(&ubi->scrub); node; node = rb_next(node)) { - wl_e = rb_entry(node, struct ubi_wl_entry, u.rb); + ubi_for_each_scrub_peb(ubi, wl_e, tmp_rb) { fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); fec->pnum = cpu_to_be32(wl_e->pnum); + set_seen(ubi, wl_e->pnum, seen_pebs); fec->ec = cpu_to_be32(wl_e->ec); scrub_peb_count++; @@ -1233,6 +1223,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, fec = (struct ubi_fm_ec *)(fm_raw + fm_pos); fec->pnum = cpu_to_be32(wl_e->pnum); + set_seen(ubi, wl_e->pnum, seen_pebs); fec->ec = cpu_to_be32(wl_e->ec); erase_peb_count++; @@ -1292,6 +1283,7 @@ static int ubi_write_fastmap(struct ubi_device *ubi, for (i = 0; i < new_fm->used_blocks; i++) { fmsb->block_loc[i] = cpu_to_be32(new_fm->e[i]->pnum); + set_seen(ubi, new_fm->e[i]->pnum, seen_pebs); fmsb->block_ec[i] = cpu_to_be32(new_fm->e[i]->ec); } @@ -1325,11 +1317,13 @@ static int ubi_write_fastmap(struct ubi_device *ubi, ubi_assert(new_fm); ubi->fm = new_fm; + ret = self_check_seen(ubi, seen_pebs); dbg_bld("fastmap written!"); out_kfree: ubi_free_vid_hdr(ubi, avhdr); ubi_free_vid_hdr(ubi, dvhdr); + free_seen(seen_pebs); out: return ret; } @@ -1384,31 +1378,87 @@ out: /** * invalidate_fastmap - destroys a fastmap. * @ubi: UBI device object - * @fm: the fastmap to be destroyed * + * This function ensures that upon next UBI attach a full scan + * is issued. We need this if UBI is about to write a new fastmap + * but is unable to do so. In this case we have two options: + * a) Make sure that the current fastmap will not be usued upon + * attach time and contine or b) fall back to RO mode to have the + * current fastmap in a valid state. * Returns 0 on success, < 0 indicates an internal error. */ -static int invalidate_fastmap(struct ubi_device *ubi, - struct ubi_fastmap_layout *fm) +static int invalidate_fastmap(struct ubi_device *ubi) { int ret; - struct ubi_vid_hdr *vh; + struct ubi_fastmap_layout *fm; + struct ubi_wl_entry *e; + struct ubi_vid_hdr *vh = NULL; - ret = erase_block(ubi, fm->e[0]->pnum); - if (ret < 0) - return ret; + if (!ubi->fm) + return 0; + + ubi->fm = NULL; + + ret = -ENOMEM; + fm = kzalloc(sizeof(*fm), GFP_KERNEL); + if (!fm) + goto out; vh = new_fm_vhdr(ubi, UBI_FM_SB_VOLUME_ID); if (!vh) - return -ENOMEM; + goto out_free_fm; - /* deleting the current fastmap SB is not enough, an old SB may exist, - * so create a (corrupted) SB such that fastmap will find it and fall - * back to scanning mode in any case */ + ret = -ENOSPC; + e = ubi_wl_get_fm_peb(ubi, 1); + if (!e) + goto out_free_fm; + + /* + * Create fake fastmap such that UBI will fall back + * to scanning mode. + */ vh->sqnum = cpu_to_be64(ubi_next_sqnum(ubi)); - ret = ubi_io_write_vid_hdr(ubi, fm->e[0]->pnum, vh); + ret = ubi_io_write_vid_hdr(ubi, e->pnum, vh); + if (ret < 0) { + ubi_wl_put_fm_peb(ubi, e, 0, 0); + goto out_free_fm; + } + + fm->used_blocks = 1; + fm->e[0] = e; + + ubi->fm = fm; +out: + ubi_free_vid_hdr(ubi, vh); return ret; + +out_free_fm: + kfree(fm); + goto out; +} + +/** + * return_fm_pebs - returns all PEBs used by a fastmap back to the + * WL sub-system. + * @ubi: UBI device object + * @fm: fastmap layout object + */ +static void return_fm_pebs(struct ubi_device *ubi, + struct ubi_fastmap_layout *fm) +{ + int i; + + if (!fm) + return; + + for (i = 0; i < fm->used_blocks; i++) { + if (fm->e[i]) { + ubi_wl_put_fm_peb(ubi, fm->e[i], i, + fm->to_be_tortured[i]); + fm->e[i] = NULL; + } + } } /** @@ -1420,45 +1470,32 @@ static int invalidate_fastmap(struct ubi_device *ubi, */ int ubi_update_fastmap(struct ubi_device *ubi) { - int ret, i; + int ret, i, j; struct ubi_fastmap_layout *new_fm, *old_fm; struct ubi_wl_entry *tmp_e; - mutex_lock(&ubi->fm_mutex); + down_write(&ubi->fm_protect); ubi_refill_pools(ubi); if (ubi->ro_mode || ubi->fm_disabled) { - mutex_unlock(&ubi->fm_mutex); + up_write(&ubi->fm_protect); return 0; } ret = ubi_ensure_anchor_pebs(ubi); if (ret) { - mutex_unlock(&ubi->fm_mutex); + up_write(&ubi->fm_protect); return ret; } new_fm = kzalloc(sizeof(*new_fm), GFP_KERNEL); if (!new_fm) { - mutex_unlock(&ubi->fm_mutex); + up_write(&ubi->fm_protect); return -ENOMEM; } new_fm->used_blocks = ubi->fm_size / ubi->leb_size; - - for (i = 0; i < new_fm->used_blocks; i++) { - new_fm->e[i] = kmem_cache_alloc(ubi_wl_entry_slab, GFP_KERNEL); - if (!new_fm->e[i]) { - while (i--) - kfree(new_fm->e[i]); - - kfree(new_fm); - mutex_unlock(&ubi->fm_mutex); - return -ENOMEM; - } - } - old_fm = ubi->fm; ubi->fm = NULL; @@ -1473,37 +1510,49 @@ int ubi_update_fastmap(struct ubi_device *ubi) tmp_e = ubi_wl_get_fm_peb(ubi, 0); spin_unlock(&ubi->wl_lock); - if (!tmp_e && !old_fm) { - int j; - ubi_err(ubi, "could not get any free erase block"); - - for (j = 1; j < i; j++) - ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0); - - ret = -ENOSPC; - goto err; - } else if (!tmp_e && old_fm) { - ret = erase_block(ubi, old_fm->e[i]->pnum); - if (ret < 0) { - int j; - - for (j = 1; j < i; j++) - ubi_wl_put_fm_peb(ubi, new_fm->e[j], - j, 0); + if (!tmp_e) { + if (old_fm && old_fm->e[i]) { + ret = erase_block(ubi, old_fm->e[i]->pnum); + if (ret < 0) { + ubi_err(ubi, "could not erase old fastmap PEB"); + + for (j = 1; j < i; j++) { + ubi_wl_put_fm_peb(ubi, new_fm->e[j], + j, 0); + new_fm->e[j] = NULL; + } + goto err; + } + new_fm->e[i] = old_fm->e[i]; + old_fm->e[i] = NULL; + } else { + ubi_err(ubi, "could not get any free erase block"); + + for (j = 1; j < i; j++) { + ubi_wl_put_fm_peb(ubi, new_fm->e[j], j, 0); + new_fm->e[j] = NULL; + } - ubi_err(ubi, "could not erase old fastmap PEB"); + ret = -ENOSPC; goto err; } - - new_fm->e[i]->pnum = old_fm->e[i]->pnum; - new_fm->e[i]->ec = old_fm->e[i]->ec; } else { - new_fm->e[i]->pnum = tmp_e->pnum; - new_fm->e[i]->ec = tmp_e->ec; + new_fm->e[i] = tmp_e; - if (old_fm) + if (old_fm && old_fm->e[i]) { ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, old_fm->to_be_tortured[i]); + old_fm->e[i] = NULL; + } + } + } + + /* Old fastmap is larger than the new one */ + if (old_fm && new_fm->used_blocks < old_fm->used_blocks) { + for (i = new_fm->used_blocks; i < old_fm->used_blocks; i++) { + ubi_wl_put_fm_peb(ubi, old_fm->e[i], i, + old_fm->to_be_tortured[i]); + old_fm->e[i] = NULL; } } @@ -1516,67 +1565,67 @@ int ubi_update_fastmap(struct ubi_device *ubi) if (!tmp_e) { ret = erase_block(ubi, old_fm->e[0]->pnum); if (ret < 0) { - int i; ubi_err(ubi, "could not erase old anchor PEB"); - for (i = 1; i < new_fm->used_blocks; i++) + for (i = 1; i < new_fm->used_blocks; i++) { ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); + new_fm->e[i] = NULL; + } goto err; } - - new_fm->e[0]->pnum = old_fm->e[0]->pnum; + new_fm->e[0] = old_fm->e[0]; new_fm->e[0]->ec = ret; + old_fm->e[0] = NULL; } else { /* we've got a new anchor PEB, return the old one */ ubi_wl_put_fm_peb(ubi, old_fm->e[0], 0, old_fm->to_be_tortured[0]); - - new_fm->e[0]->pnum = tmp_e->pnum; - new_fm->e[0]->ec = tmp_e->ec; + new_fm->e[0] = tmp_e; + old_fm->e[0] = NULL; } } else { if (!tmp_e) { - int i; ubi_err(ubi, "could not find any anchor PEB"); - for (i = 1; i < new_fm->used_blocks; i++) + for (i = 1; i < new_fm->used_blocks; i++) { ubi_wl_put_fm_peb(ubi, new_fm->e[i], i, 0); + new_fm->e[i] = NULL; + } ret = -ENOSPC; goto err; } - - new_fm->e[0]->pnum = tmp_e->pnum; - new_fm->e[0]->ec = tmp_e->ec; + new_fm->e[0] = tmp_e; } down_write(&ubi->work_sem); - down_write(&ubi->fm_sem); + down_write(&ubi->fm_eba_sem); ret = ubi_write_fastmap(ubi, new_fm); - up_write(&ubi->fm_sem); + up_write(&ubi->fm_eba_sem); up_write(&ubi->work_sem); if (ret) goto err; out_unlock: - mutex_unlock(&ubi->fm_mutex); + up_write(&ubi->fm_protect); kfree(old_fm); return ret; err: - kfree(new_fm); - ubi_warn(ubi, "Unable to write new fastmap, err=%i", ret); - ret = 0; - if (old_fm) { - ret = invalidate_fastmap(ubi, old_fm); - if (ret < 0) - ubi_err(ubi, "Unable to invalidiate current fastmap!"); - else if (ret) - ret = 0; + ret = invalidate_fastmap(ubi); + if (ret < 0) { + ubi_err(ubi, "Unable to invalidiate current fastmap!"); + ubi_ro_mode(ubi); + } else { + return_fm_pebs(ubi, old_fm); + return_fm_pebs(ubi, new_fm); + ret = 0; } + + kfree(new_fm); goto out_unlock; } diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index ed0bcb35472f..5bbd1f094f4e 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -859,6 +859,9 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, if (err) return err; + if (ubi_dbg_power_cut(ubi, POWER_CUT_EC_WRITE)) + return -EROFS; + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); return err; } @@ -1106,6 +1109,9 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, if (err) return err; + if (ubi_dbg_power_cut(ubi, POWER_CUT_VID_WRITE)) + return -EROFS; + p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, ubi->vid_hdr_alsize); diff --git a/drivers/mtd/ubi/ubi-media.h b/drivers/mtd/ubi/ubi-media.h index ac2b24d1783d..d0d072e7ccd2 100644 --- a/drivers/mtd/ubi/ubi-media.h +++ b/drivers/mtd/ubi/ubi-media.h @@ -403,8 +403,6 @@ struct ubi_vtbl_record { #define UBI_FM_MIN_POOL_SIZE 8 #define UBI_FM_MAX_POOL_SIZE 256 -#define UBI_FM_WL_POOL_SIZE 25 - /** * struct ubi_fm_sb - UBI fastmap super block * @magic: fastmap super block magic number (%UBI_FM_SB_MAGIC) diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index c5be82d9d345..c998212fc680 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -151,6 +151,17 @@ enum { UBI_BAD_FASTMAP, }; +/* + * Flags for emulate_power_cut in ubi_debug_info + * + * POWER_CUT_EC_WRITE: Emulate a power cut when writing an EC header + * POWER_CUT_VID_WRITE: Emulate a power cut when writing a VID header + */ +enum { + POWER_CUT_EC_WRITE = 0x01, + POWER_CUT_VID_WRITE = 0x02, +}; + /** * struct ubi_wl_entry - wear-leveling entry. * @u.rb: link in the corresponding (free/used) RB-tree @@ -356,30 +367,48 @@ struct ubi_wl_entry; * * @chk_gen: if UBI general extra checks are enabled * @chk_io: if UBI I/O extra checks are enabled + * @chk_fastmap: if UBI fastmap extra checks are enabled * @disable_bgt: disable the background task for testing purposes * @emulate_bitflips: emulate bit-flips for testing purposes * @emulate_io_failures: emulate write/erase failures for testing purposes + * @emulate_power_cut: emulate power cut for testing purposes + * @power_cut_counter: count down for writes left until emulated power cut + * @power_cut_min: minimum number of writes before emulating a power cut + * @power_cut_max: maximum number of writes until emulating a power cut * @dfs_dir_name: name of debugfs directory containing files of this UBI device * @dfs_dir: direntry object of the UBI device debugfs directory * @dfs_chk_gen: debugfs knob to enable UBI general extra checks * @dfs_chk_io: debugfs knob to enable UBI I/O extra checks + * @dfs_chk_fastmap: debugfs knob to enable UBI fastmap extra checks * @dfs_disable_bgt: debugfs knob to disable the background task * @dfs_emulate_bitflips: debugfs knob to emulate bit-flips * @dfs_emulate_io_failures: debugfs knob to emulate write/erase failures + * @dfs_emulate_power_cut: debugfs knob to emulate power cuts + * @dfs_power_cut_min: debugfs knob for minimum writes before power cut + * @dfs_power_cut_max: debugfs knob for maximum writes until power cut */ struct ubi_debug_info { unsigned int chk_gen:1; unsigned int chk_io:1; + unsigned int chk_fastmap:1; unsigned int disable_bgt:1; unsigned int emulate_bitflips:1; unsigned int emulate_io_failures:1; + unsigned int emulate_power_cut:2; + unsigned int power_cut_counter; + unsigned int power_cut_min; + unsigned int power_cut_max; char dfs_dir_name[UBI_DFS_DIR_LEN + 1]; struct dentry *dfs_dir; struct dentry *dfs_chk_gen; struct dentry *dfs_chk_io; + struct dentry *dfs_chk_fastmap; struct dentry *dfs_disable_bgt; struct dentry *dfs_emulate_bitflips; struct dentry *dfs_emulate_io_failures; + struct dentry *dfs_emulate_power_cut; + struct dentry *dfs_power_cut_min; + struct dentry *dfs_power_cut_max; }; /** @@ -426,11 +455,13 @@ struct ubi_debug_info { * @fm_pool: in-memory data structure of the fastmap pool * @fm_wl_pool: in-memory data structure of the fastmap pool used by the WL * sub-system - * @fm_mutex: serializes ubi_update_fastmap() and protects @fm_buf + * @fm_protect: serializes ubi_update_fastmap(), protects @fm_buf and makes sure + * that critical sections cannot be interrupted by ubi_update_fastmap() * @fm_buf: vmalloc()'d buffer which holds the raw fastmap * @fm_size: fastmap size in bytes - * @fm_sem: allows ubi_update_fastmap() to block EBA table changes + * @fm_eba_sem: allows ubi_update_fastmap() to block EBA table changes * @fm_work: fastmap work queue + * @fm_work_scheduled: non-zero if fastmap work was scheduled * * @used: RB-tree of used physical eraseblocks * @erroneous: RB-tree of erroneous used physical eraseblocks @@ -442,7 +473,8 @@ struct ubi_debug_info { * @pq_head: protection queue head * @wl_lock: protects the @used, @free, @pq, @pq_head, @lookuptbl, @move_from, * @move_to, @move_to_put @erase_pending, @wl_scheduled, @works, - * @erroneous, and @erroneous_peb_count fields + * @erroneous, @erroneous_peb_count, @fm_work_scheduled, @fm_pool, + * and @fm_wl_pool fields * @move_mutex: serializes eraseblock moves * @work_sem: used to wait for all the scheduled works to finish and prevent * new works from being submitted @@ -479,7 +511,7 @@ struct ubi_debug_info { * @vid_hdr_offset: starting offset of the volume identifier header (might be * unaligned) * @vid_hdr_aloffset: starting offset of the VID header aligned to - * @hdrs_min_io_size + * @hdrs_min_io_size * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or * not @@ -532,11 +564,12 @@ struct ubi_device { struct ubi_fastmap_layout *fm; struct ubi_fm_pool fm_pool; struct ubi_fm_pool fm_wl_pool; - struct rw_semaphore fm_sem; - struct mutex fm_mutex; + struct rw_semaphore fm_eba_sem; + struct rw_semaphore fm_protect; void *fm_buf; size_t fm_size; struct work_struct fm_work; + int fm_work_scheduled; /* Wear-leveling sub-system's stuff */ struct rb_root used; @@ -868,10 +901,14 @@ int ubi_compare_lebs(struct ubi_device *ubi, const struct ubi_ainf_peb *aeb, int pnum, const struct ubi_vid_hdr *vid_hdr); /* fastmap.c */ +#ifdef CONFIG_MTD_UBI_FASTMAP size_t ubi_calc_fm_size(struct ubi_device *ubi); int ubi_update_fastmap(struct ubi_device *ubi); int ubi_scan_fastmap(struct ubi_device *ubi, struct ubi_attach_info *ai, int fm_anchor); +#else +static inline int ubi_update_fastmap(struct ubi_device *ubi) { return 0; } +#endif /* block.c */ #ifdef CONFIG_MTD_UBI_BLOCK @@ -892,6 +929,42 @@ static inline int ubiblock_remove(struct ubi_volume_info *vi) } #endif +/* + * ubi_for_each_free_peb - walk the UBI free RB tree. + * @ubi: UBI device description object + * @e: a pointer to a ubi_wl_entry to use as cursor + * @pos: a pointer to RB-tree entry type to use as a loop counter + */ +#define ubi_for_each_free_peb(ubi, e, tmp_rb) \ + ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->free, u.rb) + +/* + * ubi_for_each_used_peb - walk the UBI used RB tree. + * @ubi: UBI device description object + * @e: a pointer to a ubi_wl_entry to use as cursor + * @pos: a pointer to RB-tree entry type to use as a loop counter + */ +#define ubi_for_each_used_peb(ubi, e, tmp_rb) \ + ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->used, u.rb) + +/* + * ubi_for_each_scub_peb - walk the UBI scub RB tree. + * @ubi: UBI device description object + * @e: a pointer to a ubi_wl_entry to use as cursor + * @pos: a pointer to RB-tree entry type to use as a loop counter + */ +#define ubi_for_each_scrub_peb(ubi, e, tmp_rb) \ + ubi_rb_for_each_entry((tmp_rb), (e), &(ubi)->scrub, u.rb) + +/* + * ubi_for_each_protected_peb - walk the UBI protection queue. + * @ubi: UBI device description object + * @i: a integer used as counter + * @e: a pointer to a ubi_wl_entry to use as cursor + */ +#define ubi_for_each_protected_peb(ubi, i, e) \ + for ((i) = 0; (i) < UBI_PROT_QUEUE_LEN; (i)++) \ + list_for_each_entry((e), &(ubi->pq[(i)]), u.list) /* * ubi_rb_for_each_entry - walk an RB-tree. diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 8f7bde6a85d6..16214d3d57a4 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -103,6 +103,7 @@ #include <linux/freezer.h> #include <linux/kthread.h> #include "ubi.h" +#include "wl.h" /* Number of physical eraseblocks reserved for wear-leveling purposes */ #define WL_RESERVED_PEBS 1 @@ -140,42 +141,6 @@ static int self_check_in_wl_tree(const struct ubi_device *ubi, static int self_check_in_pq(const struct ubi_device *ubi, struct ubi_wl_entry *e); -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * update_fastmap_work_fn - calls ubi_update_fastmap from a work queue - * @wrk: the work description object - */ -static void update_fastmap_work_fn(struct work_struct *wrk) -{ - struct ubi_device *ubi = container_of(wrk, struct ubi_device, fm_work); - ubi_update_fastmap(ubi); -} - -/** - * ubi_ubi_is_fm_block - returns 1 if a PEB is currently used in a fastmap. - * @ubi: UBI device description object - * @pnum: the to be checked PEB - */ -static int ubi_is_fm_block(struct ubi_device *ubi, int pnum) -{ - int i; - - if (!ubi->fm) - return 0; - - for (i = 0; i < ubi->fm->used_blocks; i++) - if (ubi->fm->e[i]->pnum == pnum) - return 1; - - return 0; -} -#else -static int ubi_is_fm_block(struct ubi_device *ubi, int pnum) -{ - return 0; -} -#endif - /** * wl_tree_add - add a wear-leveling entry to a WL RB-tree. * @e: the wear-leveling entry to add @@ -213,6 +178,20 @@ static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) } /** + * wl_tree_destroy - destroy a wear-leveling entry. + * @ubi: UBI device description object + * @e: the wear-leveling entry to add + * + * This function destroys a wear leveling entry and removes + * the reference from the lookup table. + */ +static void wl_entry_destroy(struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + ubi->lookuptbl[e->pnum] = NULL; + kmem_cache_free(ubi_wl_entry_slab, e); +} + +/** * do_work - do one pending work. * @ubi: UBI device description object * @@ -260,33 +239,6 @@ static int do_work(struct ubi_device *ubi) } /** - * produce_free_peb - produce a free physical eraseblock. - * @ubi: UBI device description object - * - * This function tries to make a free PEB by means of synchronous execution of - * pending works. This may be needed if, for example the background thread is - * disabled. Returns zero in case of success and a negative error code in case - * of failure. - */ -static int produce_free_peb(struct ubi_device *ubi) -{ - int err; - - while (!ubi->free.rb_node && ubi->works_count) { - spin_unlock(&ubi->wl_lock); - - dbg_wl("do one work synchronously"); - err = do_work(ubi); - - spin_lock(&ubi->wl_lock); - if (err) - return err; - } - - return 0; -} - -/** * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree. * @e: the wear-leveling entry to check * @root: the root of the tree @@ -409,119 +361,32 @@ static struct ubi_wl_entry *find_mean_wl_entry(struct ubi_device *ubi, if (last->ec - first->ec < WL_FREE_MAX_DIFF) { e = rb_entry(root->rb_node, struct ubi_wl_entry, u.rb); -#ifdef CONFIG_MTD_UBI_FASTMAP /* If no fastmap has been written and this WL entry can be used * as anchor PEB, hold it back and return the second best * WL entry such that fastmap can use the anchor PEB later. */ - if (e && !ubi->fm_disabled && !ubi->fm && - e->pnum < UBI_FM_MAX_START) - e = rb_entry(rb_next(root->rb_node), - struct ubi_wl_entry, u.rb); -#endif + e = may_reserve_for_fm(ubi, e, root); } else e = find_wl_entry(ubi, root, WL_FREE_MAX_DIFF/2); return e; } -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * find_anchor_wl_entry - find wear-leveling entry to used as anchor PEB. - * @root: the RB-tree where to look for - */ -static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root) -{ - struct rb_node *p; - struct ubi_wl_entry *e, *victim = NULL; - int max_ec = UBI_MAX_ERASECOUNTER; - - ubi_rb_for_each_entry(p, e, root, u.rb) { - if (e->pnum < UBI_FM_MAX_START && e->ec < max_ec) { - victim = e; - max_ec = e->ec; - } - } - - return victim; -} - -static int anchor_pebs_avalible(struct rb_root *root) -{ - struct rb_node *p; - struct ubi_wl_entry *e; - - ubi_rb_for_each_entry(p, e, root, u.rb) - if (e->pnum < UBI_FM_MAX_START) - return 1; - - return 0; -} - /** - * ubi_wl_get_fm_peb - find a physical erase block with a given maximal number. + * wl_get_wle - get a mean wl entry to be used by ubi_wl_get_peb() or + * refill_wl_user_pool(). * @ubi: UBI device description object - * @anchor: This PEB will be used as anchor PEB by fastmap * - * The function returns a physical erase block with a given maximal number - * and removes it from the wl subsystem. - * Must be called with wl_lock held! + * This function returns a a wear leveling entry in case of success and + * NULL in case of failure. */ -struct ubi_wl_entry *ubi_wl_get_fm_peb(struct ubi_device *ubi, int anchor) +static struct ubi_wl_entry *wl_get_wle(struct ubi_device *ubi) { - struct ubi_wl_entry *e = NULL; - - if (!ubi->free.rb_node || (ubi->free_count - ubi->beb_rsvd_pebs < 1)) - goto out; - - if (anchor) - e = find_anchor_wl_entry(&ubi->free); - else - e = find_mean_wl_entry(ubi, &ubi->free); - - if (!e) - goto out; - - self_check_in_wl_tree(ubi, e, &ubi->free); - - /* remove it from the free list, - * the wl subsystem does no longer know this erase block */ - rb_erase(&e->u.rb, &ubi->free); - ubi->free_count--; -out: - return e; -} -#endif - -/** - * __wl_get_peb - get a physical eraseblock. - * @ubi: UBI device description object - * - * This function returns a physical eraseblock in case of success and a - * negative error code in case of failure. - */ -static int __wl_get_peb(struct ubi_device *ubi) -{ - int err; struct ubi_wl_entry *e; -retry: - if (!ubi->free.rb_node) { - if (ubi->works_count == 0) { - ubi_err(ubi, "no free eraseblocks"); - ubi_assert(list_empty(&ubi->works)); - return -ENOSPC; - } - - err = produce_free_peb(ubi); - if (err < 0) - return err; - goto retry; - } - e = find_mean_wl_entry(ubi, &ubi->free); if (!e) { ubi_err(ubi, "no free eraseblocks"); - return -ENOSPC; + return NULL; } self_check_in_wl_tree(ubi, e, &ubi->free); @@ -533,174 +398,10 @@ retry: rb_erase(&e->u.rb, &ubi->free); ubi->free_count--; dbg_wl("PEB %d EC %d", e->pnum, e->ec); -#ifndef CONFIG_MTD_UBI_FASTMAP - /* We have to enqueue e only if fastmap is disabled, - * is fastmap enabled prot_queue_add() will be called by - * ubi_wl_get_peb() after removing e from the pool. */ - prot_queue_add(ubi, e); -#endif - return e->pnum; -} - -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * return_unused_pool_pebs - returns unused PEB to the free tree. - * @ubi: UBI device description object - * @pool: fastmap pool description object - */ -static void return_unused_pool_pebs(struct ubi_device *ubi, - struct ubi_fm_pool *pool) -{ - int i; - struct ubi_wl_entry *e; - - for (i = pool->used; i < pool->size; i++) { - e = ubi->lookuptbl[pool->pebs[i]]; - wl_tree_add(e, &ubi->free); - ubi->free_count++; - } -} - -/** - * refill_wl_pool - refills all the fastmap pool used by the - * WL sub-system. - * @ubi: UBI device description object - */ -static void refill_wl_pool(struct ubi_device *ubi) -{ - struct ubi_wl_entry *e; - struct ubi_fm_pool *pool = &ubi->fm_wl_pool; - - return_unused_pool_pebs(ubi, pool); - - for (pool->size = 0; pool->size < pool->max_size; pool->size++) { - if (!ubi->free.rb_node || - (ubi->free_count - ubi->beb_rsvd_pebs < 5)) - break; - - e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); - self_check_in_wl_tree(ubi, e, &ubi->free); - rb_erase(&e->u.rb, &ubi->free); - ubi->free_count--; - - pool->pebs[pool->size] = e->pnum; - } - pool->used = 0; -} - -/** - * refill_wl_user_pool - refills all the fastmap pool used by ubi_wl_get_peb. - * @ubi: UBI device description object - */ -static void refill_wl_user_pool(struct ubi_device *ubi) -{ - struct ubi_fm_pool *pool = &ubi->fm_pool; - - return_unused_pool_pebs(ubi, pool); - - for (pool->size = 0; pool->size < pool->max_size; pool->size++) { - pool->pebs[pool->size] = __wl_get_peb(ubi); - if (pool->pebs[pool->size] < 0) - break; - } - pool->used = 0; -} - -/** - * ubi_refill_pools - refills all fastmap PEB pools. - * @ubi: UBI device description object - */ -void ubi_refill_pools(struct ubi_device *ubi) -{ - spin_lock(&ubi->wl_lock); - refill_wl_pool(ubi); - refill_wl_user_pool(ubi); - spin_unlock(&ubi->wl_lock); -} - -/* ubi_wl_get_peb - works exaclty like __wl_get_peb but keeps track of - * the fastmap pool. - */ -int ubi_wl_get_peb(struct ubi_device *ubi) -{ - int ret; - struct ubi_fm_pool *pool = &ubi->fm_pool; - struct ubi_fm_pool *wl_pool = &ubi->fm_wl_pool; - - if (!pool->size || !wl_pool->size || pool->used == pool->size || - wl_pool->used == wl_pool->size) - ubi_update_fastmap(ubi); - - /* we got not a single free PEB */ - if (!pool->size) - ret = -ENOSPC; - else { - spin_lock(&ubi->wl_lock); - ret = pool->pebs[pool->used++]; - prot_queue_add(ubi, ubi->lookuptbl[ret]); - spin_unlock(&ubi->wl_lock); - } - - return ret; -} - -/* get_peb_for_wl - returns a PEB to be used internally by the WL sub-system. - * - * @ubi: UBI device description object - */ -static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) -{ - struct ubi_fm_pool *pool = &ubi->fm_wl_pool; - int pnum; - - if (pool->used == pool->size || !pool->size) { - /* We cannot update the fastmap here because this - * function is called in atomic context. - * Let's fail here and refill/update it as soon as possible. */ - schedule_work(&ubi->fm_work); - return NULL; - } else { - pnum = pool->pebs[pool->used++]; - return ubi->lookuptbl[pnum]; - } -} -#else -static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) -{ - struct ubi_wl_entry *e; - - e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); - self_check_in_wl_tree(ubi, e, &ubi->free); - ubi->free_count--; - ubi_assert(ubi->free_count >= 0); - rb_erase(&e->u.rb, &ubi->free); return e; } -int ubi_wl_get_peb(struct ubi_device *ubi) -{ - int peb, err; - - spin_lock(&ubi->wl_lock); - peb = __wl_get_peb(ubi); - spin_unlock(&ubi->wl_lock); - - if (peb < 0) - return peb; - - err = ubi_self_check_all_ff(ubi, peb, ubi->vid_hdr_aloffset, - ubi->peb_size - ubi->vid_hdr_aloffset); - if (err) { - ubi_err(ubi, "new PEB %d does not contain all 0xFF bytes", - peb); - return err; - } - - return peb; -} -#endif - /** * prot_queue_del - remove a physical eraseblock from the protection queue. * @ubi: UBI device description object @@ -867,17 +568,6 @@ static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, int shutdown); -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * ubi_is_erase_work - checks whether a work is erase work. - * @wrk: The work object to be checked - */ -int ubi_is_erase_work(struct ubi_work *wrk) -{ - return wrk->func == erase_worker; -} -#endif - /** * schedule_erase - schedule an erase work. * @ubi: UBI device description object @@ -895,7 +585,6 @@ static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, struct ubi_work *wl_wrk; ubi_assert(e); - ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", e->pnum, e->ec, torture); @@ -942,51 +631,6 @@ static int do_sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, return erase_worker(ubi, wl_wrk, 0); } -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * ubi_wl_put_fm_peb - returns a PEB used in a fastmap to the wear-leveling - * sub-system. - * see: ubi_wl_put_peb() - * - * @ubi: UBI device description object - * @fm_e: physical eraseblock to return - * @lnum: the last used logical eraseblock number for the PEB - * @torture: if this physical eraseblock has to be tortured - */ -int ubi_wl_put_fm_peb(struct ubi_device *ubi, struct ubi_wl_entry *fm_e, - int lnum, int torture) -{ - struct ubi_wl_entry *e; - int vol_id, pnum = fm_e->pnum; - - dbg_wl("PEB %d", pnum); - - ubi_assert(pnum >= 0); - ubi_assert(pnum < ubi->peb_count); - - spin_lock(&ubi->wl_lock); - e = ubi->lookuptbl[pnum]; - - /* This can happen if we recovered from a fastmap the very - * first time and writing now a new one. In this case the wl system - * has never seen any PEB used by the original fastmap. - */ - if (!e) { - e = fm_e; - ubi_assert(e->ec >= 0); - ubi->lookuptbl[pnum] = e; - } else { - e->ec = fm_e->ec; - kfree(fm_e); - } - - spin_unlock(&ubi->wl_lock); - - vol_id = lnum ? UBI_FM_DATA_VOLUME_ID : UBI_FM_SB_VOLUME_ID; - return schedule_erase(ubi, e, vol_id, lnum, torture); -} -#endif - /** * wear_leveling_worker - wear-leveling worker function. * @ubi: UBI device description object @@ -1002,7 +646,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, int shutdown) { int err, scrubbing = 0, torture = 0, protect = 0, erroneous = 0; - int vol_id = -1, uninitialized_var(lnum); + int vol_id = -1, lnum = -1; #ifdef CONFIG_MTD_UBI_FASTMAP int anchor = wrk->anchor; #endif @@ -1214,7 +858,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, err = do_sync_erase(ubi, e1, vol_id, lnum, 0); if (err) { if (e2) - kmem_cache_free(ubi_wl_entry_slab, e2); + wl_entry_destroy(ubi, e2); goto out_ro; } @@ -1282,8 +926,8 @@ out_error: spin_unlock(&ubi->wl_lock); ubi_free_vid_hdr(ubi, vid_hdr); - kmem_cache_free(ubi_wl_entry_slab, e1); - kmem_cache_free(ubi_wl_entry_slab, e2); + wl_entry_destroy(ubi, e1); + wl_entry_destroy(ubi, e2); out_ro: ubi_ro_mode(ubi); @@ -1369,38 +1013,6 @@ out_unlock: return err; } -#ifdef CONFIG_MTD_UBI_FASTMAP -/** - * ubi_ensure_anchor_pebs - schedule wear-leveling to produce an anchor PEB. - * @ubi: UBI device description object - */ -int ubi_ensure_anchor_pebs(struct ubi_device *ubi) -{ - struct ubi_work *wrk; - - spin_lock(&ubi->wl_lock); - if (ubi->wl_scheduled) { - spin_unlock(&ubi->wl_lock); - return 0; - } - ubi->wl_scheduled = 1; - spin_unlock(&ubi->wl_lock); - - wrk = kmalloc(sizeof(struct ubi_work), GFP_NOFS); - if (!wrk) { - spin_lock(&ubi->wl_lock); - ubi->wl_scheduled = 0; - spin_unlock(&ubi->wl_lock); - return -ENOMEM; - } - - wrk->anchor = 1; - wrk->func = &wear_leveling_worker; - schedule_ubi_work(ubi, wrk); - return 0; -} -#endif - /** * erase_worker - physical eraseblock erase worker function. * @ubi: UBI device description object @@ -1425,15 +1037,13 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, if (shutdown) { dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); kfree(wl_wrk); - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); return 0; } dbg_wl("erase PEB %d EC %d LEB %d:%d", pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum); - ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); - err = sync_erase(ubi, e, wl_wrk->torture); if (!err) { /* Fine, we've erased it successfully */ @@ -1471,7 +1081,7 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, return err; } - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); if (err != -EIO) /* * If this is not %-EIO, we have no idea what to do. Scheduling @@ -1563,6 +1173,8 @@ int ubi_wl_put_peb(struct ubi_device *ubi, int vol_id, int lnum, ubi_assert(pnum >= 0); ubi_assert(pnum < ubi->peb_count); + down_read(&ubi->fm_protect); + retry: spin_lock(&ubi->wl_lock); e = ubi->lookuptbl[pnum]; @@ -1593,6 +1205,7 @@ retry: ubi_assert(!ubi->move_to_put); ubi->move_to_put = 1; spin_unlock(&ubi->wl_lock); + up_read(&ubi->fm_protect); return 0; } else { if (in_wl_tree(e, &ubi->used)) { @@ -1614,6 +1227,7 @@ retry: ubi_err(ubi, "PEB %d not found", pnum); ubi_ro_mode(ubi); spin_unlock(&ubi->wl_lock); + up_read(&ubi->fm_protect); return err; } } @@ -1627,6 +1241,7 @@ retry: spin_unlock(&ubi->wl_lock); } + up_read(&ubi->fm_protect); return err; } @@ -1758,9 +1373,10 @@ int ubi_wl_flush(struct ubi_device *ubi, int vol_id, int lnum) /** * tree_destroy - destroy an RB-tree. + * @ubi: UBI device description object * @root: the root of the tree to destroy */ -static void tree_destroy(struct rb_root *root) +static void tree_destroy(struct ubi_device *ubi, struct rb_root *root) { struct rb_node *rb; struct ubi_wl_entry *e; @@ -1782,7 +1398,7 @@ static void tree_destroy(struct rb_root *root) rb->rb_right = NULL; } - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); } } } @@ -1850,6 +1466,9 @@ int ubi_thread(void *u) */ static void shutdown_work(struct ubi_device *ubi) { +#ifdef CONFIG_MTD_UBI_FASTMAP + flush_work(&ubi->fm_work); +#endif while (!list_empty(&ubi->works)) { struct ubi_work *wrk; @@ -1883,9 +1502,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) init_rwsem(&ubi->work_sem); ubi->max_ec = ai->max_ec; INIT_LIST_HEAD(&ubi->works); -#ifdef CONFIG_MTD_UBI_FASTMAP - INIT_WORK(&ubi->fm_work, update_fastmap_work_fn); -#endif sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); @@ -1907,10 +1523,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; - ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); ubi->lookuptbl[e->pnum] = e; if (schedule_erase(ubi, e, aeb->vol_id, aeb->lnum, 0)) { - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); goto out_free; } @@ -1928,7 +1543,6 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) e->pnum = aeb->pnum; e->ec = aeb->ec; ubi_assert(e->ec >= 0); - ubi_assert(!ubi_is_fm_block(ubi, e->pnum)); wl_tree_add(e, &ubi->free); ubi->free_count++; @@ -1966,17 +1580,20 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) dbg_wl("found %i PEBs", found_pebs); - if (ubi->fm) + if (ubi->fm) { ubi_assert(ubi->good_peb_count == \ found_pebs + ubi->fm->used_blocks); + + for (i = 0; i < ubi->fm->used_blocks; i++) { + e = ubi->fm->e[i]; + ubi->lookuptbl[e->pnum] = e; + } + } else ubi_assert(ubi->good_peb_count == found_pebs); reserved_pebs = WL_RESERVED_PEBS; -#ifdef CONFIG_MTD_UBI_FASTMAP - /* Reserve enough LEBs to store two fastmaps. */ - reserved_pebs += (ubi->fm_size / ubi->leb_size) * 2; -#endif + ubi_fastmap_init(ubi, &reserved_pebs); if (ubi->avail_pebs < reserved_pebs) { ubi_err(ubi, "no enough physical eraseblocks (%d, need %d)", @@ -1998,9 +1615,9 @@ int ubi_wl_init(struct ubi_device *ubi, struct ubi_attach_info *ai) out_free: shutdown_work(ubi); - tree_destroy(&ubi->used); - tree_destroy(&ubi->free); - tree_destroy(&ubi->scrub); + tree_destroy(ubi, &ubi->used); + tree_destroy(ubi, &ubi->free); + tree_destroy(ubi, &ubi->scrub); kfree(ubi->lookuptbl); return err; } @@ -2017,7 +1634,7 @@ static void protection_queue_destroy(struct ubi_device *ubi) for (i = 0; i < UBI_PROT_QUEUE_LEN; ++i) { list_for_each_entry_safe(e, tmp, &ubi->pq[i], u.list) { list_del(&e->u.list); - kmem_cache_free(ubi_wl_entry_slab, e); + wl_entry_destroy(ubi, e); } } } @@ -2029,12 +1646,13 @@ static void protection_queue_destroy(struct ubi_device *ubi) void ubi_wl_close(struct ubi_device *ubi) { dbg_wl("close the WL sub-system"); + ubi_fastmap_close(ubi); shutdown_work(ubi); protection_queue_destroy(ubi); - tree_destroy(&ubi->used); - tree_destroy(&ubi->erroneous); - tree_destroy(&ubi->free); - tree_destroy(&ubi->scrub); + tree_destroy(ubi, &ubi->used); + tree_destroy(ubi, &ubi->erroneous); + tree_destroy(ubi, &ubi->free); + tree_destroy(ubi, &ubi->scrub); kfree(ubi->lookuptbl); } @@ -2133,3 +1751,94 @@ static int self_check_in_pq(const struct ubi_device *ubi, dump_stack(); return -EINVAL; } +#ifndef CONFIG_MTD_UBI_FASTMAP +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi) +{ + struct ubi_wl_entry *e; + + e = find_wl_entry(ubi, &ubi->free, WL_FREE_MAX_DIFF); + self_check_in_wl_tree(ubi, e, &ubi->free); + ubi->free_count--; + ubi_assert(ubi->free_count >= 0); + rb_erase(&e->u.rb, &ubi->free); + + return e; +} + +/** + * produce_free_peb - produce a free physical eraseblock. + * @ubi: UBI device description object + * + * This function tries to make a free PEB by means of synchronous execution of + * pending works. This may be needed if, for example the background thread is + * disabled. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int produce_free_peb(struct ubi_device *ubi) +{ + int err; + + while (!ubi->free.rb_node && ubi->works_count) { + spin_unlock(&ubi->wl_lock); + + dbg_wl("do one work synchronously"); + err = do_work(ubi); + + spin_lock(&ubi->wl_lock); + if (err) + return err; + } + + return 0; +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. + * Returns with ubi->fm_eba_sem held in read mode! + */ +int ubi_wl_get_peb(struct ubi_device *ubi) +{ + int err; + struct ubi_wl_entry *e; + +retry: + down_read(&ubi->fm_eba_sem); + spin_lock(&ubi->wl_lock); + if (!ubi->free.rb_node) { + if (ubi->works_count == 0) { + ubi_err(ubi, "no free eraseblocks"); + ubi_assert(list_empty(&ubi->works)); + spin_unlock(&ubi->wl_lock); + return -ENOSPC; + } + + err = produce_free_peb(ubi); + if (err < 0) { + spin_unlock(&ubi->wl_lock); + return err; + } + spin_unlock(&ubi->wl_lock); + up_read(&ubi->fm_eba_sem); + goto retry; + + } + e = wl_get_wle(ubi); + prot_queue_add(ubi, e); + spin_unlock(&ubi->wl_lock); + + err = ubi_self_check_all_ff(ubi, e->pnum, ubi->vid_hdr_aloffset, + ubi->peb_size - ubi->vid_hdr_aloffset); + if (err) { + ubi_err(ubi, "new PEB %d does not contain all 0xFF bytes", e->pnum); + return err; + } + + return e->pnum; +} +#else +#include "fastmap-wl.c" +#endif diff --git a/drivers/mtd/ubi/wl.h b/drivers/mtd/ubi/wl.h new file mode 100644 index 000000000000..bd1f07e5ce9a --- /dev/null +++ b/drivers/mtd/ubi/wl.h @@ -0,0 +1,28 @@ +#ifndef UBI_WL_H +#define UBI_WL_H +#ifdef CONFIG_MTD_UBI_FASTMAP +static int anchor_pebs_avalible(struct rb_root *root); +static void update_fastmap_work_fn(struct work_struct *wrk); +static struct ubi_wl_entry *find_anchor_wl_entry(struct rb_root *root); +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi); +static void ubi_fastmap_close(struct ubi_device *ubi); +static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count) +{ + /* Reserve enough LEBs to store two fastmaps. */ + *count += (ubi->fm_size / ubi->leb_size) * 2; + INIT_WORK(&ubi->fm_work, update_fastmap_work_fn); +} +static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi, + struct ubi_wl_entry *e, + struct rb_root *root); +#else /* !CONFIG_MTD_UBI_FASTMAP */ +static struct ubi_wl_entry *get_peb_for_wl(struct ubi_device *ubi); +static inline void ubi_fastmap_close(struct ubi_device *ubi) { } +static inline void ubi_fastmap_init(struct ubi_device *ubi, int *count) { } +static struct ubi_wl_entry *may_reserve_for_fm(struct ubi_device *ubi, + struct ubi_wl_entry *e, + struct rb_root *root) { + return e; +} +#endif /* CONFIG_MTD_UBI_FASTMAP */ +#endif /* UBI_WL_H */ diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c index 8362aef0c15e..9c91ff872485 100644 --- a/drivers/net/macvtap.c +++ b/drivers/net/macvtap.c @@ -1118,8 +1118,6 @@ static const struct file_operations macvtap_fops = { .owner = THIS_MODULE, .open = macvtap_open, .release = macvtap_release, - .read = new_sync_read, - .write = new_sync_write, .read_iter = macvtap_read_iter, .write_iter = macvtap_write_iter, .poll = macvtap_poll, diff --git a/drivers/net/tun.c b/drivers/net/tun.c index b96b94cee760..e470ae59d405 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2223,8 +2223,6 @@ static void tun_chr_show_fdinfo(struct seq_file *m, struct file *f) static const struct file_operations tun_fops = { .owner = THIS_MODULE, .llseek = no_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = tun_chr_read_iter, .write_iter = tun_chr_write_iter, .poll = tun_chr_poll, diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index d140b733940c..c5c037ccf32c 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -310,7 +310,7 @@ static ssize_t ashmem_read(struct file *file, char __user *buf, * be destroyed until all references to the file are dropped and * ashmem_release is called. */ - ret = asma->file->f_op->read(asma->file, buf, len, pos); + ret = __vfs_read(asma->file, buf, len, pos); if (ret >= 0) { /** Update backing file pos, since f_ops->read() doesn't */ asma->file->f_pos = *pos; diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c index ad35ed6e93f0..304ebff119ee 100644 --- a/drivers/staging/comedi/drivers/serial2002.c +++ b/drivers/staging/comedi/drivers/serial2002.c @@ -108,24 +108,16 @@ static int serial2002_tty_write(struct file *f, unsigned char *buf, int count) { const char __user *p = (__force const char __user *)buf; int result; + loff_t offset = 0; mm_segment_t oldfs; oldfs = get_fs(); set_fs(KERNEL_DS); - f->f_pos = 0; - result = f->f_op->write(f, p, count, &f->f_pos); + result = __vfs_write(f, p, count, &offset); set_fs(oldfs); return result; } -static int serial2002_tty_readb(struct file *f, unsigned char *buf) -{ - char __user *p = (__force char __user *)buf; - - f->f_pos = 0; - return f->f_op->read(f, p, 1, &f->f_pos); -} - static void serial2002_tty_read_poll_wait(struct file *f, int timeout) { struct poll_wqueues table; @@ -161,13 +153,15 @@ static int serial2002_tty_read(struct file *f, int timeout) result = -1; if (!IS_ERR(f)) { mm_segment_t oldfs; + char __user *p = (__force char __user *)&ch; + loff_t offset = 0; oldfs = get_fs(); set_fs(KERNEL_DS); if (f->f_op->poll) { serial2002_tty_read_poll_wait(f, timeout); - if (serial2002_tty_readb(f, &ch) == 1) + if (__vfs_read(f, p, 1, &offset) == 1) result = ch; } else { /* Device does not support poll, busy wait */ @@ -178,7 +172,7 @@ static int serial2002_tty_read(struct file *f, int timeout) if (retries >= timeout) break; - if (serial2002_tty_readb(f, &ch) == 1) { + if (__vfs_read(f, p, 1, &offset) == 1) { result = ch; break; } diff --git a/drivers/staging/lustre/lustre/llite/file.c b/drivers/staging/lustre/lustre/llite/file.c index 85e74d18d1c7..529062ea112b 100644 --- a/drivers/staging/lustre/lustre/llite/file.c +++ b/drivers/staging/lustre/lustre/llite/file.c @@ -3135,9 +3135,7 @@ int ll_inode_permission(struct inode *inode, int mask) /* -o localflock - only provides locally consistent flock locks */ struct file_operations ll_file_operations = { - .read = new_sync_read, .read_iter = ll_file_read_iter, - .write = new_sync_write, .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, @@ -3150,9 +3148,7 @@ struct file_operations ll_file_operations = { }; struct file_operations ll_file_operations_flock = { - .read = new_sync_read, .read_iter = ll_file_read_iter, - .write = new_sync_write, .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, @@ -3168,9 +3164,7 @@ struct file_operations ll_file_operations_flock = { /* These are for -o noflock - to return ENOSYS on flock calls */ struct file_operations ll_file_operations_noflock = { - .read = new_sync_read, .read_iter = ll_file_read_iter, - .write = new_sync_write, .write_iter = ll_file_write_iter, .unlocked_ioctl = ll_file_ioctl, .open = ll_file_open, diff --git a/drivers/staging/lustre/lustre/llite/llite_internal.h b/drivers/staging/lustre/lustre/llite/llite_internal.h index 2af1d7286250..e7422f5c9c6f 100644 --- a/drivers/staging/lustre/lustre/llite/llite_internal.h +++ b/drivers/staging/lustre/lustre/llite/llite_internal.h @@ -938,10 +938,8 @@ struct ll_cl_context { }; struct vvp_thread_info { - struct iovec vti_local_iov; struct vvp_io_args vti_args; struct ra_io_arg vti_ria; - struct kiocb vti_kiocb; struct ll_cl_context vti_io_ctx; }; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a12315a78248..6bdb57069044 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -1061,8 +1061,6 @@ static const struct file_operations ffs_epfile_operations = { .llseek = no_llseek, .open = ffs_epfile_open, - .write = new_sync_write, - .read = new_sync_read, .write_iter = ffs_epfile_write_iter, .read_iter = ffs_epfile_read_iter, .release = ffs_epfile_release, diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 662ef2c1c62b..6af58c6dba5e 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -699,8 +699,6 @@ static const struct file_operations ep_io_operations = { .open = ep_open, .release = ep_release, .llseek = no_llseek, - .read = new_sync_read, - .write = new_sync_write, .unlocked_ioctl = ep_ioctl, .read_iter = ep_read_iter, .write_iter = ep_write_iter, diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h index b83ebfbf3fdc..5a0db6dec8d1 100644 --- a/fs/9p/v9fs_vfs.h +++ b/fs/9p/v9fs_vfs.h @@ -68,14 +68,10 @@ int v9fs_file_open(struct inode *inode, struct file *file); void v9fs_inode2stat(struct inode *inode, struct p9_wstat *stat); int v9fs_uflags2omode(int uflags, int extended); -ssize_t v9fs_file_readn(struct file *, char *, char __user *, u32, u64); -ssize_t v9fs_fid_readn(struct p9_fid *, char *, char __user *, u32, u64); void v9fs_blank_wstat(struct p9_wstat *wstat); int v9fs_vfs_setattr_dotl(struct dentry *, struct iattr *); int v9fs_file_fsync_dotl(struct file *filp, loff_t start, loff_t end, int datasync); -ssize_t v9fs_file_write_internal(struct inode *, struct p9_fid *, - const char __user *, size_t, loff_t *, int); int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode); int v9fs_refresh_inode_dotl(struct p9_fid *fid, struct inode *inode); static inline void v9fs_invalidate_inode_attr(struct inode *inode) diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index ff1a5bac4200..2e38f9a5b472 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -51,12 +51,11 @@ */ static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page) { - int retval; - loff_t offset; - char *buffer; - struct inode *inode; + struct inode *inode = page->mapping->host; + struct bio_vec bvec = {.bv_page = page, .bv_len = PAGE_SIZE}; + struct iov_iter to; + int retval, err; - inode = page->mapping->host; p9_debug(P9_DEBUG_VFS, "\n"); BUG_ON(!PageLocked(page)); @@ -65,16 +64,16 @@ static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page) if (retval == 0) return retval; - buffer = kmap(page); - offset = page_offset(page); + iov_iter_bvec(&to, ITER_BVEC | READ, &bvec, 1, PAGE_SIZE); - retval = v9fs_fid_readn(fid, buffer, NULL, PAGE_CACHE_SIZE, offset); - if (retval < 0) { + retval = p9_client_read(fid, page_offset(page), &to, &err); + if (err) { v9fs_uncache_page(inode, page); + retval = err; goto done; } - memset(buffer + retval, 0, PAGE_CACHE_SIZE - retval); + zero_user(page, retval, PAGE_SIZE - retval); flush_dcache_page(page); SetPageUptodate(page); @@ -82,7 +81,6 @@ static int v9fs_fid_readpage(struct p9_fid *fid, struct page *page) retval = 0; done: - kunmap(page); unlock_page(page); return retval; } @@ -161,41 +159,32 @@ static void v9fs_invalidate_page(struct page *page, unsigned int offset, static int v9fs_vfs_writepage_locked(struct page *page) { - char *buffer; - int retval, len; - loff_t offset, size; - mm_segment_t old_fs; - struct v9fs_inode *v9inode; struct inode *inode = page->mapping->host; + struct v9fs_inode *v9inode = V9FS_I(inode); + loff_t size = i_size_read(inode); + struct iov_iter from; + struct bio_vec bvec; + int err, len; - v9inode = V9FS_I(inode); - size = i_size_read(inode); if (page->index == size >> PAGE_CACHE_SHIFT) len = size & ~PAGE_CACHE_MASK; else len = PAGE_CACHE_SIZE; - set_page_writeback(page); - - buffer = kmap(page); - offset = page_offset(page); + bvec.bv_page = page; + bvec.bv_offset = 0; + bvec.bv_len = len; + iov_iter_bvec(&from, ITER_BVEC | WRITE, &bvec, 1, len); - old_fs = get_fs(); - set_fs(get_ds()); /* We should have writeback_fid always set */ BUG_ON(!v9inode->writeback_fid); - retval = v9fs_file_write_internal(inode, - v9inode->writeback_fid, - (__force const char __user *)buffer, - len, &offset, 0); - if (retval > 0) - retval = 0; + set_page_writeback(page); + + p9_client_write(v9inode->writeback_fid, page_offset(page), &from, &err); - set_fs(old_fs); - kunmap(page); end_page_writeback(page); - return retval; + return err; } static int v9fs_vfs_writepage(struct page *page, struct writeback_control *wbc) @@ -261,16 +250,21 @@ static int v9fs_launder_page(struct page *page) static ssize_t v9fs_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, loff_t pos) { - /* - * FIXME - * Now that we do caching with cache mode enabled, We need - * to support direct IO - */ - p9_debug(P9_DEBUG_VFS, "v9fs_direct_IO: v9fs_direct_IO (%pD) off/no(%lld/%lu) EINVAL\n", - iocb->ki_filp, - (long long)pos, iter->nr_segs); - - return -EINVAL; + struct file *file = iocb->ki_filp; + ssize_t n; + int err = 0; + if (rw & WRITE) { + n = p9_client_write(file->private_data, pos, iter, &err); + if (n) { + struct inode *inode = file_inode(file); + loff_t i_size = i_size_read(inode); + if (pos + n > i_size) + inode_add_bytes(inode, pos + n - i_size); + } + } else { + n = p9_client_read(file->private_data, pos, iter, &err); + } + return n ? n : err; } static int v9fs_write_begin(struct file *filp, struct address_space *mapping, diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 4f1151088ebe..76c3b1ab6361 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -33,6 +33,7 @@ #include <linux/inet.h> #include <linux/idr.h> #include <linux/slab.h> +#include <linux/uio.h> #include <net/9p/9p.h> #include <net/9p/client.h> @@ -115,6 +116,7 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) int buflen; int reclen = 0; struct p9_rdir *rdir; + struct kvec kvec; p9_debug(P9_DEBUG_VFS, "name %pD\n", file); fid = file->private_data; @@ -124,16 +126,21 @@ static int v9fs_dir_readdir(struct file *file, struct dir_context *ctx) rdir = v9fs_alloc_rdir_buf(file, buflen); if (!rdir) return -ENOMEM; + kvec.iov_base = rdir->buf; + kvec.iov_len = buflen; while (1) { if (rdir->tail == rdir->head) { - err = v9fs_file_readn(file, rdir->buf, NULL, - buflen, ctx->pos); - if (err <= 0) + struct iov_iter to; + int n; + iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buflen); + n = p9_client_read(file->private_data, ctx->pos, &to, + &err); + if (err) return err; rdir->head = 0; - rdir->tail = err; + rdir->tail = n; } while (rdir->head < rdir->tail) { p9stat_init(&st); diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index b40133796b87..d7fcb775311e 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -36,6 +36,8 @@ #include <linux/utsname.h> #include <asm/uaccess.h> #include <linux/idr.h> +#include <linux/uio.h> +#include <linux/slab.h> #include <net/9p/9p.h> #include <net/9p/client.h> @@ -285,6 +287,7 @@ static int v9fs_file_getlock(struct file *filp, struct file_lock *fl) fl->fl_end = glock.start + glock.length - 1; fl->fl_pid = glock.proc_id; } + kfree(glock.client_id); return res; } @@ -364,63 +367,6 @@ out_err: } /** - * v9fs_fid_readn - read from a fid - * @fid: fid to read - * @data: data buffer to read data into - * @udata: user data buffer to read data into - * @count: size of buffer - * @offset: offset at which to read data - * - */ -ssize_t -v9fs_fid_readn(struct p9_fid *fid, char *data, char __user *udata, u32 count, - u64 offset) -{ - int n, total, size; - - p9_debug(P9_DEBUG_VFS, "fid %d offset %llu count %d\n", - fid->fid, (long long unsigned)offset, count); - n = 0; - total = 0; - size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; - do { - n = p9_client_read(fid, data, udata, offset, count); - if (n <= 0) - break; - - if (data) - data += n; - if (udata) - udata += n; - - offset += n; - count -= n; - total += n; - } while (count > 0 && n == size); - - if (n < 0) - total = n; - - return total; -} - -/** - * v9fs_file_readn - read from a file - * @filp: file pointer to read - * @data: data buffer to read data into - * @udata: user data buffer to read data into - * @count: size of buffer - * @offset: offset at which to read data - * - */ -ssize_t -v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, - u64 offset) -{ - return v9fs_fid_readn(filp->private_data, data, udata, count, offset); -} - -/** * v9fs_file_read - read from a file * @filp: file pointer to read * @udata: user data buffer to read data into @@ -430,69 +376,22 @@ v9fs_file_readn(struct file *filp, char *data, char __user *udata, u32 count, */ static ssize_t -v9fs_file_read(struct file *filp, char __user *udata, size_t count, - loff_t * offset) +v9fs_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { - int ret; - struct p9_fid *fid; - size_t size; + struct p9_fid *fid = iocb->ki_filp->private_data; + int ret, err; - p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n", count, *offset); - fid = filp->private_data; + p9_debug(P9_DEBUG_VFS, "count %zu offset %lld\n", + iov_iter_count(to), iocb->ki_pos); - size = fid->iounit ? fid->iounit : fid->clnt->msize - P9_IOHDRSZ; - if (count > size) - ret = v9fs_file_readn(filp, NULL, udata, count, *offset); - else - ret = p9_client_read(fid, NULL, udata, *offset, count); - - if (ret > 0) - *offset += ret; + ret = p9_client_read(fid, iocb->ki_pos, to, &err); + if (!ret) + return err; + iocb->ki_pos += ret; return ret; } -ssize_t -v9fs_file_write_internal(struct inode *inode, struct p9_fid *fid, - const char __user *data, size_t count, - loff_t *offset, int invalidate) -{ - int n; - loff_t i_size; - size_t total = 0; - loff_t origin = *offset; - unsigned long pg_start, pg_end; - - p9_debug(P9_DEBUG_VFS, "data %p count %d offset %x\n", - data, (int)count, (int)*offset); - - do { - n = p9_client_write(fid, NULL, data+total, origin+total, count); - if (n <= 0) - break; - count -= n; - total += n; - } while (count > 0); - - if (invalidate && (total > 0)) { - pg_start = origin >> PAGE_CACHE_SHIFT; - pg_end = (origin + total - 1) >> PAGE_CACHE_SHIFT; - if (inode->i_mapping && inode->i_mapping->nrpages) - invalidate_inode_pages2_range(inode->i_mapping, - pg_start, pg_end); - *offset += total; - i_size = i_size_read(inode); - if (*offset > i_size) { - inode_add_bytes(inode, *offset - i_size); - i_size_write(inode, *offset); - } - } - if (n < 0) - return n; - - return total; -} - /** * v9fs_file_write - write to a file * @filp: file pointer to write @@ -502,35 +401,45 @@ v9fs_file_write_internal(struct inode *inode, struct p9_fid *fid, * */ static ssize_t -v9fs_file_write(struct file *filp, const char __user * data, - size_t count, loff_t *offset) +v9fs_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { + struct file *file = iocb->ki_filp; ssize_t retval = 0; - loff_t origin = *offset; + loff_t origin = iocb->ki_pos; + size_t count = iov_iter_count(from); + int err = 0; - - retval = generic_write_checks(filp, &origin, &count, 0); + retval = generic_write_checks(file, &origin, &count, 0); if (retval) - goto out; + return retval; + + iov_iter_truncate(from, count); - retval = -EINVAL; - if ((ssize_t) count < 0) - goto out; - retval = 0; if (!count) - goto out; + return 0; - retval = v9fs_file_write_internal(file_inode(filp), - filp->private_data, - data, count, &origin, 1); - /* update offset on successful write */ - if (retval > 0) - *offset = origin; -out: - return retval; + retval = p9_client_write(file->private_data, origin, from, &err); + if (retval > 0) { + struct inode *inode = file_inode(file); + loff_t i_size; + unsigned long pg_start, pg_end; + pg_start = origin >> PAGE_CACHE_SHIFT; + pg_end = (origin + retval - 1) >> PAGE_CACHE_SHIFT; + if (inode->i_mapping && inode->i_mapping->nrpages) + invalidate_inode_pages2_range(inode->i_mapping, + pg_start, pg_end); + origin += retval; + i_size = i_size_read(inode); + iocb->ki_pos = origin; + if (origin > i_size) { + inode_add_bytes(inode, origin - i_size); + i_size_write(inode, origin); + } + return retval; + } + return err; } - static int v9fs_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) { @@ -657,44 +566,6 @@ out_unlock: return VM_FAULT_NOPAGE; } -static ssize_t -v9fs_direct_read(struct file *filp, char __user *udata, size_t count, - loff_t *offsetp) -{ - loff_t size, offset; - struct inode *inode; - struct address_space *mapping; - - offset = *offsetp; - mapping = filp->f_mapping; - inode = mapping->host; - if (!count) - return 0; - size = i_size_read(inode); - if (offset < size) - filemap_write_and_wait_range(mapping, offset, - offset + count - 1); - - return v9fs_file_read(filp, udata, count, offsetp); -} - -/** - * v9fs_cached_file_read - read from a file - * @filp: file pointer to read - * @data: user data buffer to read data into - * @count: size of buffer - * @offset: offset at which to read data - * - */ -static ssize_t -v9fs_cached_file_read(struct file *filp, char __user *data, size_t count, - loff_t *offset) -{ - if (filp->f_flags & O_DIRECT) - return v9fs_direct_read(filp, data, count, offset); - return new_sync_read(filp, data, count, offset); -} - /** * v9fs_mmap_file_read - read from a file * @filp: file pointer to read @@ -704,84 +575,12 @@ v9fs_cached_file_read(struct file *filp, char __user *data, size_t count, * */ static ssize_t -v9fs_mmap_file_read(struct file *filp, char __user *data, size_t count, - loff_t *offset) +v9fs_mmap_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { /* TODO: Check if there are dirty pages */ - return v9fs_file_read(filp, data, count, offset); + return v9fs_file_read_iter(iocb, to); } -static ssize_t -v9fs_direct_write(struct file *filp, const char __user * data, - size_t count, loff_t *offsetp) -{ - loff_t offset; - ssize_t retval; - struct inode *inode; - struct address_space *mapping; - - offset = *offsetp; - mapping = filp->f_mapping; - inode = mapping->host; - if (!count) - return 0; - - mutex_lock(&inode->i_mutex); - retval = filemap_write_and_wait_range(mapping, offset, - offset + count - 1); - if (retval) - goto err_out; - /* - * After a write we want buffered reads to be sure to go to disk to get - * the new data. We invalidate clean cached page from the region we're - * about to write. We do this *before* the write so that if we fail - * here we fall back to buffered write - */ - if (mapping->nrpages) { - pgoff_t pg_start = offset >> PAGE_CACHE_SHIFT; - pgoff_t pg_end = (offset + count - 1) >> PAGE_CACHE_SHIFT; - - retval = invalidate_inode_pages2_range(mapping, - pg_start, pg_end); - /* - * If a page can not be invalidated, fall back - * to buffered write. - */ - if (retval) { - if (retval == -EBUSY) - goto buff_write; - goto err_out; - } - } - retval = v9fs_file_write(filp, data, count, offsetp); -err_out: - mutex_unlock(&inode->i_mutex); - return retval; - -buff_write: - mutex_unlock(&inode->i_mutex); - return new_sync_write(filp, data, count, offsetp); -} - -/** - * v9fs_cached_file_write - write to a file - * @filp: file pointer to write - * @data: data buffer to write data from - * @count: size of buffer - * @offset: offset at which to write data - * - */ -static ssize_t -v9fs_cached_file_write(struct file *filp, const char __user * data, - size_t count, loff_t *offset) -{ - - if (filp->f_flags & O_DIRECT) - return v9fs_direct_write(filp, data, count, offset); - return new_sync_write(filp, data, count, offset); -} - - /** * v9fs_mmap_file_write - write to a file * @filp: file pointer to write @@ -791,14 +590,13 @@ v9fs_cached_file_write(struct file *filp, const char __user * data, * */ static ssize_t -v9fs_mmap_file_write(struct file *filp, const char __user *data, - size_t count, loff_t *offset) +v9fs_mmap_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { /* * TODO: invalidate mmaps on filp's inode between * offset and offset+count */ - return v9fs_file_write(filp, data, count, offset); + return v9fs_file_write_iter(iocb, from); } static void v9fs_mmap_vm_close(struct vm_area_struct *vma) @@ -843,8 +641,6 @@ static const struct vm_operations_struct v9fs_mmap_file_vm_ops = { const struct file_operations v9fs_cached_file_operations = { .llseek = generic_file_llseek, - .read = v9fs_cached_file_read, - .write = v9fs_cached_file_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .open = v9fs_file_open, @@ -856,8 +652,6 @@ const struct file_operations v9fs_cached_file_operations = { const struct file_operations v9fs_cached_file_operations_dotl = { .llseek = generic_file_llseek, - .read = v9fs_cached_file_read, - .write = v9fs_cached_file_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .open = v9fs_file_open, @@ -870,8 +664,8 @@ const struct file_operations v9fs_cached_file_operations_dotl = { const struct file_operations v9fs_file_operations = { .llseek = generic_file_llseek, - .read = v9fs_file_read, - .write = v9fs_file_write, + .read_iter = v9fs_file_read_iter, + .write_iter = v9fs_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock, @@ -881,8 +675,8 @@ const struct file_operations v9fs_file_operations = { const struct file_operations v9fs_file_operations_dotl = { .llseek = generic_file_llseek, - .read = v9fs_file_read, - .write = v9fs_file_write, + .read_iter = v9fs_file_read_iter, + .write_iter = v9fs_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock_dotl, @@ -893,8 +687,8 @@ const struct file_operations v9fs_file_operations_dotl = { const struct file_operations v9fs_mmap_file_operations = { .llseek = generic_file_llseek, - .read = v9fs_mmap_file_read, - .write = v9fs_mmap_file_write, + .read_iter = v9fs_mmap_file_read_iter, + .write_iter = v9fs_mmap_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock, @@ -904,8 +698,8 @@ const struct file_operations v9fs_mmap_file_operations = { const struct file_operations v9fs_mmap_file_operations_dotl = { .llseek = generic_file_llseek, - .read = v9fs_mmap_file_read, - .write = v9fs_mmap_file_write, + .read_iter = v9fs_mmap_file_read_iter, + .write_iter = v9fs_mmap_file_write_iter, .open = v9fs_file_open, .release = v9fs_dir_release, .lock = v9fs_file_lock_dotl, diff --git a/fs/9p/xattr.c b/fs/9p/xattr.c index f95e01e058e4..0cf44b6cccd6 100644 --- a/fs/9p/xattr.c +++ b/fs/9p/xattr.c @@ -15,6 +15,7 @@ #include <linux/module.h> #include <linux/fs.h> #include <linux/sched.h> +#include <linux/uio.h> #include <net/9p/9p.h> #include <net/9p/client.h> @@ -25,50 +26,34 @@ ssize_t v9fs_fid_xattr_get(struct p9_fid *fid, const char *name, void *buffer, size_t buffer_size) { ssize_t retval; - int msize, read_count; - u64 offset = 0, attr_size; + u64 attr_size; struct p9_fid *attr_fid; + struct kvec kvec = {.iov_base = buffer, .iov_len = buffer_size}; + struct iov_iter to; + int err; + + iov_iter_kvec(&to, READ | ITER_KVEC, &kvec, 1, buffer_size); attr_fid = p9_client_xattrwalk(fid, name, &attr_size); if (IS_ERR(attr_fid)) { retval = PTR_ERR(attr_fid); p9_debug(P9_DEBUG_VFS, "p9_client_attrwalk failed %zd\n", retval); - attr_fid = NULL; - goto error; - } - if (!buffer_size) { - /* request to get the attr_size */ - retval = attr_size; - goto error; + return retval; } if (attr_size > buffer_size) { - retval = -ERANGE; - goto error; - } - msize = attr_fid->clnt->msize; - while (attr_size) { - if (attr_size > (msize - P9_IOHDRSZ)) - read_count = msize - P9_IOHDRSZ; + if (!buffer_size) /* request to get the attr_size */ + retval = attr_size; else - read_count = attr_size; - read_count = p9_client_read(attr_fid, ((char *)buffer)+offset, - NULL, offset, read_count); - if (read_count < 0) { - /* error in xattr read */ - retval = read_count; - goto error; - } - offset += read_count; - attr_size -= read_count; + retval = -ERANGE; + } else { + iov_iter_truncate(&to, attr_size); + retval = p9_client_read(attr_fid, 0, &to, &err); + if (err) + retval = err; } - /* Total read xattr bytes */ - retval = offset; -error: - if (attr_fid) - p9_client_clunk(attr_fid); + p9_client_clunk(attr_fid); return retval; - } @@ -120,8 +105,11 @@ int v9fs_xattr_set(struct dentry *dentry, const char *name, int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name, const void *value, size_t value_len, int flags) { - u64 offset = 0; - int retval, msize, write_count; + struct kvec kvec = {.iov_base = (void *)value, .iov_len = value_len}; + struct iov_iter from; + int retval; + + iov_iter_kvec(&from, WRITE | ITER_KVEC, &kvec, 1, value_len); p9_debug(P9_DEBUG_VFS, "name = %s value_len = %zu flags = %d\n", name, value_len, flags); @@ -135,29 +123,11 @@ int v9fs_fid_xattr_set(struct p9_fid *fid, const char *name, * On success fid points to xattr */ retval = p9_client_xattrcreate(fid, name, value_len, flags); - if (retval < 0) { + if (retval < 0) p9_debug(P9_DEBUG_VFS, "p9_client_xattrcreate failed %d\n", retval); - goto err; - } - msize = fid->clnt->msize; - while (value_len) { - if (value_len > (msize - P9_IOHDRSZ)) - write_count = msize - P9_IOHDRSZ; - else - write_count = value_len; - write_count = p9_client_write(fid, ((char *)value)+offset, - NULL, offset, write_count); - if (write_count < 0) { - /* error in xattr write */ - retval = write_count; - goto err; - } - offset += write_count; - value_len -= write_count; - } - retval = 0; -err: + else + p9_client_write(fid, 0, &from, &retval); p9_client_clunk(fid); return retval; } diff --git a/fs/adfs/file.c b/fs/adfs/file.c index 07c9edce5aa7..46c0d5671cd5 100644 --- a/fs/adfs/file.c +++ b/fs/adfs/file.c @@ -23,11 +23,9 @@ const struct file_operations adfs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, - .write = new_sync_write, .write_iter = generic_file_write_iter, .splice_read = generic_file_splice_read, }; diff --git a/fs/affs/file.c b/fs/affs/file.c index 3aa7eb66547e..7c1a3d4c19c2 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c @@ -969,9 +969,7 @@ int affs_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) } const struct file_operations affs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .open = affs_file_open, diff --git a/fs/afs/file.c b/fs/afs/file.c index 932ce07948b3..999bc3caec92 100644 --- a/fs/afs/file.c +++ b/fs/afs/file.c @@ -31,8 +31,6 @@ const struct file_operations afs_file_operations = { .open = afs_open, .release = afs_release, .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = afs_file_write, .mmap = generic_file_readonly_mmap, @@ -692,8 +692,7 @@ static struct kioctx *ioctx_alloc(unsigned nr_events) nr_events *= 2; /* Prevent overflows */ - if ((nr_events > (0x10000000U / sizeof(struct io_event))) || - (nr_events > (0x10000000U / sizeof(struct kiocb)))) { + if (nr_events > (0x10000000U / sizeof(struct io_event))) { pr_debug("ENOMEM: nr_events too high\n"); return ERR_PTR(-EINVAL); } @@ -1356,8 +1355,6 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx) return -EINVAL; } -typedef ssize_t (aio_rw_op)(struct kiocb *, const struct iovec *, - unsigned long, loff_t); typedef ssize_t (rw_iter_op)(struct kiocb *, struct iov_iter *); static int aio_setup_vectored_rw(int rw, char __user *buf, size_t len, @@ -1386,7 +1383,6 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, ssize_t ret; int rw; fmode_t mode; - aio_rw_op *rw_op; rw_iter_op *iter_op; struct iovec inline_vecs[UIO_FASTIOV], *iovec = inline_vecs; struct iov_iter iter; @@ -1396,7 +1392,6 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, case IOCB_CMD_PREADV: mode = FMODE_READ; rw = READ; - rw_op = file->f_op->aio_read; iter_op = file->f_op->read_iter; goto rw_common; @@ -1404,14 +1399,13 @@ static ssize_t aio_run_iocb(struct kiocb *req, unsigned opcode, case IOCB_CMD_PWRITEV: mode = FMODE_WRITE; rw = WRITE; - rw_op = file->f_op->aio_write; iter_op = file->f_op->write_iter; goto rw_common; rw_common: if (unlikely(!(file->f_mode & mode))) return -EBADF; - if (!rw_op && !iter_op) + if (!iter_op) return -EINVAL; if (opcode == IOCB_CMD_PREADV || opcode == IOCB_CMD_PWRITEV) @@ -1431,21 +1425,10 @@ rw_common: len = ret; - /* XXX: move/kill - rw_verify_area()? */ - /* This matches the pread()/pwrite() logic */ - if (req->ki_pos < 0) { - ret = -EINVAL; - break; - } - if (rw == WRITE) file_start_write(file); - if (iter_op) { - ret = iter_op(req, &iter); - } else { - ret = rw_op(req, iter.iov, iter.nr_segs, req->ki_pos); - } + ret = iter_op(req, &iter); if (rw == WRITE) file_end_write(file); diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h index 8e98cf954bab..d10e619632ab 100644 --- a/fs/autofs4/autofs_i.h +++ b/fs/autofs4/autofs_i.h @@ -213,7 +213,7 @@ void autofs4_clean_ino(struct autofs_info *); static inline int autofs_prepare_pipe(struct file *pipe) { - if (!pipe->f_op->write) + if (!(pipe->f_mode & FMODE_CAN_WRITE)) return -EINVAL; if (!S_ISFIFO(file_inode(pipe)->i_mode)) return -EINVAL; diff --git a/fs/autofs4/waitq.c b/fs/autofs4/waitq.c index 116fd38ee472..2ad05ab93db8 100644 --- a/fs/autofs4/waitq.c +++ b/fs/autofs4/waitq.c @@ -70,7 +70,7 @@ static int autofs4_write(struct autofs_sb_info *sbi, mutex_lock(&sbi->pipe_mutex); while (bytes && - (wr = file->f_op->write(file,data,bytes,&file->f_pos)) > 0) { + (wr = __vfs_write(file,data,bytes,&file->f_pos)) > 0) { data += wr; bytes -= wr; } diff --git a/fs/bfs/file.c b/fs/bfs/file.c index e7f88ace1a25..97f1b5160155 100644 --- a/fs/bfs/file.c +++ b/fs/bfs/file.c @@ -23,9 +23,7 @@ const struct file_operations bfs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, diff --git a/fs/block_dev.c b/fs/block_dev.c index 2e522aed6584..b5e87896f517 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1659,8 +1659,6 @@ const struct file_operations def_blk_fops = { .open = blkdev_open, .release = blkdev_close, .llseek = block_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = blkdev_read_iter, .write_iter = blkdev_write_iter, .mmap = generic_file_mmap, diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index aee18f84e315..cdc801c85105 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -2806,8 +2806,6 @@ out: const struct file_operations btrfs_file_operations = { .llseek = btrfs_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .splice_read = generic_file_splice_read, .write_iter = btrfs_file_write_iter, diff --git a/fs/ceph/file.c b/fs/ceph/file.c index 139f2fea91a0..56237ea5fc22 100644 --- a/fs/ceph/file.c +++ b/fs/ceph/file.c @@ -1331,8 +1331,6 @@ const struct file_operations ceph_file_fops = { .open = ceph_open, .release = ceph_release, .llseek = ceph_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = ceph_read_iter, .write_iter = ceph_write_iter, .mmap = ceph_mmap, diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index d72fe37f5420..eaab4b2a0595 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -906,8 +906,6 @@ const struct inode_operations cifs_symlink_inode_ops = { }; const struct file_operations cifs_file_ops = { - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_loose_read_iter, .write_iter = cifs_file_write_iter, .open = cifs_open, @@ -926,8 +924,6 @@ const struct file_operations cifs_file_ops = { }; const struct file_operations cifs_file_strict_ops = { - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_strict_readv, .write_iter = cifs_strict_writev, .open = cifs_open, @@ -947,8 +943,6 @@ const struct file_operations cifs_file_strict_ops = { const struct file_operations cifs_file_direct_ops = { /* BB reevaluate whether they can be done with directio, no cache */ - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_user_readv, .write_iter = cifs_user_writev, .open = cifs_open, @@ -967,8 +961,6 @@ const struct file_operations cifs_file_direct_ops = { }; const struct file_operations cifs_file_nobrl_ops = { - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_loose_read_iter, .write_iter = cifs_file_write_iter, .open = cifs_open, @@ -986,8 +978,6 @@ const struct file_operations cifs_file_nobrl_ops = { }; const struct file_operations cifs_file_strict_nobrl_ops = { - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_strict_readv, .write_iter = cifs_strict_writev, .open = cifs_open, @@ -1006,8 +996,6 @@ const struct file_operations cifs_file_strict_nobrl_ops = { const struct file_operations cifs_file_direct_nobrl_ops = { /* BB reevaluate whether they can be done with directio, no cache */ - .read = new_sync_read, - .write = new_sync_write, .read_iter = cifs_user_readv, .write_iter = cifs_user_writev, .open = cifs_open, diff --git a/fs/coda/file.c b/fs/coda/file.c index d244d743a232..1da3805f3ddc 100644 --- a/fs/coda/file.c +++ b/fs/coda/file.c @@ -27,19 +27,14 @@ #include "coda_int.h" static ssize_t -coda_file_read(struct file *coda_file, char __user *buf, size_t count, loff_t *ppos) +coda_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct coda_file_info *cfi; - struct file *host_file; + struct file *coda_file = iocb->ki_filp; + struct coda_file_info *cfi = CODA_FTOC(coda_file); - cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - host_file = cfi->cfi_container; - if (!host_file->f_op->read) - return -EINVAL; - - return host_file->f_op->read(host_file, buf, count, ppos); + return vfs_iter_read(cfi->cfi_container, to, &iocb->ki_pos); } static ssize_t @@ -64,32 +59,25 @@ coda_file_splice_read(struct file *coda_file, loff_t *ppos, } static ssize_t -coda_file_write(struct file *coda_file, const char __user *buf, size_t count, loff_t *ppos) +coda_file_write_iter(struct kiocb *iocb, struct iov_iter *to) { - struct inode *host_inode, *coda_inode = file_inode(coda_file); - struct coda_file_info *cfi; + struct file *coda_file = iocb->ki_filp; + struct inode *coda_inode = file_inode(coda_file); + struct coda_file_info *cfi = CODA_FTOC(coda_file); struct file *host_file; ssize_t ret; - cfi = CODA_FTOC(coda_file); BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC); - host_file = cfi->cfi_container; - - if (!host_file->f_op->write) - return -EINVAL; - host_inode = file_inode(host_file); + host_file = cfi->cfi_container; file_start_write(host_file); mutex_lock(&coda_inode->i_mutex); - - ret = host_file->f_op->write(host_file, buf, count, ppos); - - coda_inode->i_size = host_inode->i_size; + ret = vfs_iter_write(cfi->cfi_container, to, &iocb->ki_pos); + coda_inode->i_size = file_inode(host_file)->i_size; coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9; coda_inode->i_mtime = coda_inode->i_ctime = CURRENT_TIME_SEC; mutex_unlock(&coda_inode->i_mutex); file_end_write(host_file); - return ret; } @@ -231,8 +219,8 @@ int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync) const struct file_operations coda_file_operations = { .llseek = generic_file_llseek, - .read = coda_file_read, - .write = coda_file_write, + .read_iter = coda_file_read_iter, + .write_iter = coda_file_write_iter, .mmap = coda_file_mmap, .open = coda_open, .release = coda_release, diff --git a/fs/coredump.c b/fs/coredump.c index f319926ddf8c..bbbe139ab280 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -657,7 +657,7 @@ void do_coredump(const siginfo_t *siginfo) */ if (!uid_eq(inode->i_uid, current_fsuid())) goto close_fail; - if (!cprm.file->f_op->write) + if (!(cprm.file->f_mode & FMODE_CAN_WRITE)) goto close_fail; if (do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file)) goto close_fail; diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 79675089443d..a65786e26b05 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -358,9 +358,7 @@ const struct file_operations ecryptfs_dir_fops = { const struct file_operations ecryptfs_main_fops = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = ecryptfs_read_update_atime, - .write = new_sync_write, .write_iter = generic_file_write_iter, .iterate = ecryptfs_readdir, .unlocked_ioctl = ecryptfs_unlocked_ioctl, diff --git a/fs/exofs/file.c b/fs/exofs/file.c index 1a376b42d305..906de66e8e7e 100644 --- a/fs/exofs/file.c +++ b/fs/exofs/file.c @@ -67,8 +67,6 @@ static int exofs_flush(struct file *file, fl_owner_t id) const struct file_operations exofs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, diff --git a/fs/ext2/file.c b/fs/ext2/file.c index 19cac93a65d3..3a0a6c6406d0 100644 --- a/fs/ext2/file.c +++ b/fs/ext2/file.c @@ -93,8 +93,6 @@ int ext2_fsync(struct file *file, loff_t start, loff_t end, int datasync) */ const struct file_operations ext2_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .unlocked_ioctl = ext2_ioctl, diff --git a/fs/ext3/file.c b/fs/ext3/file.c index a062fa1e1b11..3b8f650de22c 100644 --- a/fs/ext3/file.c +++ b/fs/ext3/file.c @@ -50,8 +50,6 @@ static int ext3_release_file (struct inode * inode, struct file * filp) const struct file_operations ext3_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .unlocked_ioctl = ext3_ioctl, diff --git a/fs/ext4/file.c b/fs/ext4/file.c index e6d4280d66be..7a6defcf3352 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -608,8 +608,6 @@ loff_t ext4_llseek(struct file *file, loff_t offset, int whence) const struct file_operations ext4_file_operations = { .llseek = ext4_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = ext4_file_write_iter, .unlocked_ioctl = ext4_ioctl, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index 98dac27bc3f7..df6a0596eccf 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -1104,8 +1104,6 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations f2fs_file_operations = { .llseek = f2fs_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .open = generic_file_open, diff --git a/fs/fat/file.c b/fs/fat/file.c index 8429c68e3057..1e98d333879f 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -170,8 +170,6 @@ int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync) const struct file_operations fat_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, diff --git a/fs/file_table.c b/fs/file_table.c index 3f85411b03ce..294174dcc226 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -168,10 +168,10 @@ struct file *alloc_file(struct path *path, fmode_t mode, file->f_inode = path->dentry->d_inode; file->f_mapping = path->dentry->d_inode->i_mapping; if ((mode & FMODE_READ) && - likely(fop->read || fop->aio_read || fop->read_iter)) + likely(fop->read || fop->read_iter)) mode |= FMODE_CAN_READ; if ((mode & FMODE_WRITE) && - likely(fop->write || fop->aio_write || fop->write_iter)) + likely(fop->write || fop->write_iter)) mode |= FMODE_CAN_WRITE; file->f_mode = mode; file->f_op = fop; diff --git a/fs/fuse/cuse.c b/fs/fuse/cuse.c index b3fa05032234..e5bbf748b698 100644 --- a/fs/fuse/cuse.c +++ b/fs/fuse/cuse.c @@ -88,32 +88,23 @@ static struct list_head *cuse_conntbl_head(dev_t devt) * FUSE file. */ -static ssize_t cuse_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) +static ssize_t cuse_read_iter(struct kiocb *kiocb, struct iov_iter *to) { + struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp }; loff_t pos = 0; - struct iovec iov = { .iov_base = buf, .iov_len = count }; - struct fuse_io_priv io = { .async = 0, .file = file }; - struct iov_iter ii; - iov_iter_init(&ii, READ, &iov, 1, count); - return fuse_direct_io(&io, &ii, &pos, FUSE_DIO_CUSE); + return fuse_direct_io(&io, to, &pos, FUSE_DIO_CUSE); } -static ssize_t cuse_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t cuse_write_iter(struct kiocb *kiocb, struct iov_iter *from) { + struct fuse_io_priv io = { .async = 0, .file = kiocb->ki_filp }; loff_t pos = 0; - struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count }; - struct fuse_io_priv io = { .async = 0, .file = file }; - struct iov_iter ii; - iov_iter_init(&ii, WRITE, &iov, 1, count); - /* * No locking or generic_write_checks(), the server is * responsible for locking and sanity checks. */ - return fuse_direct_io(&io, &ii, &pos, + return fuse_direct_io(&io, from, &pos, FUSE_DIO_WRITE | FUSE_DIO_CUSE); } @@ -186,8 +177,8 @@ static long cuse_file_compat_ioctl(struct file *file, unsigned int cmd, static const struct file_operations cuse_frontend_fops = { .owner = THIS_MODULE, - .read = cuse_read, - .write = cuse_write, + .read_iter = cuse_read_iter, + .write_iter = cuse_write_iter, .open = cuse_open, .release = cuse_release, .unlocked_ioctl = cuse_file_ioctl, diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 95a2797eef66..c8b68ab2e574 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -710,28 +710,26 @@ struct fuse_copy_state { struct fuse_conn *fc; int write; struct fuse_req *req; - const struct iovec *iov; + struct iov_iter *iter; struct pipe_buffer *pipebufs; struct pipe_buffer *currbuf; struct pipe_inode_info *pipe; unsigned long nr_segs; - unsigned long seglen; - unsigned long addr; struct page *pg; unsigned len; unsigned offset; unsigned move_pages:1; }; -static void fuse_copy_init(struct fuse_copy_state *cs, struct fuse_conn *fc, +static void fuse_copy_init(struct fuse_copy_state *cs, + struct fuse_conn *fc, int write, - const struct iovec *iov, unsigned long nr_segs) + struct iov_iter *iter) { memset(cs, 0, sizeof(*cs)); cs->fc = fc; cs->write = write; - cs->iov = iov; - cs->nr_segs = nr_segs; + cs->iter = iter; } /* Unmap and put previous page of userspace buffer */ @@ -799,22 +797,16 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) cs->nr_segs++; } } else { - if (!cs->seglen) { - BUG_ON(!cs->nr_segs); - cs->seglen = cs->iov[0].iov_len; - cs->addr = (unsigned long) cs->iov[0].iov_base; - cs->iov++; - cs->nr_segs--; - } - err = get_user_pages_fast(cs->addr, 1, cs->write, &page); + size_t off; + err = iov_iter_get_pages(cs->iter, &page, PAGE_SIZE, 1, &off); if (err < 0) return err; - BUG_ON(err != 1); + BUG_ON(!err); + cs->len = err; + cs->offset = off; cs->pg = page; - cs->offset = cs->addr % PAGE_SIZE; - cs->len = min(PAGE_SIZE - cs->offset, cs->seglen); - cs->seglen -= cs->len; - cs->addr += cs->len; + cs->offset = off; + iov_iter_advance(cs->iter, err); } return lock_request(cs->fc, cs->req); @@ -1363,8 +1355,7 @@ static int fuse_dev_open(struct inode *inode, struct file *file) return 0; } -static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to) { struct fuse_copy_state cs; struct file *file = iocb->ki_filp; @@ -1372,9 +1363,12 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov, if (!fc) return -EPERM; - fuse_copy_init(&cs, fc, 1, iov, nr_segs); + if (!iter_is_iovec(to)) + return -EINVAL; - return fuse_dev_do_read(fc, file, &cs, iov_length(iov, nr_segs)); + fuse_copy_init(&cs, fc, 1, to); + + return fuse_dev_do_read(fc, file, &cs, iov_iter_count(to)); } static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, @@ -1394,7 +1388,7 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos, if (!bufs) return -ENOMEM; - fuse_copy_init(&cs, fc, 1, NULL, 0); + fuse_copy_init(&cs, fc, 1, NULL); cs.pipebufs = bufs; cs.pipe = pipe; ret = fuse_dev_do_read(fc, in, &cs, len); @@ -1970,17 +1964,19 @@ static ssize_t fuse_dev_do_write(struct fuse_conn *fc, return err; } -static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from) { struct fuse_copy_state cs; struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp); if (!fc) return -EPERM; - fuse_copy_init(&cs, fc, 0, iov, nr_segs); + if (!iter_is_iovec(from)) + return -EINVAL; + + fuse_copy_init(&cs, fc, 0, from); - return fuse_dev_do_write(fc, &cs, iov_length(iov, nr_segs)); + return fuse_dev_do_write(fc, &cs, iov_iter_count(from)); } static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, @@ -2043,8 +2039,9 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe, } pipe_unlock(pipe); - fuse_copy_init(&cs, fc, 0, NULL, nbuf); + fuse_copy_init(&cs, fc, 0, NULL); cs.pipebufs = bufs; + cs.nr_segs = nbuf; cs.pipe = pipe; if (flags & SPLICE_F_MOVE) @@ -2232,11 +2229,9 @@ const struct file_operations fuse_dev_operations = { .owner = THIS_MODULE, .open = fuse_dev_open, .llseek = no_llseek, - .read = do_sync_read, - .aio_read = fuse_dev_read, + .read_iter = fuse_dev_read, .splice_read = fuse_dev_splice_read, - .write = do_sync_write, - .aio_write = fuse_dev_write, + .write_iter = fuse_dev_write, .splice_write = fuse_dev_splice_write, .poll = fuse_dev_poll, .release = fuse_dev_release, diff --git a/fs/fuse/file.c b/fs/fuse/file.c index ff102cbf16ea..e1afdd7abf90 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1401,55 +1401,33 @@ static ssize_t __fuse_direct_read(struct fuse_io_priv *io, return res; } -static ssize_t fuse_direct_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t fuse_direct_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct fuse_io_priv io = { .async = 0, .file = file }; - struct iovec iov = { .iov_base = buf, .iov_len = count }; - struct iov_iter ii; - iov_iter_init(&ii, READ, &iov, 1, count); - return __fuse_direct_read(&io, &ii, ppos); + struct fuse_io_priv io = { .async = 0, .file = iocb->ki_filp }; + return __fuse_direct_read(&io, to, &iocb->ki_pos); } -static ssize_t __fuse_direct_write(struct fuse_io_priv *io, - struct iov_iter *iter, - loff_t *ppos) +static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) { - struct file *file = io->file; - struct inode *inode = file_inode(file); - size_t count = iov_iter_count(iter); - ssize_t res; - - - res = generic_write_checks(file, ppos, &count, 0); - if (!res) { - iov_iter_truncate(iter, count); - res = fuse_direct_io(io, iter, ppos, FUSE_DIO_WRITE); - } - - fuse_invalidate_attr(inode); - - return res; -} - -static ssize_t fuse_direct_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = count }; + struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); - ssize_t res; struct fuse_io_priv io = { .async = 0, .file = file }; - struct iov_iter ii; - iov_iter_init(&ii, WRITE, &iov, 1, count); + size_t count = iov_iter_count(from); + ssize_t res; if (is_bad_inode(inode)) return -EIO; /* Don't allow parallel writes to the same file */ mutex_lock(&inode->i_mutex); - res = __fuse_direct_write(&io, &ii, ppos); + res = generic_write_checks(file, &iocb->ki_pos, &count, 0); + if (!res) { + iov_iter_truncate(from, count); + res = fuse_direct_io(&io, from, &iocb->ki_pos, FUSE_DIO_WRITE); + } + fuse_invalidate_attr(inode); if (res > 0) - fuse_write_update_size(inode, *ppos); + fuse_write_update_size(inode, iocb->ki_pos); mutex_unlock(&inode->i_mutex); return res; @@ -2862,10 +2840,17 @@ fuse_direct_IO(int rw, struct kiocb *iocb, struct iov_iter *iter, if (io->async && is_sync_kiocb(iocb)) io->done = &wait; - if (rw == WRITE) - ret = __fuse_direct_write(io, iter, &pos); - else + if (rw == WRITE) { + ret = generic_write_checks(file, &pos, &count, 0); + if (!ret) { + iov_iter_truncate(iter, count); + ret = fuse_direct_io(io, iter, &pos, FUSE_DIO_WRITE); + } + + fuse_invalidate_attr(inode); + } else { ret = __fuse_direct_read(io, iter, &pos); + } if (io->async) { fuse_aio_complete(io, ret < 0 ? ret : 0, -1); @@ -2968,9 +2953,7 @@ out: static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, - .read = new_sync_read, .read_iter = fuse_file_read_iter, - .write = new_sync_write, .write_iter = fuse_file_write_iter, .mmap = fuse_file_mmap, .open = fuse_open, @@ -2988,8 +2971,8 @@ static const struct file_operations fuse_file_operations = { static const struct file_operations fuse_direct_io_file_operations = { .llseek = fuse_file_llseek, - .read = fuse_direct_read, - .write = fuse_direct_write, + .read_iter = fuse_direct_read_iter, + .write_iter = fuse_direct_write_iter, .mmap = fuse_direct_mmap, .open = fuse_open, .flush = fuse_flush, diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 8ec43ab5babf..207eb4a8135e 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c @@ -1105,9 +1105,7 @@ static int gfs2_flock(struct file *file, int cmd, struct file_lock *fl) const struct file_operations gfs2_file_fops = { .llseek = gfs2_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = gfs2_file_write_iter, .unlocked_ioctl = gfs2_ioctl, .mmap = gfs2_mmap, @@ -1137,9 +1135,7 @@ const struct file_operations gfs2_dir_fops = { const struct file_operations gfs2_file_fops_nolock = { .llseek = gfs2_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = gfs2_file_write_iter, .unlocked_ioctl = gfs2_ioctl, .mmap = gfs2_mmap, diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c index 98d4ea45bb70..9337065bcc67 100644 --- a/fs/hfs/inode.c +++ b/fs/hfs/inode.c @@ -674,9 +674,7 @@ static int hfs_file_fsync(struct file *filp, loff_t start, loff_t end, static const struct file_operations hfs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c index f541196d4ee9..5f86cadb0542 100644 --- a/fs/hfsplus/inode.c +++ b/fs/hfsplus/inode.c @@ -341,9 +341,7 @@ static const struct inode_operations hfsplus_file_inode_operations = { static const struct file_operations hfsplus_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .splice_read = generic_file_splice_read, diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h index 4fcd40d6f308..91e19f9dffe5 100644 --- a/fs/hostfs/hostfs.h +++ b/fs/hostfs/hostfs.h @@ -66,7 +66,8 @@ extern int stat_file(const char *path, struct hostfs_stat *p, int fd); extern int access_file(char *path, int r, int w, int x); extern int open_file(char *path, int r, int w, int append); extern void *open_dir(char *path, int *err_out); -extern char *read_dir(void *stream, unsigned long long *pos, +extern void seek_dir(void *stream, unsigned long long pos); +extern char *read_dir(void *stream, unsigned long long *pos_out, unsigned long long *ino_out, int *len_out, unsigned int *type_out); extern void close_file(void *stream); @@ -77,8 +78,7 @@ extern int write_file(int fd, unsigned long long *offset, const char *buf, int len); extern int lseek_file(int fd, long long offset, int whence); extern int fsync_file(int fd, int datasync); -extern int file_create(char *name, int ur, int uw, int ux, int gr, - int gw, int gx, int or, int ow, int ox); +extern int file_create(char *name, int mode); extern int set_attr(const char *file, struct hostfs_iattr *attrs, int fd); extern int make_symlink(const char *from, const char *to); extern int unlink_file(const char *file); diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index fd62cae0fdcb..b83a0343378b 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c @@ -24,6 +24,7 @@ struct hostfs_inode_info { int fd; fmode_t mode; struct inode vfs_inode; + struct mutex open_mutex; }; static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) @@ -92,16 +93,22 @@ static char *__dentry_name(struct dentry *dentry, char *name) __putname(name); return NULL; } + + /* + * This function relies on the fact that dentry_path_raw() will place + * the path name at the end of the provided buffer. + */ + BUG_ON(p + strlen(p) + 1 != name + PATH_MAX); + strlcpy(name, root, PATH_MAX); if (len > p - name) { __putname(name); return NULL; } - if (p > name + len) { - char *s = name + len; - while ((*s++ = *p++) != '\0') - ; - } + + if (p > name + len) + strcpy(name + len, p); + return name; } @@ -135,21 +142,19 @@ static char *follow_link(char *link) int len, n; char *name, *resolved, *end; - len = 64; - while (1) { + name = __getname(); + if (!name) { n = -ENOMEM; - name = kmalloc(len, GFP_KERNEL); - if (name == NULL) - goto out; - - n = hostfs_do_readlink(link, name, len); - if (n < len) - break; - len *= 2; - kfree(name); + goto out_free; } + + n = hostfs_do_readlink(link, name, PATH_MAX); if (n < 0) goto out_free; + else if (n == PATH_MAX) { + n = -E2BIG; + goto out_free; + } if (*name == '/') return name; @@ -168,13 +173,12 @@ static char *follow_link(char *link) } sprintf(resolved, "%s%s", link, name); - kfree(name); + __putname(name); kfree(link); return resolved; out_free: - kfree(name); - out: + __putname(name); return ERR_PTR(n); } @@ -225,6 +229,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb) hi->fd = -1; hi->mode = 0; inode_init_once(&hi->vfs_inode); + mutex_init(&hi->open_mutex); return &hi->vfs_inode; } @@ -257,6 +262,9 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root) if (strlen(root_path) > offset) seq_printf(seq, ",%s", root_path + offset); + if (append) + seq_puts(seq, ",append"); + return 0; } @@ -284,6 +292,7 @@ static int hostfs_readdir(struct file *file, struct dir_context *ctx) if (dir == NULL) return -error; next = ctx->pos; + seek_dir(dir, next); while ((name = read_dir(dir, &next, &ino, &len, &type)) != NULL) { if (!dir_emit(ctx, name, len, ino, type)) break; @@ -293,13 +302,12 @@ static int hostfs_readdir(struct file *file, struct dir_context *ctx) return 0; } -static int hostfs_file_open(struct inode *ino, struct file *file) +static int hostfs_open(struct inode *ino, struct file *file) { - static DEFINE_MUTEX(open_mutex); char *name; - fmode_t mode = 0; + fmode_t mode; int err; - int r = 0, w = 0, fd; + int r, w, fd; mode = file->f_mode & (FMODE_READ | FMODE_WRITE); if ((mode & HOSTFS_I(ino)->mode) == mode) @@ -308,12 +316,12 @@ static int hostfs_file_open(struct inode *ino, struct file *file) mode |= HOSTFS_I(ino)->mode; retry: + r = w = 0; + if (mode & FMODE_READ) r = 1; if (mode & FMODE_WRITE) - w = 1; - if (w) - r = 1; + r = w = 1; name = dentry_name(file->f_path.dentry); if (name == NULL) @@ -324,15 +332,16 @@ retry: if (fd < 0) return fd; - mutex_lock(&open_mutex); + mutex_lock(&HOSTFS_I(ino)->open_mutex); /* somebody else had handled it first? */ if ((mode & HOSTFS_I(ino)->mode) == mode) { - mutex_unlock(&open_mutex); + mutex_unlock(&HOSTFS_I(ino)->open_mutex); + close_file(&fd); return 0; } if ((mode | HOSTFS_I(ino)->mode) != mode) { mode |= HOSTFS_I(ino)->mode; - mutex_unlock(&open_mutex); + mutex_unlock(&HOSTFS_I(ino)->open_mutex); close_file(&fd); goto retry; } @@ -342,12 +351,12 @@ retry: err = replace_file(fd, HOSTFS_I(ino)->fd); close_file(&fd); if (err < 0) { - mutex_unlock(&open_mutex); + mutex_unlock(&HOSTFS_I(ino)->open_mutex); return err; } } HOSTFS_I(ino)->mode = mode; - mutex_unlock(&open_mutex); + mutex_unlock(&HOSTFS_I(ino)->open_mutex); return 0; } @@ -378,13 +387,11 @@ static int hostfs_fsync(struct file *file, loff_t start, loff_t end, static const struct file_operations hostfs_file_fops = { .llseek = generic_file_llseek, - .read = new_sync_read, .splice_read = generic_file_splice_read, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, - .write = new_sync_write, .mmap = generic_file_mmap, - .open = hostfs_file_open, + .open = hostfs_open, .release = hostfs_file_release, .fsync = hostfs_fsync, }; @@ -393,6 +400,8 @@ static const struct file_operations hostfs_dir_fops = { .llseek = generic_file_llseek, .iterate = hostfs_readdir, .read = generic_read_dir, + .open = hostfs_open, + .fsync = hostfs_fsync, }; static int hostfs_writepage(struct page *page, struct writeback_control *wbc) @@ -400,7 +409,7 @@ static int hostfs_writepage(struct page *page, struct writeback_control *wbc) struct address_space *mapping = page->mapping; struct inode *inode = mapping->host; char *buffer; - unsigned long long base; + loff_t base = page_offset(page); int count = PAGE_CACHE_SIZE; int end_index = inode->i_size >> PAGE_CACHE_SHIFT; int err; @@ -409,7 +418,6 @@ static int hostfs_writepage(struct page *page, struct writeback_control *wbc) count = inode->i_size & (PAGE_CACHE_SIZE-1); buffer = kmap(page); - base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); if (err != count) { @@ -434,26 +442,29 @@ static int hostfs_writepage(struct page *page, struct writeback_control *wbc) static int hostfs_readpage(struct file *file, struct page *page) { char *buffer; - long long start; - int err = 0; + loff_t start = page_offset(page); + int bytes_read, ret = 0; - start = (long long) page->index << PAGE_CACHE_SHIFT; buffer = kmap(page); - err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, + bytes_read = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, PAGE_CACHE_SIZE); - if (err < 0) + if (bytes_read < 0) { + ClearPageUptodate(page); + SetPageError(page); + ret = bytes_read; goto out; + } - memset(&buffer[err], 0, PAGE_CACHE_SIZE - err); + memset(buffer + bytes_read, 0, PAGE_CACHE_SIZE - bytes_read); - flush_dcache_page(page); + ClearPageError(page); SetPageUptodate(page); - if (PageError(page)) ClearPageError(page); - err = 0; + out: + flush_dcache_page(page); kunmap(page); unlock_page(page); - return err; + return ret; } static int hostfs_write_begin(struct file *file, struct address_space *mapping, @@ -530,11 +541,13 @@ static int read_name(struct inode *ino, char *name) init_special_inode(ino, st.mode & S_IFMT, rdev); ino->i_op = &hostfs_iops; break; - - default: + case S_IFREG: ino->i_op = &hostfs_iops; ino->i_fop = &hostfs_file_fops; ino->i_mapping->a_ops = &hostfs_aops; + break; + default: + return -EIO; } ino->i_ino = st.ino; @@ -568,10 +581,7 @@ static int hostfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, if (name == NULL) goto out_put; - fd = file_create(name, - mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, - mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, - mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH); + fd = file_create(name, mode & S_IFMT); if (fd < 0) error = fd; else diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c index 9765dab95cbd..9c1e0f019880 100644 --- a/fs/hostfs/hostfs_user.c +++ b/fs/hostfs/hostfs_user.c @@ -97,21 +97,27 @@ void *open_dir(char *path, int *err_out) return dir; } -char *read_dir(void *stream, unsigned long long *pos, +void seek_dir(void *stream, unsigned long long pos) +{ + DIR *dir = stream; + + seekdir(dir, pos); +} + +char *read_dir(void *stream, unsigned long long *pos_out, unsigned long long *ino_out, int *len_out, unsigned int *type_out) { DIR *dir = stream; struct dirent *ent; - seekdir(dir, *pos); ent = readdir(dir); if (ent == NULL) return NULL; *len_out = strlen(ent->d_name); *ino_out = ent->d_ino; *type_out = ent->d_type; - *pos = telldir(dir); + *pos_out = ent->d_off; return ent->d_name; } @@ -175,21 +181,10 @@ void close_dir(void *stream) closedir(stream); } -int file_create(char *name, int ur, int uw, int ux, int gr, - int gw, int gx, int or, int ow, int ox) +int file_create(char *name, int mode) { - int mode, fd; - - mode = 0; - mode |= ur ? S_IRUSR : 0; - mode |= uw ? S_IWUSR : 0; - mode |= ux ? S_IXUSR : 0; - mode |= gr ? S_IRGRP : 0; - mode |= gw ? S_IWGRP : 0; - mode |= gx ? S_IXGRP : 0; - mode |= or ? S_IROTH : 0; - mode |= ow ? S_IWOTH : 0; - mode |= ox ? S_IXOTH : 0; + int fd; + fd = open64(name, O_CREAT | O_RDWR, mode); if (fd < 0) return -errno; diff --git a/fs/hpfs/file.c b/fs/hpfs/file.c index 7f54e5f76cec..6d8cfe9b52d6 100644 --- a/fs/hpfs/file.c +++ b/fs/hpfs/file.c @@ -197,9 +197,7 @@ const struct address_space_operations hpfs_aops = { const struct file_operations hpfs_file_ops = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .release = hpfs_file_release, diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 3a8f12762821..2640d88b0e63 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -34,6 +34,7 @@ #include <linux/security.h> #include <linux/magic.h> #include <linux/migrate.h> +#include <linux/uio.h> #include <asm/uaccess.h> @@ -181,42 +182,33 @@ hugetlb_get_unmapped_area(struct file *file, unsigned long addr, } #endif -static int +static size_t hugetlbfs_read_actor(struct page *page, unsigned long offset, - char __user *buf, unsigned long count, - unsigned long size) + struct iov_iter *to, unsigned long size) { - char *kaddr; - unsigned long left, copied = 0; + size_t copied = 0; int i, chunksize; - if (size > count) - size = count; - /* Find which 4k chunk and offset with in that chunk */ i = offset >> PAGE_CACHE_SHIFT; offset = offset & ~PAGE_CACHE_MASK; while (size) { + size_t n; chunksize = PAGE_CACHE_SIZE; if (offset) chunksize -= offset; if (chunksize > size) chunksize = size; - kaddr = kmap(&page[i]); - left = __copy_to_user(buf, kaddr + offset, chunksize); - kunmap(&page[i]); - if (left) { - copied += (chunksize - left); - break; - } + n = copy_page_to_iter(&page[i], offset, chunksize, to); + copied += n; + if (n != chunksize) + return copied; offset = 0; size -= chunksize; - buf += chunksize; - copied += chunksize; i++; } - return copied ? copied : -EFAULT; + return copied; } /* @@ -224,39 +216,34 @@ hugetlbfs_read_actor(struct page *page, unsigned long offset, * data. Its *very* similar to do_generic_mapping_read(), we can't use that * since it has PAGE_CACHE_SIZE assumptions. */ -static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, - size_t len, loff_t *ppos) +static ssize_t hugetlbfs_read_iter(struct kiocb *iocb, struct iov_iter *to) { - struct hstate *h = hstate_file(filp); - struct address_space *mapping = filp->f_mapping; + struct file *file = iocb->ki_filp; + struct hstate *h = hstate_file(file); + struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; - unsigned long index = *ppos >> huge_page_shift(h); - unsigned long offset = *ppos & ~huge_page_mask(h); + unsigned long index = iocb->ki_pos >> huge_page_shift(h); + unsigned long offset = iocb->ki_pos & ~huge_page_mask(h); unsigned long end_index; loff_t isize; ssize_t retval = 0; - /* validate length */ - if (len == 0) - goto out; - - for (;;) { + while (iov_iter_count(to)) { struct page *page; - unsigned long nr, ret; - int ra; + size_t nr, copied; /* nr is the maximum number of bytes to copy from this page */ nr = huge_page_size(h); isize = i_size_read(inode); if (!isize) - goto out; + break; end_index = (isize - 1) >> huge_page_shift(h); - if (index >= end_index) { - if (index > end_index) - goto out; + if (index > end_index) + break; + if (index == end_index) { nr = ((isize - 1) & ~huge_page_mask(h)) + 1; if (nr <= offset) - goto out; + break; } nr = nr - offset; @@ -267,39 +254,27 @@ static ssize_t hugetlbfs_read(struct file *filp, char __user *buf, * We have a HOLE, zero out the user-buffer for the * length of the hole or request. */ - ret = len < nr ? len : nr; - if (clear_user(buf, ret)) - ra = -EFAULT; - else - ra = 0; + copied = iov_iter_zero(nr, to); } else { unlock_page(page); /* * We have the page, copy it to user space buffer. */ - ra = hugetlbfs_read_actor(page, offset, buf, len, nr); - ret = ra; + copied = hugetlbfs_read_actor(page, offset, to, nr); page_cache_release(page); } - if (ra < 0) { - if (retval == 0) - retval = ra; - goto out; + offset += copied; + retval += copied; + if (copied != nr && iov_iter_count(to)) { + if (!retval) + retval = -EFAULT; + break; } - - offset += ret; - retval += ret; - len -= ret; index += offset >> huge_page_shift(h); offset &= ~huge_page_mask(h); - - /* short read or no more work */ - if ((ret != nr) || (len == 0)) - break; } -out: - *ppos = ((loff_t)index << huge_page_shift(h)) + offset; + iocb->ki_pos = ((loff_t)index << huge_page_shift(h)) + offset; return retval; } @@ -723,7 +698,7 @@ static void init_once(void *foo) } const struct file_operations hugetlbfs_file_operations = { - .read = hugetlbfs_read, + .read_iter = hugetlbfs_read_iter, .mmap = hugetlbfs_file_mmap, .fsync = noop_fsync, .get_unmapped_area = hugetlb_get_unmapped_area, diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index 64989ca9ba90..f509f62e12f6 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -51,9 +51,7 @@ const struct file_operations jffs2_file_operations = { .llseek = generic_file_llseek, .open = generic_file_open, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .unlocked_ioctl=jffs2_ioctl, .mmap = generic_file_readonly_mmap, diff --git a/fs/jfs/file.c b/fs/jfs/file.c index 10815f8dfd8b..ae46788b9723 100644 --- a/fs/jfs/file.c +++ b/fs/jfs/file.c @@ -151,8 +151,6 @@ const struct inode_operations jfs_file_inode_operations = { const struct file_operations jfs_file_operations = { .open = jfs_open, .llseek = generic_file_llseek, - .write = new_sync_write, - .read = new_sync_read, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, diff --git a/fs/locks.c b/fs/locks.c index 40bc384728c0..52b780fb5258 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -203,11 +203,11 @@ static struct kmem_cache *flctx_cache __read_mostly; static struct kmem_cache *filelock_cache __read_mostly; static struct file_lock_context * -locks_get_lock_context(struct inode *inode) +locks_get_lock_context(struct inode *inode, int type) { struct file_lock_context *new; - if (likely(inode->i_flctx)) + if (likely(inode->i_flctx) || type == F_UNLCK) goto out; new = kmem_cache_alloc(flctx_cache, GFP_KERNEL); @@ -223,14 +223,7 @@ locks_get_lock_context(struct inode *inode) * Assign the pointer if it's not already assigned. If it is, then * free the context we just allocated. */ - spin_lock(&inode->i_lock); - if (likely(!inode->i_flctx)) { - inode->i_flctx = new; - new = NULL; - } - spin_unlock(&inode->i_lock); - - if (new) + if (cmpxchg(&inode->i_flctx, NULL, new)) kmem_cache_free(flctx_cache, new); out: return inode->i_flctx; @@ -276,8 +269,10 @@ void locks_release_private(struct file_lock *fl) } if (fl->fl_lmops) { - if (fl->fl_lmops->lm_put_owner) - fl->fl_lmops->lm_put_owner(fl); + if (fl->fl_lmops->lm_put_owner) { + fl->fl_lmops->lm_put_owner(fl->fl_owner); + fl->fl_owner = NULL; + } fl->fl_lmops = NULL; } } @@ -333,7 +328,7 @@ void locks_copy_conflock(struct file_lock *new, struct file_lock *fl) if (fl->fl_lmops) { if (fl->fl_lmops->lm_get_owner) - fl->fl_lmops->lm_get_owner(new, fl); + fl->fl_lmops->lm_get_owner(fl->fl_owner); } } EXPORT_SYMBOL(locks_copy_conflock); @@ -592,11 +587,15 @@ posix_owner_key(struct file_lock *fl) static void locks_insert_global_blocked(struct file_lock *waiter) { + lockdep_assert_held(&blocked_lock_lock); + hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter)); } static void locks_delete_global_blocked(struct file_lock *waiter) { + lockdep_assert_held(&blocked_lock_lock); + hash_del(&waiter->fl_link); } @@ -730,7 +729,7 @@ static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *s /* POSIX locks owned by the same process do not conflict with * each other. */ - if (!IS_POSIX(sys_fl) || posix_same_owner(caller_fl, sys_fl)) + if (posix_same_owner(caller_fl, sys_fl)) return (0); /* Check whether they overlap */ @@ -748,7 +747,7 @@ static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *s /* FLOCK locks referring to the same filp do not conflict with * each other. */ - if (!IS_FLOCK(sys_fl) || (caller_fl->fl_file == sys_fl->fl_file)) + if (caller_fl->fl_file == sys_fl->fl_file) return (0); if ((caller_fl->fl_type & LOCK_MAND) || (sys_fl->fl_type & LOCK_MAND)) return 0; @@ -838,6 +837,8 @@ static int posix_locks_deadlock(struct file_lock *caller_fl, { int i = 0; + lockdep_assert_held(&blocked_lock_lock); + /* * This deadlock detector can't reasonably detect deadlocks with * FL_OFDLCK locks, since they aren't owned by a process, per-se. @@ -871,9 +872,12 @@ static int flock_lock_file(struct file *filp, struct file_lock *request) bool found = false; LIST_HEAD(dispose); - ctx = locks_get_lock_context(inode); - if (!ctx) - return -ENOMEM; + ctx = locks_get_lock_context(inode, request->fl_type); + if (!ctx) { + if (request->fl_type != F_UNLCK) + return -ENOMEM; + return (request->fl_flags & FL_EXISTS) ? -ENOENT : 0; + } if (!(request->fl_flags & FL_ACCESS) && (request->fl_type != F_UNLCK)) { new_fl = locks_alloc_lock(); @@ -939,9 +943,9 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str bool added = false; LIST_HEAD(dispose); - ctx = locks_get_lock_context(inode); + ctx = locks_get_lock_context(inode, request->fl_type); if (!ctx) - return -ENOMEM; + return (request->fl_type == F_UNLCK) ? 0 : -ENOMEM; /* * We may need two file_lock structures for this operation, @@ -964,8 +968,6 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str */ if (request->fl_type != F_UNLCK) { list_for_each_entry(fl, &ctx->flc_posix, fl_list) { - if (!IS_POSIX(fl)) - continue; if (!posix_locks_conflict(request, fl)) continue; if (conflock) @@ -1605,7 +1607,8 @@ generic_add_lease(struct file *filp, long arg, struct file_lock **flp, void **pr lease = *flp; trace_generic_add_lease(inode, lease); - ctx = locks_get_lock_context(inode); + /* Note that arg is never F_UNLCK here */ + ctx = locks_get_lock_context(inode, arg); if (!ctx) return -ENOMEM; @@ -2555,15 +2558,10 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl, : (fl->fl_type == F_WRLCK) ? "WRITE" : "READ "); } if (inode) { -#ifdef WE_CAN_BREAK_LSLK_NOW - seq_printf(f, "%d %s:%ld ", fl_pid, - inode->i_sb->s_id, inode->i_ino); -#else - /* userspace relies on this representation of dev_t ;-( */ + /* userspace relies on this representation of dev_t */ seq_printf(f, "%d %02x:%02x:%ld ", fl_pid, MAJOR(inode->i_sb->s_dev), MINOR(inode->i_sb->s_dev), inode->i_ino); -#endif } else { seq_printf(f, "%d <none>:0 ", fl_pid); } diff --git a/fs/logfs/file.c b/fs/logfs/file.c index 8538752df2f6..b2c13f739ffa 100644 --- a/fs/logfs/file.c +++ b/fs/logfs/file.c @@ -271,8 +271,6 @@ const struct file_operations logfs_reg_fops = { .llseek = generic_file_llseek, .mmap = generic_file_readonly_mmap, .open = generic_file_open, - .read = new_sync_read, - .write = new_sync_write, }; const struct address_space_operations logfs_reg_aops = { diff --git a/fs/minix/file.c b/fs/minix/file.c index a967de085ac0..6d63e27ec961 100644 --- a/fs/minix/file.c +++ b/fs/minix/file.c @@ -14,9 +14,7 @@ */ const struct file_operations minix_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, diff --git a/fs/ncpfs/file.c b/fs/ncpfs/file.c index 1dd7007f974d..479bf8db264e 100644 --- a/fs/ncpfs/file.c +++ b/fs/ncpfs/file.c @@ -98,30 +98,24 @@ out: } static ssize_t -ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +ncp_file_read_iter(struct kiocb *iocb, struct iov_iter *to) { + struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_read = 0; - off_t pos; + off_t pos = iocb->ki_pos; size_t bufsize; int error; - void* freepage; + void *freepage; size_t freelen; ncp_dbg(1, "enter %pD2\n", file); - pos = *ppos; - - if ((ssize_t) count < 0) { - return -EINVAL; - } - if (!count) + if (!iov_iter_count(to)) return 0; if (pos > inode->i_sb->s_maxbytes) return 0; - if (pos + count > inode->i_sb->s_maxbytes) { - count = inode->i_sb->s_maxbytes - pos; - } + iov_iter_truncate(to, inode->i_sb->s_maxbytes - pos); error = ncp_make_open(inode, O_RDONLY); if (error) { @@ -138,31 +132,29 @@ ncp_file_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) goto outrel; error = 0; /* First read in as much as possible for each bufsize. */ - while (already_read < count) { + while (iov_iter_count(to)) { int read_this_time; - size_t to_read = min_t(unsigned int, + size_t to_read = min_t(size_t, bufsize - (pos % bufsize), - count - already_read); + iov_iter_count(to)); error = ncp_read_bounce(NCP_SERVER(inode), NCP_FINFO(inode)->file_handle, - pos, to_read, buf, &read_this_time, + pos, to_read, to, &read_this_time, freepage, freelen); if (error) { error = -EIO; /* NW errno -> Linux errno */ break; } pos += read_this_time; - buf += read_this_time; already_read += read_this_time; - if (read_this_time != to_read) { + if (read_this_time != to_read) break; - } } vfree(freepage); - *ppos = pos; + iocb->ki_pos = pos; file_accessed(file); @@ -173,42 +165,26 @@ outrel: } static ssize_t -ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +ncp_file_write_iter(struct kiocb *iocb, struct iov_iter *from) { + struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); size_t already_written = 0; - off_t pos; + loff_t pos = iocb->ki_pos; + size_t count = iov_iter_count(from); size_t bufsize; int errno; - void* bouncebuffer; + void *bouncebuffer; ncp_dbg(1, "enter %pD2\n", file); - if ((ssize_t) count < 0) - return -EINVAL; - pos = *ppos; - if (file->f_flags & O_APPEND) { - pos = i_size_read(inode); - } - - if (pos + count > MAX_NON_LFS && !(file->f_flags&O_LARGEFILE)) { - if (pos >= MAX_NON_LFS) { - return -EFBIG; - } - if (count > MAX_NON_LFS - (u32)pos) { - count = MAX_NON_LFS - (u32)pos; - } - } - if (pos >= inode->i_sb->s_maxbytes) { - if (count || pos > inode->i_sb->s_maxbytes) { - return -EFBIG; - } - } - if (pos + count > inode->i_sb->s_maxbytes) { - count = inode->i_sb->s_maxbytes - pos; - } + errno = generic_write_checks(file, &pos, &count, 0); + if (errno) + return errno; + iov_iter_truncate(from, count); if (!count) return 0; + errno = ncp_make_open(inode, O_WRONLY); if (errno) { ncp_dbg(1, "open failed, error=%d\n", errno); @@ -216,8 +192,6 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * } bufsize = NCP_SERVER(inode)->buffer_size; - already_written = 0; - errno = file_update_time(file); if (errno) goto outrel; @@ -227,13 +201,13 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * errno = -EIO; /* -ENOMEM */ goto outrel; } - while (already_written < count) { + while (iov_iter_count(from)) { int written_this_time; - size_t to_write = min_t(unsigned int, - bufsize - (pos % bufsize), - count - already_written); + size_t to_write = min_t(size_t, + bufsize - ((off_t)pos % bufsize), + iov_iter_count(from)); - if (copy_from_user(bouncebuffer, buf, to_write)) { + if (copy_from_iter(bouncebuffer, to_write, from) != to_write) { errno = -EFAULT; break; } @@ -244,16 +218,14 @@ ncp_file_write(struct file *file, const char __user *buf, size_t count, loff_t * break; } pos += written_this_time; - buf += written_this_time; already_written += written_this_time; - if (written_this_time != to_write) { + if (written_this_time != to_write) break; - } } vfree(bouncebuffer); - *ppos = pos; + iocb->ki_pos = pos; if (pos > i_size_read(inode)) { mutex_lock(&inode->i_mutex); @@ -277,8 +249,8 @@ static int ncp_release(struct inode *inode, struct file *file) { const struct file_operations ncp_file_operations = { .llseek = generic_file_llseek, - .read = ncp_file_read, - .write = ncp_file_write, + .read_iter = ncp_file_read_iter, + .write_iter = ncp_file_write_iter, .unlocked_ioctl = ncp_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ncp_compat_ioctl, diff --git a/fs/ncpfs/ncplib_kernel.c b/fs/ncpfs/ncplib_kernel.c index 482387532f54..2b502a0d7941 100644 --- a/fs/ncpfs/ncplib_kernel.c +++ b/fs/ncpfs/ncplib_kernel.c @@ -1001,8 +1001,8 @@ out: */ int ncp_read_bounce(struct ncp_server *server, const char *file_id, - __u32 offset, __u16 to_read, char __user *target, int *bytes_read, - void* bounce, __u32 bufsize) + __u32 offset, __u16 to_read, struct iov_iter *to, + int *bytes_read, void *bounce, __u32 bufsize) { int result; @@ -1025,7 +1025,7 @@ ncp_read_bounce(struct ncp_server *server, const char *file_id, (offset & 1); *bytes_read = len; result = 0; - if (copy_to_user(target, source, len)) + if (copy_to_iter(source, len, to) != len) result = -EFAULT; } } diff --git a/fs/ncpfs/ncplib_kernel.h b/fs/ncpfs/ncplib_kernel.h index 250e443a07f3..5233fbc1747a 100644 --- a/fs/ncpfs/ncplib_kernel.h +++ b/fs/ncpfs/ncplib_kernel.h @@ -53,7 +53,7 @@ static inline int ncp_read_bounce_size(__u32 size) { return sizeof(struct ncp_reply_header) + 2 + 2 + size + 8; }; int ncp_read_bounce(struct ncp_server *, const char *, __u32, __u16, - char __user *, int *, void* bounce, __u32 bouncelen); + struct iov_iter *, int *, void *bounce, __u32 bouncelen); int ncp_read_kernel(struct ncp_server *, const char *, __u32, __u16, char *, int *); int ncp_write_kernel(struct ncp_server *, const char *, __u32, __u16, diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 37b15582e0de..f6a3adedf027 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -926,8 +926,6 @@ EXPORT_SYMBOL_GPL(nfs_flock); const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = nfs_file_read, .write_iter = nfs_file_write, .mmap = nfs_file_mmap, diff --git a/fs/nfs/nfs4file.c b/fs/nfs/nfs4file.c index 8b46389c4c5b..0181cde1d102 100644 --- a/fs/nfs/nfs4file.c +++ b/fs/nfs/nfs4file.c @@ -170,8 +170,6 @@ const struct file_operations nfs4_file_operations = { #else .llseek = nfs_file_llseek, #endif - .read = new_sync_read, - .write = new_sync_write, .read_iter = nfs_file_read, .write_iter = nfs_file_write, .mmap = nfs_file_mmap, diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8ba1d888f1e6..326a545ea7b2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -4932,20 +4932,22 @@ nfs4_transform_lock_offset(struct file_lock *lock) lock->fl_end = OFFSET_MAX; } -static void nfsd4_fl_get_owner(struct file_lock *dst, struct file_lock *src) +static fl_owner_t +nfsd4_fl_get_owner(fl_owner_t owner) { - struct nfs4_lockowner *lo = (struct nfs4_lockowner *)src->fl_owner; - dst->fl_owner = (fl_owner_t)lockowner(nfs4_get_stateowner(&lo->lo_owner)); + struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner; + + nfs4_get_stateowner(&lo->lo_owner); + return owner; } -static void nfsd4_fl_put_owner(struct file_lock *fl) +static void +nfsd4_fl_put_owner(fl_owner_t owner) { - struct nfs4_lockowner *lo = (struct nfs4_lockowner *)fl->fl_owner; + struct nfs4_lockowner *lo = (struct nfs4_lockowner *)owner; - if (lo) { + if (lo) nfs4_put_stateowner(&lo->lo_owner); - fl->fl_owner = NULL; - } } static const struct lock_manager_operations nfsd_posix_mng_ops = { diff --git a/fs/nilfs2/file.c b/fs/nilfs2/file.c index a8c728acb7a8..54575e3cc1a2 100644 --- a/fs/nilfs2/file.c +++ b/fs/nilfs2/file.c @@ -143,8 +143,6 @@ static int nilfs_file_mmap(struct file *file, struct vm_area_struct *vma) */ const struct file_operations nilfs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .unlocked_ioctl = nilfs_ioctl, diff --git a/fs/ntfs/file.c b/fs/ntfs/file.c index c1da78dad1af..840e95e3f1d2 100644 --- a/fs/ntfs/file.c +++ b/fs/ntfs/file.c @@ -2048,10 +2048,8 @@ static int ntfs_file_fsync(struct file *filp, loff_t start, loff_t end, const struct file_operations ntfs_file_ops = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, #ifdef NTFS_RW - .write = new_sync_write, .write_iter = ntfs_file_write_iter, .fsync = ntfs_file_fsync, #endif /* NTFS_RW */ diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index 91f03ce98108..8c48e989beba 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -2681,8 +2681,6 @@ const struct inode_operations ocfs2_special_file_iops = { */ const struct file_operations ocfs2_fops = { .llseek = ocfs2_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .mmap = ocfs2_mmap, .fsync = ocfs2_sync_file, .release = ocfs2_file_release, @@ -2729,8 +2727,6 @@ const struct file_operations ocfs2_dops = { */ const struct file_operations ocfs2_fops_no_plocks = { .llseek = ocfs2_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .mmap = ocfs2_mmap, .fsync = ocfs2_sync_file, .release = ocfs2_file_release, diff --git a/fs/omfs/file.c b/fs/omfs/file.c index 902e88527fce..f993be7f2156 100644 --- a/fs/omfs/file.c +++ b/fs/omfs/file.c @@ -337,8 +337,6 @@ static sector_t omfs_bmap(struct address_space *mapping, sector_t block) const struct file_operations omfs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, diff --git a/fs/open.c b/fs/open.c index 6a83c47d5904..6796f04d6032 100644 --- a/fs/open.c +++ b/fs/open.c @@ -734,10 +734,10 @@ static int do_dentry_open(struct file *f, if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) i_readcount_inc(inode); if ((f->f_mode & FMODE_READ) && - likely(f->f_op->read || f->f_op->aio_read || f->f_op->read_iter)) + likely(f->f_op->read || f->f_op->read_iter)) f->f_mode |= FMODE_CAN_READ; if ((f->f_mode & FMODE_WRITE) && - likely(f->f_op->write || f->f_op->aio_write || f->f_op->write_iter)) + likely(f->f_op->write || f->f_op->write_iter)) f->f_mode |= FMODE_CAN_WRITE; f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); diff --git a/fs/pipe.c b/fs/pipe.c index 2d084f2d0b83..822da5b7cff0 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -946,9 +946,7 @@ err: const struct file_operations pipefifo_fops = { .open = fifo_open, .llseek = no_llseek, - .read = new_sync_read, .read_iter = pipe_read, - .write = new_sync_write, .write_iter = pipe_write, .poll = pipe_poll, .unlocked_ioctl = pipe_ioctl, diff --git a/fs/ramfs/file-mmu.c b/fs/ramfs/file-mmu.c index 4f56de822d2f..183a212694bf 100644 --- a/fs/ramfs/file-mmu.c +++ b/fs/ramfs/file-mmu.c @@ -31,9 +31,7 @@ #include "internal.h" const struct file_operations ramfs_file_operations = { - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = noop_fsync, diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index f6ab41b39612..0b38befa69f3 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -44,9 +44,7 @@ const struct file_operations ramfs_file_operations = { .mmap_capabilities = ramfs_mmap_capabilities, .mmap = ramfs_nommu_mmap, .get_unmapped_area = ramfs_nommu_get_unmapped_area, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .fsync = noop_fsync, .splice_read = generic_file_splice_read, diff --git a/fs/read_write.c b/fs/read_write.c index 69128b378646..45d583c33879 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -22,13 +22,10 @@ #include <asm/unistd.h> typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *); -typedef ssize_t (*iov_fn_t)(struct kiocb *, const struct iovec *, - unsigned long, loff_t); typedef ssize_t (*iter_fn_t)(struct kiocb *, struct iov_iter *); const struct file_operations generic_ro_fops = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, .mmap = generic_file_readonly_mmap, .splice_read = generic_file_splice_read, @@ -411,24 +408,7 @@ int rw_verify_area(int read_write, struct file *file, const loff_t *ppos, size_t return count > MAX_RW_COUNT ? MAX_RW_COUNT : count; } -ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) -{ - struct iovec iov = { .iov_base = buf, .iov_len = len }; - struct kiocb kiocb; - ssize_t ret; - - init_sync_kiocb(&kiocb, filp); - kiocb.ki_pos = *ppos; - - ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); - BUG_ON(ret == -EIOCBQUEUED); - *ppos = kiocb.ki_pos; - return ret; -} - -EXPORT_SYMBOL(do_sync_read); - -ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) +static ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = buf, .iov_len = len }; struct kiocb kiocb; @@ -445,24 +425,17 @@ ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *p return ret; } -EXPORT_SYMBOL(new_sync_read); - ssize_t __vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { - ssize_t ret; - if (file->f_op->read) - ret = file->f_op->read(file, buf, count, pos); - else if (file->f_op->aio_read) - ret = do_sync_read(file, buf, count, pos); + return file->f_op->read(file, buf, count, pos); else if (file->f_op->read_iter) - ret = new_sync_read(file, buf, count, pos); + return new_sync_read(file, buf, count, pos); else - ret = -EINVAL; - - return ret; + return -EINVAL; } +EXPORT_SYMBOL(__vfs_read); ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) { @@ -491,24 +464,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) EXPORT_SYMBOL(vfs_read); -ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) -{ - struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; - struct kiocb kiocb; - ssize_t ret; - - init_sync_kiocb(&kiocb, filp); - kiocb.ki_pos = *ppos; - - ret = filp->f_op->aio_write(&kiocb, &iov, 1, kiocb.ki_pos); - BUG_ON(ret == -EIOCBQUEUED); - *ppos = kiocb.ki_pos; - return ret; -} - -EXPORT_SYMBOL(do_sync_write); - -ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) +static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) { struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len }; struct kiocb kiocb; @@ -525,7 +481,17 @@ ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, lo return ret; } -EXPORT_SYMBOL(new_sync_write); +ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, + loff_t *pos) +{ + if (file->f_op->write) + return file->f_op->write(file, p, count, pos); + else if (file->f_op->write_iter) + return new_sync_write(file, p, count, pos); + else + return -EINVAL; +} +EXPORT_SYMBOL(__vfs_write); ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos) { @@ -541,12 +507,7 @@ ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t p = (__force const char __user *)buf; if (count > MAX_RW_COUNT) count = MAX_RW_COUNT; - if (file->f_op->write) - ret = file->f_op->write(file, p, count, pos); - else if (file->f_op->aio_write) - ret = do_sync_write(file, p, count, pos); - else - ret = new_sync_write(file, p, count, pos); + ret = __vfs_write(file, p, count, pos); set_fs(old_fs); if (ret > 0) { fsnotify_modify(file); @@ -573,12 +534,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ if (ret >= 0) { count = ret; file_start_write(file); - if (file->f_op->write) - ret = file->f_op->write(file, buf, count, pos); - else if (file->f_op->aio_write) - ret = do_sync_write(file, buf, count, pos); - else - ret = new_sync_write(file, buf, count, pos); + ret = __vfs_write(file, buf, count, pos); if (ret > 0) { fsnotify_modify(file); add_wchar(current, ret); @@ -710,21 +666,6 @@ static ssize_t do_iter_readv_writev(struct file *filp, struct iov_iter *iter, return ret; } -static ssize_t do_sync_readv_writev(struct file *filp, struct iov_iter *iter, - loff_t *ppos, iov_fn_t fn) -{ - struct kiocb kiocb; - ssize_t ret; - - init_sync_kiocb(&kiocb, filp); - kiocb.ki_pos = *ppos; - - ret = fn(&kiocb, iter->iov, iter->nr_segs, kiocb.ki_pos); - BUG_ON(ret == -EIOCBQUEUED); - *ppos = kiocb.ki_pos; - return ret; -} - /* Do it by hand, with file-ops */ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter, loff_t *ppos, io_fn_t fn) @@ -839,7 +780,6 @@ static ssize_t do_readv_writev(int type, struct file *file, struct iov_iter iter; ssize_t ret; io_fn_t fn; - iov_fn_t fnv; iter_fn_t iter_fn; ret = import_iovec(type, uvector, nr_segs, @@ -854,22 +794,17 @@ static ssize_t do_readv_writev(int type, struct file *file, if (ret < 0) goto out; - fnv = NULL; if (type == READ) { fn = file->f_op->read; - fnv = file->f_op->aio_read; iter_fn = file->f_op->read_iter; } else { fn = (io_fn_t)file->f_op->write; - fnv = file->f_op->aio_write; iter_fn = file->f_op->write_iter; file_start_write(file); } if (iter_fn) ret = do_iter_readv_writev(file, &iter, pos, iter_fn); - else if (fnv) - ret = do_sync_readv_writev(file, &iter, pos, fnv); else ret = do_loop_readv_writev(file, &iter, pos, fn); @@ -1019,7 +954,6 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, struct iov_iter iter; ssize_t ret; io_fn_t fn; - iov_fn_t fnv; iter_fn_t iter_fn; ret = compat_import_iovec(type, uvector, nr_segs, @@ -1034,22 +968,17 @@ static ssize_t compat_do_readv_writev(int type, struct file *file, if (ret < 0) goto out; - fnv = NULL; if (type == READ) { fn = file->f_op->read; - fnv = file->f_op->aio_read; iter_fn = file->f_op->read_iter; } else { fn = (io_fn_t)file->f_op->write; - fnv = file->f_op->aio_write; iter_fn = file->f_op->write_iter; file_start_write(file); } if (iter_fn) ret = do_iter_readv_writev(file, &iter, pos, iter_fn); - else if (fnv) - ret = do_sync_readv_writev(file, &iter, pos, fnv); else ret = do_loop_readv_writev(file, &iter, pos, fn); diff --git a/fs/reiserfs/file.c b/fs/reiserfs/file.c index 751dd3f4346b..96a1bcf33db4 100644 --- a/fs/reiserfs/file.c +++ b/fs/reiserfs/file.c @@ -243,8 +243,6 @@ drop_write_lock: } const struct file_operations reiserfs_file_operations = { - .read = new_sync_read, - .write = new_sync_write, .unlocked_ioctl = reiserfs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = reiserfs_compat_ioctl, diff --git a/fs/romfs/mmap-nommu.c b/fs/romfs/mmap-nommu.c index 7da9e2153953..1118a0dc6b45 100644 --- a/fs/romfs/mmap-nommu.c +++ b/fs/romfs/mmap-nommu.c @@ -81,7 +81,6 @@ static unsigned romfs_mmap_capabilities(struct file *file) const struct file_operations romfs_ro_fops = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, .splice_read = generic_file_splice_read, .mmap = romfs_mmap, diff --git a/fs/sysv/file.c b/fs/sysv/file.c index b00811c75b24..a48e30410ad1 100644 --- a/fs/sysv/file.c +++ b/fs/sysv/file.c @@ -21,9 +21,7 @@ */ const struct file_operations sysv_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .fsync = generic_file_fsync, diff --git a/fs/ubifs/budget.c b/fs/ubifs/budget.c index eb997e9c4ab0..11a11b32a2a9 100644 --- a/fs/ubifs/budget.c +++ b/fs/ubifs/budget.c @@ -509,7 +509,7 @@ again: c->bi.nospace_rp = 1; smp_wmb(); } else - ubifs_err("cannot budget space, error %d", err); + ubifs_err(c, "cannot budget space, error %d", err); return err; } diff --git a/fs/ubifs/commit.c b/fs/ubifs/commit.c index 26b69b2d4a45..63f56619991d 100644 --- a/fs/ubifs/commit.c +++ b/fs/ubifs/commit.c @@ -225,7 +225,7 @@ out_cancel: out_up: up_write(&c->commit_sem); out: - ubifs_err("commit failed, error %d", err); + ubifs_err(c, "commit failed, error %d", err); spin_lock(&c->cs_lock); c->cmt_state = COMMIT_BROKEN; wake_up(&c->cmt_wq); @@ -289,7 +289,7 @@ int ubifs_bg_thread(void *info) int err; struct ubifs_info *c = info; - ubifs_msg("background thread \"%s\" started, PID %d", + ubifs_msg(c, "background thread \"%s\" started, PID %d", c->bgt_name, current->pid); set_freezable(); @@ -324,7 +324,7 @@ int ubifs_bg_thread(void *info) cond_resched(); } - ubifs_msg("background thread \"%s\" stops", c->bgt_name); + ubifs_msg(c, "background thread \"%s\" stops", c->bgt_name); return 0; } @@ -712,13 +712,13 @@ out: return 0; out_dump: - ubifs_err("dumping index node (iip=%d)", i->iip); + ubifs_err(c, "dumping index node (iip=%d)", i->iip); ubifs_dump_node(c, idx); list_del(&i->list); kfree(i); if (!list_empty(&list)) { i = list_entry(list.prev, struct idx_node, list); - ubifs_err("dumping parent index node"); + ubifs_err(c, "dumping parent index node"); ubifs_dump_node(c, &i->idx); } out_free: @@ -727,7 +727,7 @@ out_free: list_del(&i->list); kfree(i); } - ubifs_err("failed, error %d", err); + ubifs_err(c, "failed, error %d", err); if (err > 0) err = -EINVAL; return err; diff --git a/fs/ubifs/compress.c b/fs/ubifs/compress.c index 2bfa0953335d..565cb56d7225 100644 --- a/fs/ubifs/compress.c +++ b/fs/ubifs/compress.c @@ -92,8 +92,8 @@ struct ubifs_compressor *ubifs_compressors[UBIFS_COMPR_TYPES_CNT]; * Note, if the input buffer was not compressed, it is copied to the output * buffer and %UBIFS_COMPR_NONE is returned in @compr_type. */ -void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, - int *compr_type) +void ubifs_compress(const struct ubifs_info *c, const void *in_buf, + int in_len, void *out_buf, int *out_len, int *compr_type) { int err; struct ubifs_compressor *compr = ubifs_compressors[*compr_type]; @@ -112,9 +112,9 @@ void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, if (compr->comp_mutex) mutex_unlock(compr->comp_mutex); if (unlikely(err)) { - ubifs_warn("cannot compress %d bytes, compressor %s, error %d, leave data uncompressed", + ubifs_warn(c, "cannot compress %d bytes, compressor %s, error %d, leave data uncompressed", in_len, compr->name, err); - goto no_compr; + goto no_compr; } /* @@ -144,21 +144,21 @@ no_compr: * The length of the uncompressed data is returned in @out_len. This functions * returns %0 on success or a negative error code on failure. */ -int ubifs_decompress(const void *in_buf, int in_len, void *out_buf, - int *out_len, int compr_type) +int ubifs_decompress(const struct ubifs_info *c, const void *in_buf, + int in_len, void *out_buf, int *out_len, int compr_type) { int err; struct ubifs_compressor *compr; if (unlikely(compr_type < 0 || compr_type >= UBIFS_COMPR_TYPES_CNT)) { - ubifs_err("invalid compression type %d", compr_type); + ubifs_err(c, "invalid compression type %d", compr_type); return -EINVAL; } compr = ubifs_compressors[compr_type]; if (unlikely(!compr->capi_name)) { - ubifs_err("%s compression is not compiled in", compr->name); + ubifs_err(c, "%s compression is not compiled in", compr->name); return -EINVAL; } @@ -175,7 +175,7 @@ int ubifs_decompress(const void *in_buf, int in_len, void *out_buf, if (compr->decomp_mutex) mutex_unlock(compr->decomp_mutex); if (err) - ubifs_err("cannot decompress %d bytes, compressor %s, error %d", + ubifs_err(c, "cannot decompress %d bytes, compressor %s, error %d", in_len, compr->name, err); return err; @@ -193,8 +193,8 @@ static int __init compr_init(struct ubifs_compressor *compr) if (compr->capi_name) { compr->cc = crypto_alloc_comp(compr->capi_name, 0, 0); if (IS_ERR(compr->cc)) { - ubifs_err("cannot initialize compressor %s, error %ld", - compr->name, PTR_ERR(compr->cc)); + pr_err("UBIFS error (pid %d): cannot initialize compressor %s, error %ld", + current->pid, compr->name, PTR_ERR(compr->cc)); return PTR_ERR(compr->cc); } } diff --git a/fs/ubifs/debug.c b/fs/ubifs/debug.c index 4cfb3e82c56f..4c46a9865fa7 100644 --- a/fs/ubifs/debug.c +++ b/fs/ubifs/debug.c @@ -746,7 +746,7 @@ void ubifs_dump_lprops(struct ubifs_info *c) for (lnum = c->main_first; lnum < c->leb_cnt; lnum++) { err = ubifs_read_one_lp(c, lnum, &lp); if (err) { - ubifs_err("cannot read lprops for LEB %d", lnum); + ubifs_err(c, "cannot read lprops for LEB %d", lnum); continue; } @@ -819,13 +819,13 @@ void ubifs_dump_leb(const struct ubifs_info *c, int lnum) buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); if (!buf) { - ubifs_err("cannot allocate memory for dumping LEB %d", lnum); + ubifs_err(c, "cannot allocate memory for dumping LEB %d", lnum); return; } sleb = ubifs_scan(c, lnum, 0, buf, 0); if (IS_ERR(sleb)) { - ubifs_err("scan error %d", (int)PTR_ERR(sleb)); + ubifs_err(c, "scan error %d", (int)PTR_ERR(sleb)); goto out; } @@ -1032,7 +1032,7 @@ int dbg_check_space_info(struct ubifs_info *c) spin_unlock(&c->space_lock); if (free != d->saved_free) { - ubifs_err("free space changed from %lld to %lld", + ubifs_err(c, "free space changed from %lld to %lld", d->saved_free, free); goto out; } @@ -1040,15 +1040,15 @@ int dbg_check_space_info(struct ubifs_info *c) return 0; out: - ubifs_msg("saved lprops statistics dump"); + ubifs_msg(c, "saved lprops statistics dump"); ubifs_dump_lstats(&d->saved_lst); - ubifs_msg("saved budgeting info dump"); + ubifs_msg(c, "saved budgeting info dump"); ubifs_dump_budg(c, &d->saved_bi); - ubifs_msg("saved idx_gc_cnt %d", d->saved_idx_gc_cnt); - ubifs_msg("current lprops statistics dump"); + ubifs_msg(c, "saved idx_gc_cnt %d", d->saved_idx_gc_cnt); + ubifs_msg(c, "current lprops statistics dump"); ubifs_get_lp_stats(c, &lst); ubifs_dump_lstats(&lst); - ubifs_msg("current budgeting info dump"); + ubifs_msg(c, "current budgeting info dump"); ubifs_dump_budg(c, &c->bi); dump_stack(); return -EINVAL; @@ -1077,9 +1077,9 @@ int dbg_check_synced_i_size(const struct ubifs_info *c, struct inode *inode) mutex_lock(&ui->ui_mutex); spin_lock(&ui->ui_lock); if (ui->ui_size != ui->synced_i_size && !ui->dirty) { - ubifs_err("ui_size is %lld, synced_i_size is %lld, but inode is clean", + ubifs_err(c, "ui_size is %lld, synced_i_size is %lld, but inode is clean", ui->ui_size, ui->synced_i_size); - ubifs_err("i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino, + ubifs_err(c, "i_ino %lu, i_mode %#x, i_size %lld", inode->i_ino, inode->i_mode, i_size_read(inode)); dump_stack(); err = -EINVAL; @@ -1140,7 +1140,7 @@ int dbg_check_dir(struct ubifs_info *c, const struct inode *dir) kfree(pdent); if (i_size_read(dir) != size) { - ubifs_err("directory inode %lu has size %llu, but calculated size is %llu", + ubifs_err(c, "directory inode %lu has size %llu, but calculated size is %llu", dir->i_ino, (unsigned long long)i_size_read(dir), (unsigned long long)size); ubifs_dump_inode(c, dir); @@ -1148,7 +1148,7 @@ int dbg_check_dir(struct ubifs_info *c, const struct inode *dir) return -EINVAL; } if (dir->i_nlink != nlink) { - ubifs_err("directory inode %lu has nlink %u, but calculated nlink is %u", + ubifs_err(c, "directory inode %lu has nlink %u, but calculated nlink is %u", dir->i_ino, dir->i_nlink, nlink); ubifs_dump_inode(c, dir); dump_stack(); @@ -1207,10 +1207,10 @@ static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1, err = 1; key_read(c, &dent1->key, &key); if (keys_cmp(c, &zbr1->key, &key)) { - ubifs_err("1st entry at %d:%d has key %s", zbr1->lnum, + ubifs_err(c, "1st entry at %d:%d has key %s", zbr1->lnum, zbr1->offs, dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); - ubifs_err("but it should have key %s according to tnc", + ubifs_err(c, "but it should have key %s according to tnc", dbg_snprintf_key(c, &zbr1->key, key_buf, DBG_KEY_BUF_LEN)); ubifs_dump_node(c, dent1); @@ -1219,10 +1219,10 @@ static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1, key_read(c, &dent2->key, &key); if (keys_cmp(c, &zbr2->key, &key)) { - ubifs_err("2nd entry at %d:%d has key %s", zbr1->lnum, + ubifs_err(c, "2nd entry at %d:%d has key %s", zbr1->lnum, zbr1->offs, dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); - ubifs_err("but it should have key %s according to tnc", + ubifs_err(c, "but it should have key %s according to tnc", dbg_snprintf_key(c, &zbr2->key, key_buf, DBG_KEY_BUF_LEN)); ubifs_dump_node(c, dent2); @@ -1238,14 +1238,14 @@ static int dbg_check_key_order(struct ubifs_info *c, struct ubifs_zbranch *zbr1, goto out_free; } if (cmp == 0 && nlen1 == nlen2) - ubifs_err("2 xent/dent nodes with the same name"); + ubifs_err(c, "2 xent/dent nodes with the same name"); else - ubifs_err("bad order of colliding key %s", + ubifs_err(c, "bad order of colliding key %s", dbg_snprintf_key(c, &key, key_buf, DBG_KEY_BUF_LEN)); - ubifs_msg("first node at %d:%d\n", zbr1->lnum, zbr1->offs); + ubifs_msg(c, "first node at %d:%d\n", zbr1->lnum, zbr1->offs); ubifs_dump_node(c, dent1); - ubifs_msg("second node at %d:%d\n", zbr2->lnum, zbr2->offs); + ubifs_msg(c, "second node at %d:%d\n", zbr2->lnum, zbr2->offs); ubifs_dump_node(c, dent2); out_free: @@ -1447,11 +1447,11 @@ static int dbg_check_znode(struct ubifs_info *c, struct ubifs_zbranch *zbr) return 0; out: - ubifs_err("failed, error %d", err); - ubifs_msg("dump of the znode"); + ubifs_err(c, "failed, error %d", err); + ubifs_msg(c, "dump of the znode"); ubifs_dump_znode(c, znode); if (zp) { - ubifs_msg("dump of the parent znode"); + ubifs_msg(c, "dump of the parent znode"); ubifs_dump_znode(c, zp); } dump_stack(); @@ -1518,9 +1518,9 @@ int dbg_check_tnc(struct ubifs_info *c, int extra) if (err < 0) return err; if (err) { - ubifs_msg("first znode"); + ubifs_msg(c, "first znode"); ubifs_dump_znode(c, prev); - ubifs_msg("second znode"); + ubifs_msg(c, "second znode"); ubifs_dump_znode(c, znode); return -EINVAL; } @@ -1529,13 +1529,13 @@ int dbg_check_tnc(struct ubifs_info *c, int extra) if (extra) { if (clean_cnt != atomic_long_read(&c->clean_zn_cnt)) { - ubifs_err("incorrect clean_zn_cnt %ld, calculated %ld", + ubifs_err(c, "incorrect clean_zn_cnt %ld, calculated %ld", atomic_long_read(&c->clean_zn_cnt), clean_cnt); return -EINVAL; } if (dirty_cnt != atomic_long_read(&c->dirty_zn_cnt)) { - ubifs_err("incorrect dirty_zn_cnt %ld, calculated %ld", + ubifs_err(c, "incorrect dirty_zn_cnt %ld, calculated %ld", atomic_long_read(&c->dirty_zn_cnt), dirty_cnt); return -EINVAL; @@ -1608,7 +1608,7 @@ int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, if (znode_cb) { err = znode_cb(c, znode, priv); if (err) { - ubifs_err("znode checking function returned error %d", + ubifs_err(c, "znode checking function returned error %d", err); ubifs_dump_znode(c, znode); goto out_dump; @@ -1619,7 +1619,7 @@ int dbg_walk_index(struct ubifs_info *c, dbg_leaf_callback leaf_cb, zbr = &znode->zbranch[idx]; err = leaf_cb(c, zbr, priv); if (err) { - ubifs_err("leaf checking function returned error %d, for leaf at LEB %d:%d", + ubifs_err(c, "leaf checking function returned error %d, for leaf at LEB %d:%d", err, zbr->lnum, zbr->offs); goto out_dump; } @@ -1675,7 +1675,7 @@ out_dump: zbr = &znode->parent->zbranch[znode->iip]; else zbr = &c->zroot; - ubifs_msg("dump of znode at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_msg(c, "dump of znode at LEB %d:%d", zbr->lnum, zbr->offs); ubifs_dump_znode(c, znode); out_unlock: mutex_unlock(&c->tnc_mutex); @@ -1722,12 +1722,12 @@ int dbg_check_idx_size(struct ubifs_info *c, long long idx_size) err = dbg_walk_index(c, NULL, add_size, &calc); if (err) { - ubifs_err("error %d while walking the index", err); + ubifs_err(c, "error %d while walking the index", err); return err; } if (calc != idx_size) { - ubifs_err("index size check failed: calculated size is %lld, should be %lld", + ubifs_err(c, "index size check failed: calculated size is %lld, should be %lld", calc, idx_size); dump_stack(); return -EINVAL; @@ -1814,7 +1814,7 @@ static struct fsck_inode *add_inode(struct ubifs_info *c, } if (inum > c->highest_inum) { - ubifs_err("too high inode number, max. is %lu", + ubifs_err(c, "too high inode number, max. is %lu", (unsigned long)c->highest_inum); return ERR_PTR(-EINVAL); } @@ -1921,17 +1921,17 @@ static struct fsck_inode *read_add_inode(struct ubifs_info *c, ino_key_init(c, &key, inum); err = ubifs_lookup_level0(c, &key, &znode, &n); if (!err) { - ubifs_err("inode %lu not found in index", (unsigned long)inum); + ubifs_err(c, "inode %lu not found in index", (unsigned long)inum); return ERR_PTR(-ENOENT); } else if (err < 0) { - ubifs_err("error %d while looking up inode %lu", + ubifs_err(c, "error %d while looking up inode %lu", err, (unsigned long)inum); return ERR_PTR(err); } zbr = &znode->zbranch[n]; if (zbr->len < UBIFS_INO_NODE_SZ) { - ubifs_err("bad node %lu node length %d", + ubifs_err(c, "bad node %lu node length %d", (unsigned long)inum, zbr->len); return ERR_PTR(-EINVAL); } @@ -1942,7 +1942,7 @@ static struct fsck_inode *read_add_inode(struct ubifs_info *c, err = ubifs_tnc_read_node(c, zbr, ino); if (err) { - ubifs_err("cannot read inode node at LEB %d:%d, error %d", + ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d", zbr->lnum, zbr->offs, err); kfree(ino); return ERR_PTR(err); @@ -1951,7 +1951,7 @@ static struct fsck_inode *read_add_inode(struct ubifs_info *c, fscki = add_inode(c, fsckd, ino); kfree(ino); if (IS_ERR(fscki)) { - ubifs_err("error %ld while adding inode %lu node", + ubifs_err(c, "error %ld while adding inode %lu node", PTR_ERR(fscki), (unsigned long)inum); return fscki; } @@ -1985,7 +1985,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, struct fsck_inode *fscki; if (zbr->len < UBIFS_CH_SZ) { - ubifs_err("bad leaf length %d (LEB %d:%d)", + ubifs_err(c, "bad leaf length %d (LEB %d:%d)", zbr->len, zbr->lnum, zbr->offs); return -EINVAL; } @@ -1996,7 +1996,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, err = ubifs_tnc_read_node(c, zbr, node); if (err) { - ubifs_err("cannot read leaf node at LEB %d:%d, error %d", + ubifs_err(c, "cannot read leaf node at LEB %d:%d, error %d", zbr->lnum, zbr->offs, err); goto out_free; } @@ -2006,7 +2006,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, fscki = add_inode(c, priv, node); if (IS_ERR(fscki)) { err = PTR_ERR(fscki); - ubifs_err("error %d while adding inode node", err); + ubifs_err(c, "error %d while adding inode node", err); goto out_dump; } goto out; @@ -2014,7 +2014,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, if (type != UBIFS_DENT_KEY && type != UBIFS_XENT_KEY && type != UBIFS_DATA_KEY) { - ubifs_err("unexpected node type %d at LEB %d:%d", + ubifs_err(c, "unexpected node type %d at LEB %d:%d", type, zbr->lnum, zbr->offs); err = -EINVAL; goto out_free; @@ -2022,7 +2022,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, ch = node; if (le64_to_cpu(ch->sqnum) > c->max_sqnum) { - ubifs_err("too high sequence number, max. is %llu", + ubifs_err(c, "too high sequence number, max. is %llu", c->max_sqnum); err = -EINVAL; goto out_dump; @@ -2042,7 +2042,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, fscki = read_add_inode(c, priv, inum); if (IS_ERR(fscki)) { err = PTR_ERR(fscki); - ubifs_err("error %d while processing data node and trying to find inode node %lu", + ubifs_err(c, "error %d while processing data node and trying to find inode node %lu", err, (unsigned long)inum); goto out_dump; } @@ -2052,7 +2052,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, blk_offs <<= UBIFS_BLOCK_SHIFT; blk_offs += le32_to_cpu(dn->size); if (blk_offs > fscki->size) { - ubifs_err("data node at LEB %d:%d is not within inode size %lld", + ubifs_err(c, "data node at LEB %d:%d is not within inode size %lld", zbr->lnum, zbr->offs, fscki->size); err = -EINVAL; goto out_dump; @@ -2076,7 +2076,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, fscki = read_add_inode(c, priv, inum); if (IS_ERR(fscki)) { err = PTR_ERR(fscki); - ubifs_err("error %d while processing entry node and trying to find inode node %lu", + ubifs_err(c, "error %d while processing entry node and trying to find inode node %lu", err, (unsigned long)inum); goto out_dump; } @@ -2088,7 +2088,7 @@ static int check_leaf(struct ubifs_info *c, struct ubifs_zbranch *zbr, fscki1 = read_add_inode(c, priv, inum); if (IS_ERR(fscki1)) { err = PTR_ERR(fscki1); - ubifs_err("error %d while processing entry node and trying to find parent inode node %lu", + ubifs_err(c, "error %d while processing entry node and trying to find parent inode node %lu", err, (unsigned long)inum); goto out_dump; } @@ -2111,7 +2111,7 @@ out: return 0; out_dump: - ubifs_msg("dump of node at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_msg(c, "dump of node at LEB %d:%d", zbr->lnum, zbr->offs); ubifs_dump_node(c, node); out_free: kfree(node); @@ -2162,52 +2162,52 @@ static int check_inodes(struct ubifs_info *c, struct fsck_data *fsckd) */ if (fscki->inum != UBIFS_ROOT_INO && fscki->references != 1) { - ubifs_err("directory inode %lu has %d direntries which refer it, but should be 1", + ubifs_err(c, "directory inode %lu has %d direntries which refer it, but should be 1", (unsigned long)fscki->inum, fscki->references); goto out_dump; } if (fscki->inum == UBIFS_ROOT_INO && fscki->references != 0) { - ubifs_err("root inode %lu has non-zero (%d) direntries which refer it", + ubifs_err(c, "root inode %lu has non-zero (%d) direntries which refer it", (unsigned long)fscki->inum, fscki->references); goto out_dump; } if (fscki->calc_sz != fscki->size) { - ubifs_err("directory inode %lu size is %lld, but calculated size is %lld", + ubifs_err(c, "directory inode %lu size is %lld, but calculated size is %lld", (unsigned long)fscki->inum, fscki->size, fscki->calc_sz); goto out_dump; } if (fscki->calc_cnt != fscki->nlink) { - ubifs_err("directory inode %lu nlink is %d, but calculated nlink is %d", + ubifs_err(c, "directory inode %lu nlink is %d, but calculated nlink is %d", (unsigned long)fscki->inum, fscki->nlink, fscki->calc_cnt); goto out_dump; } } else { if (fscki->references != fscki->nlink) { - ubifs_err("inode %lu nlink is %d, but calculated nlink is %d", + ubifs_err(c, "inode %lu nlink is %d, but calculated nlink is %d", (unsigned long)fscki->inum, fscki->nlink, fscki->references); goto out_dump; } } if (fscki->xattr_sz != fscki->calc_xsz) { - ubifs_err("inode %lu has xattr size %u, but calculated size is %lld", + ubifs_err(c, "inode %lu has xattr size %u, but calculated size is %lld", (unsigned long)fscki->inum, fscki->xattr_sz, fscki->calc_xsz); goto out_dump; } if (fscki->xattr_cnt != fscki->calc_xcnt) { - ubifs_err("inode %lu has %u xattrs, but calculated count is %lld", + ubifs_err(c, "inode %lu has %u xattrs, but calculated count is %lld", (unsigned long)fscki->inum, fscki->xattr_cnt, fscki->calc_xcnt); goto out_dump; } if (fscki->xattr_nms != fscki->calc_xnms) { - ubifs_err("inode %lu has xattr names' size %u, but calculated names' size is %lld", + ubifs_err(c, "inode %lu has xattr names' size %u, but calculated names' size is %lld", (unsigned long)fscki->inum, fscki->xattr_nms, fscki->calc_xnms); goto out_dump; @@ -2221,11 +2221,11 @@ out_dump: ino_key_init(c, &key, fscki->inum); err = ubifs_lookup_level0(c, &key, &znode, &n); if (!err) { - ubifs_err("inode %lu not found in index", + ubifs_err(c, "inode %lu not found in index", (unsigned long)fscki->inum); return -ENOENT; } else if (err < 0) { - ubifs_err("error %d while looking up inode %lu", + ubifs_err(c, "error %d while looking up inode %lu", err, (unsigned long)fscki->inum); return err; } @@ -2237,13 +2237,13 @@ out_dump: err = ubifs_tnc_read_node(c, zbr, ino); if (err) { - ubifs_err("cannot read inode node at LEB %d:%d, error %d", + ubifs_err(c, "cannot read inode node at LEB %d:%d, error %d", zbr->lnum, zbr->offs, err); kfree(ino); return err; } - ubifs_msg("dump of the inode %lu sitting in LEB %d:%d", + ubifs_msg(c, "dump of the inode %lu sitting in LEB %d:%d", (unsigned long)fscki->inum, zbr->lnum, zbr->offs); ubifs_dump_node(c, ino); kfree(ino); @@ -2284,7 +2284,7 @@ int dbg_check_filesystem(struct ubifs_info *c) return 0; out_free: - ubifs_err("file-system check failed with error %d", err); + ubifs_err(c, "file-system check failed with error %d", err); dump_stack(); free_inodes(&fsckd); return err; @@ -2315,12 +2315,12 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head) sb = container_of(cur->next, struct ubifs_scan_node, list); if (sa->type != UBIFS_DATA_NODE) { - ubifs_err("bad node type %d", sa->type); + ubifs_err(c, "bad node type %d", sa->type); ubifs_dump_node(c, sa->node); return -EINVAL; } if (sb->type != UBIFS_DATA_NODE) { - ubifs_err("bad node type %d", sb->type); + ubifs_err(c, "bad node type %d", sb->type); ubifs_dump_node(c, sb->node); return -EINVAL; } @@ -2331,7 +2331,7 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head) if (inuma < inumb) continue; if (inuma > inumb) { - ubifs_err("larger inum %lu goes before inum %lu", + ubifs_err(c, "larger inum %lu goes before inum %lu", (unsigned long)inuma, (unsigned long)inumb); goto error_dump; } @@ -2340,11 +2340,11 @@ int dbg_check_data_nodes_order(struct ubifs_info *c, struct list_head *head) blkb = key_block(c, &sb->key); if (blka > blkb) { - ubifs_err("larger block %u goes before %u", blka, blkb); + ubifs_err(c, "larger block %u goes before %u", blka, blkb); goto error_dump; } if (blka == blkb) { - ubifs_err("two data nodes for the same block"); + ubifs_err(c, "two data nodes for the same block"); goto error_dump; } } @@ -2383,19 +2383,19 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && sa->type != UBIFS_XENT_NODE) { - ubifs_err("bad node type %d", sa->type); + ubifs_err(c, "bad node type %d", sa->type); ubifs_dump_node(c, sa->node); return -EINVAL; } if (sa->type != UBIFS_INO_NODE && sa->type != UBIFS_DENT_NODE && sa->type != UBIFS_XENT_NODE) { - ubifs_err("bad node type %d", sb->type); + ubifs_err(c, "bad node type %d", sb->type); ubifs_dump_node(c, sb->node); return -EINVAL; } if (sa->type != UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { - ubifs_err("non-inode node goes before inode node"); + ubifs_err(c, "non-inode node goes before inode node"); goto error_dump; } @@ -2405,7 +2405,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) if (sa->type == UBIFS_INO_NODE && sb->type == UBIFS_INO_NODE) { /* Inode nodes are sorted in descending size order */ if (sa->len < sb->len) { - ubifs_err("smaller inode node goes first"); + ubifs_err(c, "smaller inode node goes first"); goto error_dump; } continue; @@ -2421,7 +2421,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) if (inuma < inumb) continue; if (inuma > inumb) { - ubifs_err("larger inum %lu goes before inum %lu", + ubifs_err(c, "larger inum %lu goes before inum %lu", (unsigned long)inuma, (unsigned long)inumb); goto error_dump; } @@ -2430,7 +2430,7 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) hashb = key_block(c, &sb->key); if (hasha > hashb) { - ubifs_err("larger hash %u goes before %u", + ubifs_err(c, "larger hash %u goes before %u", hasha, hashb); goto error_dump; } @@ -2439,9 +2439,9 @@ int dbg_check_nondata_nodes_order(struct ubifs_info *c, struct list_head *head) return 0; error_dump: - ubifs_msg("dumping first node"); + ubifs_msg(c, "dumping first node"); ubifs_dump_node(c, sa->node); - ubifs_msg("dumping second node"); + ubifs_msg(c, "dumping second node"); ubifs_dump_node(c, sb->node); return -EINVAL; return 0; @@ -2470,13 +2470,13 @@ static int power_cut_emulated(struct ubifs_info *c, int lnum, int write) delay = prandom_u32() % 60000; d->pc_timeout = jiffies; d->pc_timeout += msecs_to_jiffies(delay); - ubifs_warn("failing after %lums", delay); + ubifs_warn(c, "failing after %lums", delay); } else { d->pc_delay = 2; delay = prandom_u32() % 10000; /* Fail within 10000 operations */ d->pc_cnt_max = delay; - ubifs_warn("failing after %lu calls", delay); + ubifs_warn(c, "failing after %lu calls", delay); } } @@ -2494,55 +2494,55 @@ static int power_cut_emulated(struct ubifs_info *c, int lnum, int write) return 0; if (chance(19, 20)) return 0; - ubifs_warn("failing in super block LEB %d", lnum); + ubifs_warn(c, "failing in super block LEB %d", lnum); } else if (lnum == UBIFS_MST_LNUM || lnum == UBIFS_MST_LNUM + 1) { if (chance(19, 20)) return 0; - ubifs_warn("failing in master LEB %d", lnum); + ubifs_warn(c, "failing in master LEB %d", lnum); } else if (lnum >= UBIFS_LOG_LNUM && lnum <= c->log_last) { if (write && chance(99, 100)) return 0; if (chance(399, 400)) return 0; - ubifs_warn("failing in log LEB %d", lnum); + ubifs_warn(c, "failing in log LEB %d", lnum); } else if (lnum >= c->lpt_first && lnum <= c->lpt_last) { if (write && chance(7, 8)) return 0; if (chance(19, 20)) return 0; - ubifs_warn("failing in LPT LEB %d", lnum); + ubifs_warn(c, "failing in LPT LEB %d", lnum); } else if (lnum >= c->orph_first && lnum <= c->orph_last) { if (write && chance(1, 2)) return 0; if (chance(9, 10)) return 0; - ubifs_warn("failing in orphan LEB %d", lnum); + ubifs_warn(c, "failing in orphan LEB %d", lnum); } else if (lnum == c->ihead_lnum) { if (chance(99, 100)) return 0; - ubifs_warn("failing in index head LEB %d", lnum); + ubifs_warn(c, "failing in index head LEB %d", lnum); } else if (c->jheads && lnum == c->jheads[GCHD].wbuf.lnum) { if (chance(9, 10)) return 0; - ubifs_warn("failing in GC head LEB %d", lnum); + ubifs_warn(c, "failing in GC head LEB %d", lnum); } else if (write && !RB_EMPTY_ROOT(&c->buds) && !ubifs_search_bud(c, lnum)) { if (chance(19, 20)) return 0; - ubifs_warn("failing in non-bud LEB %d", lnum); + ubifs_warn(c, "failing in non-bud LEB %d", lnum); } else if (c->cmt_state == COMMIT_RUNNING_BACKGROUND || c->cmt_state == COMMIT_RUNNING_REQUIRED) { if (chance(999, 1000)) return 0; - ubifs_warn("failing in bud LEB %d commit running", lnum); + ubifs_warn(c, "failing in bud LEB %d commit running", lnum); } else { if (chance(9999, 10000)) return 0; - ubifs_warn("failing in bud LEB %d commit not running", lnum); + ubifs_warn(c, "failing in bud LEB %d commit not running", lnum); } d->pc_happened = 1; - ubifs_warn("========== Power cut emulated =========="); + ubifs_warn(c, "========== Power cut emulated =========="); dump_stack(); return 1; } @@ -2557,7 +2557,7 @@ static int corrupt_data(const struct ubifs_info *c, const void *buf, /* Corruption span max to end of write unit */ to = min(len, ALIGN(from + 1, c->max_write_size)); - ubifs_warn("filled bytes %u-%u with %s", from, to - 1, + ubifs_warn(c, "filled bytes %u-%u with %s", from, to - 1, ffs ? "0xFFs" : "random data"); if (ffs) @@ -2579,7 +2579,7 @@ int dbg_leb_write(struct ubifs_info *c, int lnum, const void *buf, failing = power_cut_emulated(c, lnum, 1); if (failing) { len = corrupt_data(c, buf, len); - ubifs_warn("actually write %d bytes to LEB %d:%d (the buffer was corrupted)", + ubifs_warn(c, "actually write %d bytes to LEB %d:%d (the buffer was corrupted)", len, lnum, offs); } err = ubi_leb_write(c->ubi, lnum, buf, offs, len); @@ -2909,7 +2909,7 @@ out_remove: debugfs_remove_recursive(d->dfs_dir); out: err = dent ? PTR_ERR(dent) : -ENODEV; - ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n", + ubifs_err(c, "cannot create \"%s\" debugfs file or directory, error %d\n", fname, err); return err; } @@ -3063,8 +3063,8 @@ out_remove: debugfs_remove_recursive(dfs_rootdir); out: err = dent ? PTR_ERR(dent) : -ENODEV; - ubifs_err("cannot create \"%s\" debugfs file or directory, error %d\n", - fname, err); + pr_err("UBIFS error (pid %d): cannot create \"%s\" debugfs file or directory, error %d\n", + current->pid, fname, err); return err; } diff --git a/fs/ubifs/dir.c b/fs/ubifs/dir.c index 0fa6c803992e..02d1ee778df0 100644 --- a/fs/ubifs/dir.c +++ b/fs/ubifs/dir.c @@ -146,12 +146,12 @@ struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir, if (c->highest_inum >= INUM_WARN_WATERMARK) { if (c->highest_inum >= INUM_WATERMARK) { spin_unlock(&c->cnt_lock); - ubifs_err("out of inode numbers"); + ubifs_err(c, "out of inode numbers"); make_bad_inode(inode); iput(inode); return ERR_PTR(-EINVAL); } - ubifs_warn("running out of inode numbers (current %lu, max %d)", + ubifs_warn(c, "running out of inode numbers (current %lu, max %u)", (unsigned long)c->highest_inum, INUM_WATERMARK); } @@ -222,7 +222,7 @@ static struct dentry *ubifs_lookup(struct inode *dir, struct dentry *dentry, * checking. */ err = PTR_ERR(inode); - ubifs_err("dead directory entry '%pd', error %d", + ubifs_err(c, "dead directory entry '%pd', error %d", dentry, err); ubifs_ro_mode(c, err); goto out; @@ -272,7 +272,7 @@ static int ubifs_create(struct inode *dir, struct dentry *dentry, umode_t mode, err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) - goto out_cancel; + goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; @@ -292,11 +292,12 @@ out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); +out_inode: make_bad_inode(inode); iput(inode); out_budg: ubifs_release_budget(c, &req); - ubifs_err("cannot create regular file, error %d", err); + ubifs_err(c, "cannot create regular file, error %d", err); return err; } @@ -449,7 +450,7 @@ static int ubifs_readdir(struct file *file, struct dir_context *ctx) out: if (err != -ENOENT) { - ubifs_err("cannot find next direntry, error %d", err); + ubifs_err(c, "cannot find next direntry, error %d", err); return err; } @@ -732,7 +733,7 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) - goto out_cancel; + goto out_inode; mutex_lock(&dir_ui->ui_mutex); insert_inode_hash(inode); @@ -743,7 +744,7 @@ static int ubifs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) dir->i_mtime = dir->i_ctime = inode->i_ctime; err = ubifs_jnl_update(c, dir, &dentry->d_name, inode, 0, 0); if (err) { - ubifs_err("cannot create directory, error %d", err); + ubifs_err(c, "cannot create directory, error %d", err); goto out_cancel; } mutex_unlock(&dir_ui->ui_mutex); @@ -757,6 +758,7 @@ out_cancel: dir_ui->ui_size = dir->i_size; drop_nlink(dir); mutex_unlock(&dir_ui->ui_mutex); +out_inode: make_bad_inode(inode); iput(inode); out_budg: @@ -816,7 +818,7 @@ static int ubifs_mknod(struct inode *dir, struct dentry *dentry, err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) - goto out_cancel; + goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; @@ -836,6 +838,7 @@ out_cancel: dir->i_size -= sz_change; dir_ui->ui_size = dir->i_size; mutex_unlock(&dir_ui->ui_mutex); +out_inode: make_bad_inode(inode); iput(inode); out_budg: @@ -896,7 +899,7 @@ static int ubifs_symlink(struct inode *dir, struct dentry *dentry, err = ubifs_init_security(dir, inode, &dentry->d_name); if (err) - goto out_cancel; + goto out_inode; mutex_lock(&dir_ui->ui_mutex); dir->i_size += sz_change; diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c index c3d15fe83403..3ba3fef64e9e 100644 --- a/fs/ubifs/file.c +++ b/fs/ubifs/file.c @@ -79,7 +79,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block, dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; out_len = UBIFS_BLOCK_SIZE; - err = ubifs_decompress(&dn->data, dlen, addr, &out_len, + err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len, le16_to_cpu(dn->compr_type)); if (err || len != out_len) goto dump; @@ -95,7 +95,7 @@ static int read_block(struct inode *inode, void *addr, unsigned int block, return 0; dump: - ubifs_err("bad data node (block %u, inode %lu)", + ubifs_err(c, "bad data node (block %u, inode %lu)", block, inode->i_ino); ubifs_dump_node(c, dn); return -EINVAL; @@ -160,13 +160,14 @@ static int do_readpage(struct page *page) addr += UBIFS_BLOCK_SIZE; } if (err) { + struct ubifs_info *c = inode->i_sb->s_fs_info; if (err == -ENOENT) { /* Not found, so it must be a hole */ SetPageChecked(page); dbg_gen("hole"); goto out_free; } - ubifs_err("cannot read page %lu of inode %lu, error %d", + ubifs_err(c, "cannot read page %lu of inode %lu, error %d", page->index, inode->i_ino, err); goto error; } @@ -649,7 +650,7 @@ static int populate_page(struct ubifs_info *c, struct page *page, dlen = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; out_len = UBIFS_BLOCK_SIZE; - err = ubifs_decompress(&dn->data, dlen, addr, &out_len, + err = ubifs_decompress(c, &dn->data, dlen, addr, &out_len, le16_to_cpu(dn->compr_type)); if (err || len != out_len) goto out_err; @@ -697,7 +698,7 @@ out_err: SetPageError(page); flush_dcache_page(page); kunmap(page); - ubifs_err("bad data node (block %u, inode %lu)", + ubifs_err(c, "bad data node (block %u, inode %lu)", page_block, inode->i_ino); return -EINVAL; } @@ -801,7 +802,7 @@ out_free: return ret; out_warn: - ubifs_warn("ignoring error %d and skipping bulk-read", err); + ubifs_warn(c, "ignoring error %d and skipping bulk-read", err); goto out_free; out_bu_off: @@ -929,7 +930,7 @@ static int do_writepage(struct page *page, int len) } if (err) { SetPageError(page); - ubifs_err("cannot write page %lu of inode %lu, error %d", + ubifs_err(c, "cannot write page %lu of inode %lu, error %d", page->index, inode->i_ino, err); ubifs_ro_mode(c, err); } @@ -1484,7 +1485,7 @@ static int ubifs_vm_page_mkwrite(struct vm_area_struct *vma, err = ubifs_budget_space(c, &req); if (unlikely(err)) { if (err == -ENOSPC) - ubifs_warn("out of space for mmapped file (inode number %lu)", + ubifs_warn(c, "out of space for mmapped file (inode number %lu)", inode->i_ino); return VM_FAULT_SIGBUS; } @@ -1580,8 +1581,6 @@ const struct inode_operations ubifs_symlink_inode_operations = { const struct file_operations ubifs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = generic_file_read_iter, .write_iter = ubifs_write_iter, .mmap = ubifs_file_mmap, diff --git a/fs/ubifs/io.c b/fs/ubifs/io.c index fb08b0c514b6..97be41215332 100644 --- a/fs/ubifs/io.c +++ b/fs/ubifs/io.c @@ -85,7 +85,7 @@ void ubifs_ro_mode(struct ubifs_info *c, int err) c->ro_error = 1; c->no_chk_data_crc = 0; c->vfs_sb->s_flags |= MS_RDONLY; - ubifs_warn("switched to read-only mode, error %d", err); + ubifs_warn(c, "switched to read-only mode, error %d", err); dump_stack(); } } @@ -107,7 +107,7 @@ int ubifs_leb_read(const struct ubifs_info *c, int lnum, void *buf, int offs, * @even_ebadmsg is true. */ if (err && (err != -EBADMSG || even_ebadmsg)) { - ubifs_err("reading %d bytes from LEB %d:%d failed, error %d", + ubifs_err(c, "reading %d bytes from LEB %d:%d failed, error %d", len, lnum, offs, err); dump_stack(); } @@ -127,7 +127,7 @@ int ubifs_leb_write(struct ubifs_info *c, int lnum, const void *buf, int offs, else err = dbg_leb_write(c, lnum, buf, offs, len); if (err) { - ubifs_err("writing %d bytes to LEB %d:%d failed, error %d", + ubifs_err(c, "writing %d bytes to LEB %d:%d failed, error %d", len, lnum, offs, err); ubifs_ro_mode(c, err); dump_stack(); @@ -147,7 +147,7 @@ int ubifs_leb_change(struct ubifs_info *c, int lnum, const void *buf, int len) else err = dbg_leb_change(c, lnum, buf, len); if (err) { - ubifs_err("changing %d bytes in LEB %d failed, error %d", + ubifs_err(c, "changing %d bytes in LEB %d failed, error %d", len, lnum, err); ubifs_ro_mode(c, err); dump_stack(); @@ -167,7 +167,7 @@ int ubifs_leb_unmap(struct ubifs_info *c, int lnum) else err = dbg_leb_unmap(c, lnum); if (err) { - ubifs_err("unmap LEB %d failed, error %d", lnum, err); + ubifs_err(c, "unmap LEB %d failed, error %d", lnum, err); ubifs_ro_mode(c, err); dump_stack(); } @@ -186,7 +186,7 @@ int ubifs_leb_map(struct ubifs_info *c, int lnum) else err = dbg_leb_map(c, lnum); if (err) { - ubifs_err("mapping LEB %d failed, error %d", lnum, err); + ubifs_err(c, "mapping LEB %d failed, error %d", lnum, err); ubifs_ro_mode(c, err); dump_stack(); } @@ -199,7 +199,7 @@ int ubifs_is_mapped(const struct ubifs_info *c, int lnum) err = ubi_is_mapped(c->ubi, lnum); if (err < 0) { - ubifs_err("ubi_is_mapped failed for LEB %d, error %d", + ubifs_err(c, "ubi_is_mapped failed for LEB %d, error %d", lnum, err); dump_stack(); } @@ -247,7 +247,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, magic = le32_to_cpu(ch->magic); if (magic != UBIFS_NODE_MAGIC) { if (!quiet) - ubifs_err("bad magic %#08x, expected %#08x", + ubifs_err(c, "bad magic %#08x, expected %#08x", magic, UBIFS_NODE_MAGIC); err = -EUCLEAN; goto out; @@ -256,7 +256,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, type = ch->node_type; if (type < 0 || type >= UBIFS_NODE_TYPES_CNT) { if (!quiet) - ubifs_err("bad node type %d", type); + ubifs_err(c, "bad node type %d", type); goto out; } @@ -279,7 +279,7 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, node_crc = le32_to_cpu(ch->crc); if (crc != node_crc) { if (!quiet) - ubifs_err("bad CRC: calculated %#08x, read %#08x", + ubifs_err(c, "bad CRC: calculated %#08x, read %#08x", crc, node_crc); err = -EUCLEAN; goto out; @@ -289,10 +289,10 @@ int ubifs_check_node(const struct ubifs_info *c, const void *buf, int lnum, out_len: if (!quiet) - ubifs_err("bad node length %d", node_len); + ubifs_err(c, "bad node length %d", node_len); out: if (!quiet) { - ubifs_err("bad node at LEB %d:%d", lnum, offs); + ubifs_err(c, "bad node at LEB %d:%d", lnum, offs); ubifs_dump_node(c, buf); dump_stack(); } @@ -355,11 +355,11 @@ static unsigned long long next_sqnum(struct ubifs_info *c) if (unlikely(sqnum >= SQNUM_WARN_WATERMARK)) { if (sqnum >= SQNUM_WATERMARK) { - ubifs_err("sequence number overflow %llu, end of life", + ubifs_err(c, "sequence number overflow %llu, end of life", sqnum); ubifs_ro_mode(c, -EINVAL); } - ubifs_warn("running out of sequence numbers, end of life soon"); + ubifs_warn(c, "running out of sequence numbers, end of life soon"); } return sqnum; @@ -636,7 +636,7 @@ int ubifs_bg_wbufs_sync(struct ubifs_info *c) err = ubifs_wbuf_sync_nolock(wbuf); mutex_unlock(&wbuf->io_mutex); if (err) { - ubifs_err("cannot sync write-buffer, error %d", err); + ubifs_err(c, "cannot sync write-buffer, error %d", err); ubifs_ro_mode(c, err); goto out_timers; } @@ -833,7 +833,7 @@ exit: return 0; out: - ubifs_err("cannot write %d bytes to LEB %d:%d, error %d", + ubifs_err(c, "cannot write %d bytes to LEB %d:%d, error %d", len, wbuf->lnum, wbuf->offs, err); ubifs_dump_node(c, buf); dump_stack(); @@ -932,27 +932,27 @@ int ubifs_read_node_wbuf(struct ubifs_wbuf *wbuf, void *buf, int type, int len, } if (type != ch->node_type) { - ubifs_err("bad node type (%d but expected %d)", + ubifs_err(c, "bad node type (%d but expected %d)", ch->node_type, type); goto out; } err = ubifs_check_node(c, buf, lnum, offs, 0, 0); if (err) { - ubifs_err("expected node type %d", type); + ubifs_err(c, "expected node type %d", type); return err; } rlen = le32_to_cpu(ch->len); if (rlen != len) { - ubifs_err("bad node length %d, expected %d", rlen, len); + ubifs_err(c, "bad node length %d, expected %d", rlen, len); goto out; } return 0; out: - ubifs_err("bad node at LEB %d:%d", lnum, offs); + ubifs_err(c, "bad node at LEB %d:%d", lnum, offs); ubifs_dump_node(c, buf); dump_stack(); return -EINVAL; diff --git a/fs/ubifs/ioctl.c b/fs/ubifs/ioctl.c index 648b143606cc..3c7b29de0ca7 100644 --- a/fs/ubifs/ioctl.c +++ b/fs/ubifs/ioctl.c @@ -138,7 +138,7 @@ static int setflags(struct inode *inode, int flags) return err; out_unlock: - ubifs_err("can't modify inode %lu attributes", inode->i_ino); + ubifs_err(c, "can't modify inode %lu attributes", inode->i_ino); mutex_unlock(&ui->ui_mutex); ubifs_release_budget(c, &req); return err; diff --git a/fs/ubifs/journal.c b/fs/ubifs/journal.c index f6ac3f29323c..90ae1a8439d9 100644 --- a/fs/ubifs/journal.c +++ b/fs/ubifs/journal.c @@ -363,11 +363,11 @@ again: * This should not happen unless the journal size limitations * are too tough. */ - ubifs_err("stuck in space allocation"); + ubifs_err(c, "stuck in space allocation"); err = -ENOSPC; goto out; } else if (cmt_retries > 32) - ubifs_warn("too many space allocation re-tries (%d)", + ubifs_warn(c, "too many space allocation re-tries (%d)", cmt_retries); dbg_jnl("-EAGAIN, commit and retry (retried %d times)", @@ -380,7 +380,7 @@ again: goto again; out: - ubifs_err("cannot reserve %d bytes in jhead %d, error %d", + ubifs_err(c, "cannot reserve %d bytes in jhead %d, error %d", len, jhead, err); if (err == -ENOSPC) { /* This are some budgeting problems, print useful information */ @@ -731,7 +731,7 @@ int ubifs_jnl_write_data(struct ubifs_info *c, const struct inode *inode, compr_type = ui->compr_type; out_len = dlen - UBIFS_DATA_NODE_SZ; - ubifs_compress(buf, len, &data->data, &out_len, &compr_type); + ubifs_compress(c, buf, len, &data->data, &out_len, &compr_type); ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); dlen = UBIFS_DATA_NODE_SZ + out_len; @@ -1100,7 +1100,8 @@ out_free: * This function is used when an inode is truncated and the last data node of * the inode has to be re-compressed and re-written. */ -static int recomp_data_node(struct ubifs_data_node *dn, int *new_len) +static int recomp_data_node(const struct ubifs_info *c, + struct ubifs_data_node *dn, int *new_len) { void *buf; int err, len, compr_type, out_len; @@ -1112,11 +1113,11 @@ static int recomp_data_node(struct ubifs_data_node *dn, int *new_len) len = le32_to_cpu(dn->ch.len) - UBIFS_DATA_NODE_SZ; compr_type = le16_to_cpu(dn->compr_type); - err = ubifs_decompress(&dn->data, len, buf, &out_len, compr_type); + err = ubifs_decompress(c, &dn->data, len, buf, &out_len, compr_type); if (err) goto out; - ubifs_compress(buf, *new_len, &dn->data, &out_len, &compr_type); + ubifs_compress(c, buf, *new_len, &dn->data, &out_len, &compr_type); ubifs_assert(out_len <= UBIFS_BLOCK_SIZE); dn->compr_type = cpu_to_le16(compr_type); dn->size = cpu_to_le32(*new_len); @@ -1191,7 +1192,7 @@ int ubifs_jnl_truncate(struct ubifs_info *c, const struct inode *inode, int compr_type = le16_to_cpu(dn->compr_type); if (compr_type != UBIFS_COMPR_NONE) { - err = recomp_data_node(dn, &dlen); + err = recomp_data_node(c, dn, &dlen); if (err) goto out_free; } else { diff --git a/fs/ubifs/log.c b/fs/ubifs/log.c index c14628fbeee2..8c795e6392b1 100644 --- a/fs/ubifs/log.c +++ b/fs/ubifs/log.c @@ -696,7 +696,7 @@ int ubifs_consolidate_log(struct ubifs_info *c) destroy_done_tree(&done_tree); vfree(buf); if (write_lnum == c->lhead_lnum) { - ubifs_err("log is too full"); + ubifs_err(c, "log is too full"); return -EINVAL; } /* Unmap remaining LEBs */ @@ -743,7 +743,7 @@ static int dbg_check_bud_bytes(struct ubifs_info *c) bud_bytes += c->leb_size - bud->start; if (c->bud_bytes != bud_bytes) { - ubifs_err("bad bud_bytes %lld, calculated %lld", + ubifs_err(c, "bad bud_bytes %lld, calculated %lld", c->bud_bytes, bud_bytes); err = -EINVAL; } diff --git a/fs/ubifs/lprops.c b/fs/ubifs/lprops.c index 46190a7c42a6..a0011aa3a779 100644 --- a/fs/ubifs/lprops.c +++ b/fs/ubifs/lprops.c @@ -682,7 +682,7 @@ int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, out: ubifs_release_lprops(c); if (err) - ubifs_err("cannot change properties of LEB %d, error %d", + ubifs_err(c, "cannot change properties of LEB %d, error %d", lnum, err); return err; } @@ -721,7 +721,7 @@ int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty, out: ubifs_release_lprops(c); if (err) - ubifs_err("cannot update properties of LEB %d, error %d", + ubifs_err(c, "cannot update properties of LEB %d, error %d", lnum, err); return err; } @@ -746,7 +746,7 @@ int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp) lpp = ubifs_lpt_lookup(c, lnum); if (IS_ERR(lpp)) { err = PTR_ERR(lpp); - ubifs_err("cannot read properties of LEB %d, error %d", + ubifs_err(c, "cannot read properties of LEB %d, error %d", lnum, err); goto out; } @@ -873,13 +873,13 @@ int dbg_check_cats(struct ubifs_info *c) list_for_each_entry(lprops, &c->empty_list, list) { if (lprops->free != c->leb_size) { - ubifs_err("non-empty LEB %d on empty list (free %d dirty %d flags %d)", + ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; } if (lprops->flags & LPROPS_TAKEN) { - ubifs_err("taken LEB %d on empty list (free %d dirty %d flags %d)", + ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; @@ -889,13 +889,13 @@ int dbg_check_cats(struct ubifs_info *c) i = 0; list_for_each_entry(lprops, &c->freeable_list, list) { if (lprops->free + lprops->dirty != c->leb_size) { - ubifs_err("non-freeable LEB %d on freeable list (free %d dirty %d flags %d)", + ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; } if (lprops->flags & LPROPS_TAKEN) { - ubifs_err("taken LEB %d on freeable list (free %d dirty %d flags %d)", + ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; @@ -903,7 +903,7 @@ int dbg_check_cats(struct ubifs_info *c) i += 1; } if (i != c->freeable_cnt) { - ubifs_err("freeable list count %d expected %d", i, + ubifs_err(c, "freeable list count %d expected %d", i, c->freeable_cnt); return -EINVAL; } @@ -912,26 +912,26 @@ int dbg_check_cats(struct ubifs_info *c) list_for_each(pos, &c->idx_gc) i += 1; if (i != c->idx_gc_cnt) { - ubifs_err("idx_gc list count %d expected %d", i, + ubifs_err(c, "idx_gc list count %d expected %d", i, c->idx_gc_cnt); return -EINVAL; } list_for_each_entry(lprops, &c->frdi_idx_list, list) { if (lprops->free + lprops->dirty != c->leb_size) { - ubifs_err("non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)", + ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; } if (lprops->flags & LPROPS_TAKEN) { - ubifs_err("taken LEB %d on frdi_idx list (free %d dirty %d flags %d)", + ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; } if (!(lprops->flags & LPROPS_INDEX)) { - ubifs_err("non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)", + ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)", lprops->lnum, lprops->free, lprops->dirty, lprops->flags); return -EINVAL; @@ -944,15 +944,15 @@ int dbg_check_cats(struct ubifs_info *c) for (i = 0; i < heap->cnt; i++) { lprops = heap->arr[i]; if (!lprops) { - ubifs_err("null ptr in LPT heap cat %d", cat); + ubifs_err(c, "null ptr in LPT heap cat %d", cat); return -EINVAL; } if (lprops->hpos != i) { - ubifs_err("bad ptr in LPT heap cat %d", cat); + ubifs_err(c, "bad ptr in LPT heap cat %d", cat); return -EINVAL; } if (lprops->flags & LPROPS_TAKEN) { - ubifs_err("taken LEB in LPT heap cat %d", cat); + ubifs_err(c, "taken LEB in LPT heap cat %d", cat); return -EINVAL; } } @@ -988,7 +988,7 @@ void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, goto out; } if (lprops != lp) { - ubifs_err("lprops %zx lp %zx lprops->lnum %d lp->lnum %d", + ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d", (size_t)lprops, (size_t)lp, lprops->lnum, lp->lnum); err = 4; @@ -1008,7 +1008,7 @@ void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat, } out: if (err) { - ubifs_err("failed cat %d hpos %d err %d", cat, i, err); + ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err); dump_stack(); ubifs_dump_heap(c, heap, cat); } @@ -1039,7 +1039,7 @@ static int scan_check_cb(struct ubifs_info *c, if (cat != LPROPS_UNCAT) { cat = ubifs_categorize_lprops(c, lp); if (cat != (lp->flags & LPROPS_CAT_MASK)) { - ubifs_err("bad LEB category %d expected %d", + ubifs_err(c, "bad LEB category %d expected %d", (lp->flags & LPROPS_CAT_MASK), cat); return -EINVAL; } @@ -1074,7 +1074,7 @@ static int scan_check_cb(struct ubifs_info *c, } } if (!found) { - ubifs_err("bad LPT list (category %d)", cat); + ubifs_err(c, "bad LPT list (category %d)", cat); return -EINVAL; } } @@ -1086,7 +1086,7 @@ static int scan_check_cb(struct ubifs_info *c, if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) || lp != heap->arr[lp->hpos]) { - ubifs_err("bad LPT heap (category %d)", cat); + ubifs_err(c, "bad LPT heap (category %d)", cat); return -EINVAL; } } @@ -1133,7 +1133,7 @@ static int scan_check_cb(struct ubifs_info *c, is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0; if (is_idx && snod->type != UBIFS_IDX_NODE) { - ubifs_err("indexing node in data LEB %d:%d", + ubifs_err(c, "indexing node in data LEB %d:%d", lnum, snod->offs); goto out_destroy; } @@ -1159,7 +1159,7 @@ static int scan_check_cb(struct ubifs_info *c, if (free > c->leb_size || free < 0 || dirty > c->leb_size || dirty < 0) { - ubifs_err("bad calculated accounting for LEB %d: free %d, dirty %d", + ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d", lnum, free, dirty); goto out_destroy; } @@ -1206,13 +1206,13 @@ static int scan_check_cb(struct ubifs_info *c, /* Free but not unmapped LEB, it's fine */ is_idx = 0; else { - ubifs_err("indexing node without indexing flag"); + ubifs_err(c, "indexing node without indexing flag"); goto out_print; } } if (!is_idx && (lp->flags & LPROPS_INDEX)) { - ubifs_err("data node with indexing flag"); + ubifs_err(c, "data node with indexing flag"); goto out_print; } @@ -1241,7 +1241,7 @@ static int scan_check_cb(struct ubifs_info *c, return LPT_SCAN_CONTINUE; out_print: - ubifs_err("bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d", + ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d", lnum, lp->free, lp->dirty, lp->flags, free, dirty); ubifs_dump_leb(c, lnum); out_destroy: @@ -1293,11 +1293,11 @@ int dbg_check_lprops(struct ubifs_info *c) lst.total_free != c->lst.total_free || lst.total_dirty != c->lst.total_dirty || lst.total_used != c->lst.total_used) { - ubifs_err("bad overall accounting"); - ubifs_err("calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", + ubifs_err(c, "bad overall accounting"); + ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", lst.empty_lebs, lst.idx_lebs, lst.total_free, lst.total_dirty, lst.total_used); - ubifs_err("read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", + ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld", c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free, c->lst.total_dirty, c->lst.total_used); err = -EINVAL; @@ -1306,10 +1306,10 @@ int dbg_check_lprops(struct ubifs_info *c) if (lst.total_dead != c->lst.total_dead || lst.total_dark != c->lst.total_dark) { - ubifs_err("bad dead/dark space accounting"); - ubifs_err("calculated: total_dead %lld, total_dark %lld", + ubifs_err(c, "bad dead/dark space accounting"); + ubifs_err(c, "calculated: total_dead %lld, total_dark %lld", lst.total_dead, lst.total_dark); - ubifs_err("read from lprops: total_dead %lld, total_dark %lld", + ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld", c->lst.total_dead, c->lst.total_dark); err = -EINVAL; goto out; diff --git a/fs/ubifs/lpt.c b/fs/ubifs/lpt.c index 421bd0a80424..dc9f27e9d61b 100644 --- a/fs/ubifs/lpt.c +++ b/fs/ubifs/lpt.c @@ -145,13 +145,13 @@ int ubifs_calc_lpt_geom(struct ubifs_info *c) sz = c->lpt_sz * 2; /* Must have at least 2 times the size */ lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size); if (lebs_needed > c->lpt_lebs) { - ubifs_err("too few LPT LEBs"); + ubifs_err(c, "too few LPT LEBs"); return -EINVAL; } /* Verify that ltab fits in a single LEB (since ltab is a single node */ if (c->ltab_sz > c->leb_size) { - ubifs_err("LPT ltab too big"); + ubifs_err(c, "LPT ltab too big"); return -EINVAL; } @@ -213,7 +213,7 @@ static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs, continue; } if (c->ltab_sz > c->leb_size) { - ubifs_err("LPT ltab too big"); + ubifs_err(c, "LPT ltab too big"); return -EINVAL; } *main_lebs = c->main_lebs; @@ -911,7 +911,7 @@ static void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode, * * This function returns %0 on success and a negative error code on failure. */ -static int check_lpt_crc(void *buf, int len) +static int check_lpt_crc(const struct ubifs_info *c, void *buf, int len) { int pos = 0; uint8_t *addr = buf; @@ -921,8 +921,8 @@ static int check_lpt_crc(void *buf, int len) calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES, len - UBIFS_LPT_CRC_BYTES); if (crc != calc_crc) { - ubifs_err("invalid crc in LPT node: crc %hx calc %hx", crc, - calc_crc); + ubifs_err(c, "invalid crc in LPT node: crc %hx calc %hx", + crc, calc_crc); dump_stack(); return -EINVAL; } @@ -938,14 +938,15 @@ static int check_lpt_crc(void *buf, int len) * * This function returns %0 on success and a negative error code on failure. */ -static int check_lpt_type(uint8_t **addr, int *pos, int type) +static int check_lpt_type(const struct ubifs_info *c, uint8_t **addr, + int *pos, int type) { int node_type; node_type = ubifs_unpack_bits(addr, pos, UBIFS_LPT_TYPE_BITS); if (node_type != type) { - ubifs_err("invalid type (%d) in LPT node type %d", node_type, - type); + ubifs_err(c, "invalid type (%d) in LPT node type %d", + node_type, type); dump_stack(); return -EINVAL; } @@ -966,7 +967,7 @@ static int unpack_pnode(const struct ubifs_info *c, void *buf, uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err; - err = check_lpt_type(&addr, &pos, UBIFS_LPT_PNODE); + err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_PNODE); if (err) return err; if (c->big_lpt) @@ -985,7 +986,7 @@ static int unpack_pnode(const struct ubifs_info *c, void *buf, lprops->flags = 0; lprops->flags |= ubifs_categorize_lprops(c, lprops); } - err = check_lpt_crc(buf, c->pnode_sz); + err = check_lpt_crc(c, buf, c->pnode_sz); return err; } @@ -1003,7 +1004,7 @@ int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf, uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err; - err = check_lpt_type(&addr, &pos, UBIFS_LPT_NNODE); + err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_NNODE); if (err) return err; if (c->big_lpt) @@ -1019,7 +1020,7 @@ int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf, nnode->nbranch[i].offs = ubifs_unpack_bits(&addr, &pos, c->lpt_offs_bits); } - err = check_lpt_crc(buf, c->nnode_sz); + err = check_lpt_crc(c, buf, c->nnode_sz); return err; } @@ -1035,7 +1036,7 @@ static int unpack_ltab(const struct ubifs_info *c, void *buf) uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err; - err = check_lpt_type(&addr, &pos, UBIFS_LPT_LTAB); + err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LTAB); if (err) return err; for (i = 0; i < c->lpt_lebs; i++) { @@ -1051,7 +1052,7 @@ static int unpack_ltab(const struct ubifs_info *c, void *buf) c->ltab[i].tgc = 0; c->ltab[i].cmt = 0; } - err = check_lpt_crc(buf, c->ltab_sz); + err = check_lpt_crc(c, buf, c->ltab_sz); return err; } @@ -1067,7 +1068,7 @@ static int unpack_lsave(const struct ubifs_info *c, void *buf) uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES; int i, pos = 0, err; - err = check_lpt_type(&addr, &pos, UBIFS_LPT_LSAVE); + err = check_lpt_type(c, &addr, &pos, UBIFS_LPT_LSAVE); if (err) return err; for (i = 0; i < c->lsave_cnt; i++) { @@ -1077,7 +1078,7 @@ static int unpack_lsave(const struct ubifs_info *c, void *buf) return -EINVAL; c->lsave[i] = lnum; } - err = check_lpt_crc(buf, c->lsave_sz); + err = check_lpt_crc(c, buf, c->lsave_sz); return err; } @@ -1243,7 +1244,7 @@ int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) return 0; out: - ubifs_err("error %d reading nnode at %d:%d", err, lnum, offs); + ubifs_err(c, "error %d reading nnode at %d:%d", err, lnum, offs); dump_stack(); kfree(nnode); return err; @@ -1308,10 +1309,10 @@ static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip) return 0; out: - ubifs_err("error %d reading pnode at %d:%d", err, lnum, offs); + ubifs_err(c, "error %d reading pnode at %d:%d", err, lnum, offs); ubifs_dump_pnode(c, pnode, parent, iip); dump_stack(); - ubifs_err("calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); + ubifs_err(c, "calc num: %d", calc_pnode_num_from_parent(c, parent, iip)); kfree(pnode); return err; } @@ -2095,7 +2096,7 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, int i; if (pnode->num != col) { - ubifs_err("pnode num %d expected %d parent num %d iip %d", + ubifs_err(c, "pnode num %d expected %d parent num %d iip %d", pnode->num, col, pnode->parent->num, pnode->iip); return -EINVAL; } @@ -2110,13 +2111,13 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, if (lnum >= c->leb_cnt) continue; if (lprops->lnum != lnum) { - ubifs_err("bad LEB number %d expected %d", + ubifs_err(c, "bad LEB number %d expected %d", lprops->lnum, lnum); return -EINVAL; } if (lprops->flags & LPROPS_TAKEN) { if (cat != LPROPS_UNCAT) { - ubifs_err("LEB %d taken but not uncat %d", + ubifs_err(c, "LEB %d taken but not uncat %d", lprops->lnum, cat); return -EINVAL; } @@ -2129,7 +2130,7 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, case LPROPS_FRDI_IDX: break; default: - ubifs_err("LEB %d index but cat %d", + ubifs_err(c, "LEB %d index but cat %d", lprops->lnum, cat); return -EINVAL; } @@ -2142,7 +2143,7 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, case LPROPS_FREEABLE: break; default: - ubifs_err("LEB %d not index but cat %d", + ubifs_err(c, "LEB %d not index but cat %d", lprops->lnum, cat); return -EINVAL; } @@ -2183,14 +2184,14 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, break; } if (!found) { - ubifs_err("LEB %d cat %d not found in cat heap/list", + ubifs_err(c, "LEB %d cat %d not found in cat heap/list", lprops->lnum, cat); return -EINVAL; } switch (cat) { case LPROPS_EMPTY: if (lprops->free != c->leb_size) { - ubifs_err("LEB %d cat %d free %d dirty %d", + ubifs_err(c, "LEB %d cat %d free %d dirty %d", lprops->lnum, cat, lprops->free, lprops->dirty); return -EINVAL; @@ -2199,7 +2200,7 @@ static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode, case LPROPS_FREEABLE: case LPROPS_FRDI_IDX: if (lprops->free + lprops->dirty != c->leb_size) { - ubifs_err("LEB %d cat %d free %d dirty %d", + ubifs_err(c, "LEB %d cat %d free %d dirty %d", lprops->lnum, cat, lprops->free, lprops->dirty); return -EINVAL; @@ -2236,7 +2237,7 @@ int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode, /* cnode is a nnode */ num = calc_nnode_num(row, col); if (cnode->num != num) { - ubifs_err("nnode num %d expected %d parent num %d iip %d", + ubifs_err(c, "nnode num %d expected %d parent num %d iip %d", cnode->num, num, (nnode ? nnode->num : 0), cnode->iip); return -EINVAL; diff --git a/fs/ubifs/lpt_commit.c b/fs/ubifs/lpt_commit.c index d9c02928e992..ce89bdc3eb02 100644 --- a/fs/ubifs/lpt_commit.c +++ b/fs/ubifs/lpt_commit.c @@ -319,7 +319,7 @@ static int layout_cnodes(struct ubifs_info *c) return 0; no_space: - ubifs_err("LPT out of space at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", + ubifs_err(c, "LPT out of space at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", lnum, offs, len, done_ltab, done_lsave); ubifs_dump_lpt_info(c); ubifs_dump_lpt_lebs(c); @@ -543,7 +543,7 @@ static int write_cnodes(struct ubifs_info *c) return 0; no_space: - ubifs_err("LPT out of space mismatch at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", + ubifs_err(c, "LPT out of space mismatch at LEB %d:%d needing %d, done_ltab %d, done_lsave %d", lnum, offs, len, done_ltab, done_lsave); ubifs_dump_lpt_info(c); ubifs_dump_lpt_lebs(c); @@ -1638,7 +1638,7 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum) buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); if (!buf) { - ubifs_err("cannot allocate memory for ltab checking"); + ubifs_err(c, "cannot allocate memory for ltab checking"); return 0; } @@ -1660,18 +1660,18 @@ static int dbg_check_ltab_lnum(struct ubifs_info *c, int lnum) continue; } if (!dbg_is_all_ff(p, len)) { - ubifs_err("invalid empty space in LEB %d at %d", + ubifs_err(c, "invalid empty space in LEB %d at %d", lnum, c->leb_size - len); err = -EINVAL; } i = lnum - c->lpt_first; if (len != c->ltab[i].free) { - ubifs_err("invalid free space in LEB %d (free %d, expected %d)", + ubifs_err(c, "invalid free space in LEB %d (free %d, expected %d)", lnum, len, c->ltab[i].free); err = -EINVAL; } if (dirty != c->ltab[i].dirty) { - ubifs_err("invalid dirty space in LEB %d (dirty %d, expected %d)", + ubifs_err(c, "invalid dirty space in LEB %d (dirty %d, expected %d)", lnum, dirty, c->ltab[i].dirty); err = -EINVAL; } @@ -1725,7 +1725,7 @@ int dbg_check_ltab(struct ubifs_info *c) for (lnum = c->lpt_first; lnum <= c->lpt_last; lnum++) { err = dbg_check_ltab_lnum(c, lnum); if (err) { - ubifs_err("failed at LEB %d", lnum); + ubifs_err(c, "failed at LEB %d", lnum); return err; } } @@ -1757,7 +1757,7 @@ int dbg_chk_lpt_free_spc(struct ubifs_info *c) free += c->leb_size; } if (free < c->lpt_sz) { - ubifs_err("LPT space error: free %lld lpt_sz %lld", + ubifs_err(c, "LPT space error: free %lld lpt_sz %lld", free, c->lpt_sz); ubifs_dump_lpt_info(c); ubifs_dump_lpt_lebs(c); @@ -1797,12 +1797,12 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) d->chk_lpt_lebs = 0; d->chk_lpt_wastage = 0; if (c->dirty_pn_cnt > c->pnode_cnt) { - ubifs_err("dirty pnodes %d exceed max %d", + ubifs_err(c, "dirty pnodes %d exceed max %d", c->dirty_pn_cnt, c->pnode_cnt); err = -EINVAL; } if (c->dirty_nn_cnt > c->nnode_cnt) { - ubifs_err("dirty nnodes %d exceed max %d", + ubifs_err(c, "dirty nnodes %d exceed max %d", c->dirty_nn_cnt, c->nnode_cnt); err = -EINVAL; } @@ -1820,22 +1820,22 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) chk_lpt_sz *= d->chk_lpt_lebs; chk_lpt_sz += len - c->nhead_offs; if (d->chk_lpt_sz != chk_lpt_sz) { - ubifs_err("LPT wrote %lld but space used was %lld", + ubifs_err(c, "LPT wrote %lld but space used was %lld", d->chk_lpt_sz, chk_lpt_sz); err = -EINVAL; } if (d->chk_lpt_sz > c->lpt_sz) { - ubifs_err("LPT wrote %lld but lpt_sz is %lld", + ubifs_err(c, "LPT wrote %lld but lpt_sz is %lld", d->chk_lpt_sz, c->lpt_sz); err = -EINVAL; } if (d->chk_lpt_sz2 && d->chk_lpt_sz != d->chk_lpt_sz2) { - ubifs_err("LPT layout size %lld but wrote %lld", + ubifs_err(c, "LPT layout size %lld but wrote %lld", d->chk_lpt_sz, d->chk_lpt_sz2); err = -EINVAL; } if (d->chk_lpt_sz2 && d->new_nhead_offs != len) { - ubifs_err("LPT new nhead offs: expected %d was %d", + ubifs_err(c, "LPT new nhead offs: expected %d was %d", d->new_nhead_offs, len); err = -EINVAL; } @@ -1845,7 +1845,7 @@ int dbg_chk_lpt_sz(struct ubifs_info *c, int action, int len) if (c->big_lpt) lpt_sz += c->lsave_sz; if (d->chk_lpt_sz - d->chk_lpt_wastage > lpt_sz) { - ubifs_err("LPT chk_lpt_sz %lld + waste %lld exceeds %lld", + ubifs_err(c, "LPT chk_lpt_sz %lld + waste %lld exceeds %lld", d->chk_lpt_sz, d->chk_lpt_wastage, lpt_sz); err = -EINVAL; } @@ -1887,7 +1887,7 @@ static void dump_lpt_leb(const struct ubifs_info *c, int lnum) pr_err("(pid %d) start dumping LEB %d\n", current->pid, lnum); buf = p = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); if (!buf) { - ubifs_err("cannot allocate memory to dump LPT"); + ubifs_err(c, "cannot allocate memory to dump LPT"); return; } @@ -1962,7 +1962,7 @@ static void dump_lpt_leb(const struct ubifs_info *c, int lnum) pr_err("LEB %d:%d, lsave len\n", lnum, offs); break; default: - ubifs_err("LPT node type %d not recognized", node_type); + ubifs_err(c, "LPT node type %d not recognized", node_type); goto out; } diff --git a/fs/ubifs/master.c b/fs/ubifs/master.c index 1a4bb9e8b3b8..c6a5e39e2ba5 100644 --- a/fs/ubifs/master.c +++ b/fs/ubifs/master.c @@ -82,7 +82,7 @@ out: return -EUCLEAN; out_dump: - ubifs_err("unexpected node type %d master LEB %d:%d", + ubifs_err(c, "unexpected node type %d master LEB %d:%d", snod->type, lnum, snod->offs); ubifs_scan_destroy(sleb); return -EINVAL; @@ -240,7 +240,7 @@ static int validate_master(const struct ubifs_info *c) return 0; out: - ubifs_err("bad master node at offset %d error %d", c->mst_offs, err); + ubifs_err(c, "bad master node at offset %d error %d", c->mst_offs, err); ubifs_dump_node(c, c->mst_node); return -EINVAL; } @@ -316,7 +316,7 @@ int ubifs_read_master(struct ubifs_info *c) if (c->leb_cnt < old_leb_cnt || c->leb_cnt < UBIFS_MIN_LEB_CNT) { - ubifs_err("bad leb_cnt on master node"); + ubifs_err(c, "bad leb_cnt on master node"); ubifs_dump_node(c, c->mst_node); return -EINVAL; } diff --git a/fs/ubifs/orphan.c b/fs/ubifs/orphan.c index 4409f486ecef..caf2d123e9ee 100644 --- a/fs/ubifs/orphan.c +++ b/fs/ubifs/orphan.c @@ -88,7 +88,7 @@ int ubifs_add_orphan(struct ubifs_info *c, ino_t inum) else if (inum > o->inum) p = &(*p)->rb_right; else { - ubifs_err("orphaned twice"); + ubifs_err(c, "orphaned twice"); spin_unlock(&c->orphan_lock); kfree(orphan); return 0; @@ -155,7 +155,7 @@ void ubifs_delete_orphan(struct ubifs_info *c, ino_t inum) } } spin_unlock(&c->orphan_lock); - ubifs_err("missing orphan ino %lu", (unsigned long)inum); + ubifs_err(c, "missing orphan ino %lu", (unsigned long)inum); dump_stack(); } @@ -287,7 +287,7 @@ static int write_orph_node(struct ubifs_info *c, int atomic) * We limit the number of orphans so that this should * never happen. */ - ubifs_err("out of space in orphan area"); + ubifs_err(c, "out of space in orphan area"); return -EINVAL; } } @@ -397,7 +397,7 @@ static int consolidate(struct ubifs_info *c) * We limit the number of orphans so that this should * never happen. */ - ubifs_err("out of space in orphan area"); + ubifs_err(c, "out of space in orphan area"); err = -EINVAL; } spin_unlock(&c->orphan_lock); @@ -569,7 +569,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, list_for_each_entry(snod, &sleb->nodes, list) { if (snod->type != UBIFS_ORPH_NODE) { - ubifs_err("invalid node type %d in orphan area at %d:%d", + ubifs_err(c, "invalid node type %d in orphan area at %d:%d", snod->type, sleb->lnum, snod->offs); ubifs_dump_node(c, snod->node); return -EINVAL; @@ -596,7 +596,7 @@ static int do_kill_orphans(struct ubifs_info *c, struct ubifs_scan_leb *sleb, * number. That makes this orphan node, out of date. */ if (!first) { - ubifs_err("out of order commit number %llu in orphan node at %d:%d", + ubifs_err(c, "out of order commit number %llu in orphan node at %d:%d", cmt_no, sleb->lnum, snod->offs); ubifs_dump_node(c, snod->node); return -EINVAL; @@ -831,20 +831,20 @@ static int dbg_orphan_check(struct ubifs_info *c, struct ubifs_zbranch *zbr, if (inum != ci->last_ino) { /* Lowest node type is the inode node, so it comes first */ if (key_type(c, &zbr->key) != UBIFS_INO_KEY) - ubifs_err("found orphan node ino %lu, type %d", + ubifs_err(c, "found orphan node ino %lu, type %d", (unsigned long)inum, key_type(c, &zbr->key)); ci->last_ino = inum; ci->tot_inos += 1; err = ubifs_tnc_read_node(c, zbr, ci->node); if (err) { - ubifs_err("node read failed, error %d", err); + ubifs_err(c, "node read failed, error %d", err); return err; } if (ci->node->nlink == 0) /* Must be recorded as an orphan */ if (!dbg_find_check_orphan(&ci->root, inum) && !dbg_find_orphan(c, inum)) { - ubifs_err("missing orphan, ino %lu", + ubifs_err(c, "missing orphan, ino %lu", (unsigned long)inum); ci->missing += 1; } @@ -887,7 +887,7 @@ static int dbg_scan_orphans(struct ubifs_info *c, struct check_info *ci) buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL); if (!buf) { - ubifs_err("cannot allocate memory to check orphans"); + ubifs_err(c, "cannot allocate memory to check orphans"); return 0; } @@ -925,7 +925,7 @@ static int dbg_check_orphans(struct ubifs_info *c) ci.root = RB_ROOT; ci.node = kmalloc(UBIFS_MAX_INO_NODE_SZ, GFP_NOFS); if (!ci.node) { - ubifs_err("out of memory"); + ubifs_err(c, "out of memory"); return -ENOMEM; } @@ -935,12 +935,12 @@ static int dbg_check_orphans(struct ubifs_info *c) err = dbg_walk_index(c, &dbg_orphan_check, NULL, &ci); if (err) { - ubifs_err("cannot scan TNC, error %d", err); + ubifs_err(c, "cannot scan TNC, error %d", err); goto out; } if (ci.missing) { - ubifs_err("%lu missing orphan(s)", ci.missing); + ubifs_err(c, "%lu missing orphan(s)", ci.missing); err = -EINVAL; goto out; } diff --git a/fs/ubifs/recovery.c b/fs/ubifs/recovery.c index c640938f62f0..695fc71d5244 100644 --- a/fs/ubifs/recovery.c +++ b/fs/ubifs/recovery.c @@ -305,7 +305,7 @@ int ubifs_recover_master_node(struct ubifs_info *c) mst = mst2; } - ubifs_msg("recovered master node from LEB %d", + ubifs_msg(c, "recovered master node from LEB %d", (mst == mst1 ? UBIFS_MST_LNUM : UBIFS_MST_LNUM + 1)); memcpy(c->mst_node, mst, UBIFS_MST_NODE_SZ); @@ -360,13 +360,13 @@ int ubifs_recover_master_node(struct ubifs_info *c) out_err: err = -EINVAL; out_free: - ubifs_err("failed to recover master node"); + ubifs_err(c, "failed to recover master node"); if (mst1) { - ubifs_err("dumping first master node"); + ubifs_err(c, "dumping first master node"); ubifs_dump_node(c, mst1); } if (mst2) { - ubifs_err("dumping second master node"); + ubifs_err(c, "dumping second master node"); ubifs_dump_node(c, mst2); } vfree(buf2); @@ -682,7 +682,7 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, ret, lnum, offs); break; } else { - ubifs_err("unexpected return value %d", ret); + ubifs_err(c, "unexpected return value %d", ret); err = -EINVAL; goto error; } @@ -702,7 +702,7 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, * See header comment for this file for more * explanations about the reasons we have this check. */ - ubifs_err("corrupt empty space LEB %d:%d, corruption starts at %d", + ubifs_err(c, "corrupt empty space LEB %d:%d, corruption starts at %d", lnum, offs, corruption); /* Make sure we dump interesting non-0xFF data */ offs += corruption; @@ -788,13 +788,13 @@ struct ubifs_scan_leb *ubifs_recover_leb(struct ubifs_info *c, int lnum, corrupted_rescan: /* Re-scan the corrupted data with verbose messages */ - ubifs_err("corruption %d", ret); + ubifs_err(c, "corruption %d", ret); ubifs_scan_a_node(c, buf, len, lnum, offs, 1); corrupted: ubifs_scanned_corruption(c, lnum, offs, buf); err = -EUCLEAN; error: - ubifs_err("LEB %d scanning failed", lnum); + ubifs_err(c, "LEB %d scanning failed", lnum); ubifs_scan_destroy(sleb); return ERR_PTR(err); } @@ -826,15 +826,15 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, goto out_free; ret = ubifs_scan_a_node(c, cs_node, UBIFS_CS_NODE_SZ, lnum, offs, 0); if (ret != SCANNED_A_NODE) { - ubifs_err("Not a valid node"); + ubifs_err(c, "Not a valid node"); goto out_err; } if (cs_node->ch.node_type != UBIFS_CS_NODE) { - ubifs_err("Node a CS node, type is %d", cs_node->ch.node_type); + ubifs_err(c, "Node a CS node, type is %d", cs_node->ch.node_type); goto out_err; } if (le64_to_cpu(cs_node->cmt_no) != c->cmt_no) { - ubifs_err("CS node cmt_no %llu != current cmt_no %llu", + ubifs_err(c, "CS node cmt_no %llu != current cmt_no %llu", (unsigned long long)le64_to_cpu(cs_node->cmt_no), c->cmt_no); goto out_err; @@ -847,7 +847,7 @@ static int get_cs_sqnum(struct ubifs_info *c, int lnum, int offs, out_err: err = -EINVAL; out_free: - ubifs_err("failed to get CS sqnum"); + ubifs_err(c, "failed to get CS sqnum"); kfree(cs_node); return err; } @@ -899,7 +899,7 @@ struct ubifs_scan_leb *ubifs_recover_log_leb(struct ubifs_info *c, int lnum, } } if (snod->sqnum > cs_sqnum) { - ubifs_err("unrecoverable log corruption in LEB %d", + ubifs_err(c, "unrecoverable log corruption in LEB %d", lnum); ubifs_scan_destroy(sleb); return ERR_PTR(-EUCLEAN); @@ -975,11 +975,8 @@ int ubifs_recover_inl_heads(struct ubifs_info *c, void *sbuf) return err; dbg_rcvry("checking LPT head at %d:%d", c->nhead_lnum, c->nhead_offs); - err = recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf); - if (err) - return err; - return 0; + return recover_head(c, c->nhead_lnum, c->nhead_offs, sbuf); } /** @@ -1004,10 +1001,7 @@ static int clean_an_unclean_leb(struct ubifs_info *c, if (len == 0) { /* Nothing to read, just unmap it */ - err = ubifs_leb_unmap(c, lnum); - if (err) - return err; - return 0; + return ubifs_leb_unmap(c, lnum); } err = ubifs_leb_read(c, lnum, buf, offs, len, 0); @@ -1043,7 +1037,7 @@ static int clean_an_unclean_leb(struct ubifs_info *c, } if (ret == SCANNED_EMPTY_SPACE) { - ubifs_err("unexpected empty space at %d:%d", + ubifs_err(c, "unexpected empty space at %d:%d", lnum, offs); return -EUCLEAN; } @@ -1137,7 +1131,7 @@ static int grab_empty_leb(struct ubifs_info *c) */ lnum = ubifs_find_free_leb_for_idx(c); if (lnum < 0) { - ubifs_err("could not find an empty LEB"); + ubifs_err(c, "could not find an empty LEB"); ubifs_dump_lprops(c); ubifs_dump_budg(c, &c->bi); return lnum; @@ -1217,7 +1211,7 @@ int ubifs_rcvry_gc_commit(struct ubifs_info *c) } mutex_unlock(&wbuf->io_mutex); if (err < 0) { - ubifs_err("GC failed, error %d", err); + ubifs_err(c, "GC failed, error %d", err); if (err == -EAGAIN) err = -EINVAL; return err; @@ -1464,7 +1458,7 @@ static int fix_size_in_place(struct ubifs_info *c, struct size_entry *e) return 0; out: - ubifs_warn("inode %lu failed to fix size %lld -> %lld error %d", + ubifs_warn(c, "inode %lu failed to fix size %lld -> %lld error %d", (unsigned long)e->inum, e->i_size, e->d_size, err); return err; } diff --git a/fs/ubifs/replay.c b/fs/ubifs/replay.c index 9b40a1c5e160..3ca4540130b5 100644 --- a/fs/ubifs/replay.c +++ b/fs/ubifs/replay.c @@ -458,13 +458,13 @@ int ubifs_validate_entry(struct ubifs_info *c, nlen > UBIFS_MAX_NLEN || dent->name[nlen] != 0 || strnlen(dent->name, nlen) != nlen || le64_to_cpu(dent->inum) > MAX_INUM) { - ubifs_err("bad %s node", key_type == UBIFS_DENT_KEY ? + ubifs_err(c, "bad %s node", key_type == UBIFS_DENT_KEY ? "directory entry" : "extended attribute entry"); return -EINVAL; } if (key_type != UBIFS_DENT_KEY && key_type != UBIFS_XENT_KEY) { - ubifs_err("bad key type %d", key_type); + ubifs_err(c, "bad key type %d", key_type); return -EINVAL; } @@ -589,7 +589,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) cond_resched(); if (snod->sqnum >= SQNUM_WATERMARK) { - ubifs_err("file system's life ended"); + ubifs_err(c, "file system's life ended"); goto out_dump; } @@ -647,7 +647,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) if (old_size < 0 || old_size > c->max_inode_sz || new_size < 0 || new_size > c->max_inode_sz || old_size <= new_size) { - ubifs_err("bad truncation node"); + ubifs_err(c, "bad truncation node"); goto out_dump; } @@ -662,7 +662,7 @@ static int replay_bud(struct ubifs_info *c, struct bud_entry *b) break; } default: - ubifs_err("unexpected node type %d in bud LEB %d:%d", + ubifs_err(c, "unexpected node type %d in bud LEB %d:%d", snod->type, lnum, snod->offs); err = -EINVAL; goto out_dump; @@ -685,7 +685,7 @@ out: return err; out_dump: - ubifs_err("bad node is at LEB %d:%d", lnum, snod->offs); + ubifs_err(c, "bad node is at LEB %d:%d", lnum, snod->offs); ubifs_dump_node(c, snod->node); ubifs_scan_destroy(sleb); return -EINVAL; @@ -805,7 +805,7 @@ static int validate_ref(struct ubifs_info *c, const struct ubifs_ref_node *ref) if (bud) { if (bud->jhead == jhead && bud->start <= offs) return 1; - ubifs_err("bud at LEB %d:%d was already referred", lnum, offs); + ubifs_err(c, "bud at LEB %d:%d was already referred", lnum, offs); return -EINVAL; } @@ -861,12 +861,12 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) * numbers. */ if (snod->type != UBIFS_CS_NODE) { - ubifs_err("first log node at LEB %d:%d is not CS node", + ubifs_err(c, "first log node at LEB %d:%d is not CS node", lnum, offs); goto out_dump; } if (le64_to_cpu(node->cmt_no) != c->cmt_no) { - ubifs_err("first CS node at LEB %d:%d has wrong commit number %llu expected %llu", + ubifs_err(c, "first CS node at LEB %d:%d has wrong commit number %llu expected %llu", lnum, offs, (unsigned long long)le64_to_cpu(node->cmt_no), c->cmt_no); @@ -891,7 +891,7 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) /* Make sure the first node sits at offset zero of the LEB */ if (snod->offs != 0) { - ubifs_err("first node is not at zero offset"); + ubifs_err(c, "first node is not at zero offset"); goto out_dump; } @@ -899,12 +899,12 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) cond_resched(); if (snod->sqnum >= SQNUM_WATERMARK) { - ubifs_err("file system's life ended"); + ubifs_err(c, "file system's life ended"); goto out_dump; } if (snod->sqnum < c->cs_sqnum) { - ubifs_err("bad sqnum %llu, commit sqnum %llu", + ubifs_err(c, "bad sqnum %llu, commit sqnum %llu", snod->sqnum, c->cs_sqnum); goto out_dump; } @@ -934,12 +934,12 @@ static int replay_log_leb(struct ubifs_info *c, int lnum, int offs, void *sbuf) case UBIFS_CS_NODE: /* Make sure it sits at the beginning of LEB */ if (snod->offs != 0) { - ubifs_err("unexpected node in log"); + ubifs_err(c, "unexpected node in log"); goto out_dump; } break; default: - ubifs_err("unexpected node in log"); + ubifs_err(c, "unexpected node in log"); goto out_dump; } } @@ -955,7 +955,7 @@ out: return err; out_dump: - ubifs_err("log error detected while replaying the log at LEB %d:%d", + ubifs_err(c, "log error detected while replaying the log at LEB %d:%d", lnum, offs + snod->offs); ubifs_dump_node(c, snod->node); ubifs_scan_destroy(sleb); @@ -1017,7 +1017,7 @@ int ubifs_replay_journal(struct ubifs_info *c) return free; /* Error code */ if (c->ihead_offs != c->leb_size - free) { - ubifs_err("bad index head LEB %d:%d", c->ihead_lnum, + ubifs_err(c, "bad index head LEB %d:%d", c->ihead_lnum, c->ihead_offs); return -EINVAL; } @@ -1040,7 +1040,7 @@ int ubifs_replay_journal(struct ubifs_info *c) * someting went wrong and we cannot proceed mounting * the file-system. */ - ubifs_err("no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted", + ubifs_err(c, "no UBIFS nodes found at the log head LEB %d:%d, possibly corrupted", lnum, 0); err = -EINVAL; } diff --git a/fs/ubifs/sb.c b/fs/ubifs/sb.c index 79c6dbbc0e04..f4fbc7b6b794 100644 --- a/fs/ubifs/sb.c +++ b/fs/ubifs/sb.c @@ -335,7 +335,7 @@ static int create_default_filesystem(struct ubifs_info *c) if (err) return err; - ubifs_msg("default file-system created"); + ubifs_msg(c, "default file-system created"); return 0; } @@ -365,13 +365,13 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) } if (le32_to_cpu(sup->min_io_size) != c->min_io_size) { - ubifs_err("min. I/O unit mismatch: %d in superblock, %d real", + ubifs_err(c, "min. I/O unit mismatch: %d in superblock, %d real", le32_to_cpu(sup->min_io_size), c->min_io_size); goto failed; } if (le32_to_cpu(sup->leb_size) != c->leb_size) { - ubifs_err("LEB size mismatch: %d in superblock, %d real", + ubifs_err(c, "LEB size mismatch: %d in superblock, %d real", le32_to_cpu(sup->leb_size), c->leb_size); goto failed; } @@ -393,33 +393,33 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) min_leb_cnt += c->lpt_lebs + c->orph_lebs + c->jhead_cnt + 6; if (c->leb_cnt < min_leb_cnt || c->leb_cnt > c->vi.size) { - ubifs_err("bad LEB count: %d in superblock, %d on UBI volume, %d minimum required", + ubifs_err(c, "bad LEB count: %d in superblock, %d on UBI volume, %d minimum required", c->leb_cnt, c->vi.size, min_leb_cnt); goto failed; } if (c->max_leb_cnt < c->leb_cnt) { - ubifs_err("max. LEB count %d less than LEB count %d", + ubifs_err(c, "max. LEB count %d less than LEB count %d", c->max_leb_cnt, c->leb_cnt); goto failed; } if (c->main_lebs < UBIFS_MIN_MAIN_LEBS) { - ubifs_err("too few main LEBs count %d, must be at least %d", + ubifs_err(c, "too few main LEBs count %d, must be at least %d", c->main_lebs, UBIFS_MIN_MAIN_LEBS); goto failed; } max_bytes = (long long)c->leb_size * UBIFS_MIN_BUD_LEBS; if (c->max_bud_bytes < max_bytes) { - ubifs_err("too small journal (%lld bytes), must be at least %lld bytes", + ubifs_err(c, "too small journal (%lld bytes), must be at least %lld bytes", c->max_bud_bytes, max_bytes); goto failed; } max_bytes = (long long)c->leb_size * c->main_lebs; if (c->max_bud_bytes > max_bytes) { - ubifs_err("too large journal size (%lld bytes), only %lld bytes available in the main area", + ubifs_err(c, "too large journal size (%lld bytes), only %lld bytes available in the main area", c->max_bud_bytes, max_bytes); goto failed; } @@ -468,7 +468,7 @@ static int validate_sb(struct ubifs_info *c, struct ubifs_sb_node *sup) return 0; failed: - ubifs_err("bad superblock, error %d", err); + ubifs_err(c, "bad superblock, error %d", err); ubifs_dump_node(c, sup); return -EINVAL; } @@ -549,12 +549,12 @@ int ubifs_read_superblock(struct ubifs_info *c) ubifs_assert(!c->ro_media || c->ro_mount); if (!c->ro_mount || c->ro_compat_version > UBIFS_RO_COMPAT_VERSION) { - ubifs_err("on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", + ubifs_err(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", c->fmt_version, c->ro_compat_version, UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION); if (c->ro_compat_version <= UBIFS_RO_COMPAT_VERSION) { - ubifs_msg("only R/O mounting is possible"); + ubifs_msg(c, "only R/O mounting is possible"); err = -EROFS; } else err = -EINVAL; @@ -570,7 +570,7 @@ int ubifs_read_superblock(struct ubifs_info *c) } if (c->fmt_version < 3) { - ubifs_err("on-flash format version %d is not supported", + ubifs_err(c, "on-flash format version %d is not supported", c->fmt_version); err = -EINVAL; goto out; @@ -595,7 +595,7 @@ int ubifs_read_superblock(struct ubifs_info *c) c->key_len = UBIFS_SK_LEN; break; default: - ubifs_err("unsupported key format"); + ubifs_err(c, "unsupported key format"); err = -EINVAL; goto out; } @@ -785,7 +785,7 @@ int ubifs_fixup_free_space(struct ubifs_info *c) ubifs_assert(c->space_fixup); ubifs_assert(!c->ro_mount); - ubifs_msg("start fixing up free space"); + ubifs_msg(c, "start fixing up free space"); err = fixup_free_space(c); if (err) @@ -804,6 +804,6 @@ int ubifs_fixup_free_space(struct ubifs_info *c) if (err) return err; - ubifs_msg("free space fixup complete"); + ubifs_msg(c, "free space fixup complete"); return err; } diff --git a/fs/ubifs/scan.c b/fs/ubifs/scan.c index 89adbc4d08ac..aab87340d3de 100644 --- a/fs/ubifs/scan.c +++ b/fs/ubifs/scan.c @@ -100,7 +100,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, if (pad_len < 0 || offs + node_len + pad_len > c->leb_size) { if (!quiet) { - ubifs_err("bad pad node at LEB %d:%d", + ubifs_err(c, "bad pad node at LEB %d:%d", lnum, offs); ubifs_dump_node(c, pad); } @@ -110,7 +110,7 @@ int ubifs_scan_a_node(const struct ubifs_info *c, void *buf, int len, int lnum, /* Make the node pads to 8-byte boundary */ if ((node_len + pad_len) & 7) { if (!quiet) - ubifs_err("bad padding length %d - %d", + ubifs_err(c, "bad padding length %d - %d", offs, offs + node_len + pad_len); return SCANNED_A_BAD_PAD_NODE; } @@ -152,7 +152,7 @@ struct ubifs_scan_leb *ubifs_start_scan(const struct ubifs_info *c, int lnum, err = ubifs_leb_read(c, lnum, sbuf + offs, offs, c->leb_size - offs, 0); if (err && err != -EBADMSG) { - ubifs_err("cannot read %d bytes from LEB %d:%d, error %d", + ubifs_err(c, "cannot read %d bytes from LEB %d:%d, error %d", c->leb_size - offs, lnum, offs, err); kfree(sleb); return ERR_PTR(err); @@ -240,11 +240,11 @@ void ubifs_scanned_corruption(const struct ubifs_info *c, int lnum, int offs, { int len; - ubifs_err("corruption at LEB %d:%d", lnum, offs); + ubifs_err(c, "corruption at LEB %d:%d", lnum, offs); len = c->leb_size - offs; if (len > 8192) len = 8192; - ubifs_err("first %d bytes from LEB %d:%d", len, lnum, offs); + ubifs_err(c, "first %d bytes from LEB %d:%d", len, lnum, offs); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 4, buf, len, 1); } @@ -299,16 +299,16 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, switch (ret) { case SCANNED_GARBAGE: - ubifs_err("garbage"); + ubifs_err(c, "garbage"); goto corrupted; case SCANNED_A_NODE: break; case SCANNED_A_CORRUPT_NODE: case SCANNED_A_BAD_PAD_NODE: - ubifs_err("bad node"); + ubifs_err(c, "bad node"); goto corrupted; default: - ubifs_err("unknown"); + ubifs_err(c, "unknown"); err = -EINVAL; goto error; } @@ -325,7 +325,7 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, if (offs % c->min_io_size) { if (!quiet) - ubifs_err("empty space starts at non-aligned offset %d", + ubifs_err(c, "empty space starts at non-aligned offset %d", offs); goto corrupted; } @@ -338,7 +338,7 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, for (; len; offs++, buf++, len--) if (*(uint8_t *)buf != 0xff) { if (!quiet) - ubifs_err("corrupt empty space at LEB %d:%d", + ubifs_err(c, "corrupt empty space at LEB %d:%d", lnum, offs); goto corrupted; } @@ -348,14 +348,14 @@ struct ubifs_scan_leb *ubifs_scan(const struct ubifs_info *c, int lnum, corrupted: if (!quiet) { ubifs_scanned_corruption(c, lnum, offs, buf); - ubifs_err("LEB %d scanning failed", lnum); + ubifs_err(c, "LEB %d scanning failed", lnum); } err = -EUCLEAN; ubifs_scan_destroy(sleb); return ERR_PTR(err); error: - ubifs_err("LEB %d scanning failed, error %d", lnum, err); + ubifs_err(c, "LEB %d scanning failed, error %d", lnum, err); ubifs_scan_destroy(sleb); return ERR_PTR(err); } diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c index 93e946561c5c..75e6f04bb795 100644 --- a/fs/ubifs/super.c +++ b/fs/ubifs/super.c @@ -70,13 +70,13 @@ static int validate_inode(struct ubifs_info *c, const struct inode *inode) const struct ubifs_inode *ui = ubifs_inode(inode); if (inode->i_size > c->max_inode_sz) { - ubifs_err("inode is too large (%lld)", + ubifs_err(c, "inode is too large (%lld)", (long long)inode->i_size); return 1; } if (ui->compr_type >= UBIFS_COMPR_TYPES_CNT) { - ubifs_err("unknown compression type %d", ui->compr_type); + ubifs_err(c, "unknown compression type %d", ui->compr_type); return 2; } @@ -90,7 +90,7 @@ static int validate_inode(struct ubifs_info *c, const struct inode *inode) return 5; if (!ubifs_compr_present(ui->compr_type)) { - ubifs_warn("inode %lu uses '%s' compression, but it was not compiled in", + ubifs_warn(c, "inode %lu uses '%s' compression, but it was not compiled in", inode->i_ino, ubifs_compr_name(ui->compr_type)); } @@ -242,14 +242,14 @@ struct inode *ubifs_iget(struct super_block *sb, unsigned long inum) return inode; out_invalid: - ubifs_err("inode %lu validation failed, error %d", inode->i_ino, err); + ubifs_err(c, "inode %lu validation failed, error %d", inode->i_ino, err); ubifs_dump_node(c, ino); ubifs_dump_inode(c, inode); err = -EINVAL; out_ino: kfree(ino); out: - ubifs_err("failed to read inode %lu, error %d", inode->i_ino, err); + ubifs_err(c, "failed to read inode %lu, error %d", inode->i_ino, err); iget_failed(inode); return ERR_PTR(err); } @@ -319,7 +319,7 @@ static int ubifs_write_inode(struct inode *inode, struct writeback_control *wbc) if (inode->i_nlink) { err = ubifs_jnl_write_inode(c, inode); if (err) - ubifs_err("can't write inode %lu, error %d", + ubifs_err(c, "can't write inode %lu, error %d", inode->i_ino, err); else err = dbg_check_inode_size(c, inode, ui->ui_size); @@ -363,7 +363,7 @@ static void ubifs_evict_inode(struct inode *inode) * Worst case we have a lost orphan inode wasting space, so a * simple error message is OK here. */ - ubifs_err("can't delete inode %lu, error %d", + ubifs_err(c, "can't delete inode %lu, error %d", inode->i_ino, err); out: @@ -492,17 +492,17 @@ static int ubifs_sync_fs(struct super_block *sb, int wait) static int init_constants_early(struct ubifs_info *c) { if (c->vi.corrupted) { - ubifs_warn("UBI volume is corrupted - read-only mode"); + ubifs_warn(c, "UBI volume is corrupted - read-only mode"); c->ro_media = 1; } if (c->di.ro_mode) { - ubifs_msg("read-only UBI device"); + ubifs_msg(c, "read-only UBI device"); c->ro_media = 1; } if (c->vi.vol_type == UBI_STATIC_VOLUME) { - ubifs_msg("static UBI volume - read-only mode"); + ubifs_msg(c, "static UBI volume - read-only mode"); c->ro_media = 1; } @@ -516,19 +516,19 @@ static int init_constants_early(struct ubifs_info *c) c->max_write_shift = fls(c->max_write_size) - 1; if (c->leb_size < UBIFS_MIN_LEB_SZ) { - ubifs_err("too small LEBs (%d bytes), min. is %d bytes", + ubifs_err(c, "too small LEBs (%d bytes), min. is %d bytes", c->leb_size, UBIFS_MIN_LEB_SZ); return -EINVAL; } if (c->leb_cnt < UBIFS_MIN_LEB_CNT) { - ubifs_err("too few LEBs (%d), min. is %d", + ubifs_err(c, "too few LEBs (%d), min. is %d", c->leb_cnt, UBIFS_MIN_LEB_CNT); return -EINVAL; } if (!is_power_of_2(c->min_io_size)) { - ubifs_err("bad min. I/O size %d", c->min_io_size); + ubifs_err(c, "bad min. I/O size %d", c->min_io_size); return -EINVAL; } @@ -539,7 +539,7 @@ static int init_constants_early(struct ubifs_info *c) if (c->max_write_size < c->min_io_size || c->max_write_size % c->min_io_size || !is_power_of_2(c->max_write_size)) { - ubifs_err("bad write buffer size %d for %d min. I/O unit", + ubifs_err(c, "bad write buffer size %d for %d min. I/O unit", c->max_write_size, c->min_io_size); return -EINVAL; } @@ -665,7 +665,7 @@ static int init_constants_sb(struct ubifs_info *c) tmp = UBIFS_CS_NODE_SZ + UBIFS_REF_NODE_SZ * c->jhead_cnt; tmp = ALIGN(tmp, c->min_io_size); if (tmp > c->leb_size) { - ubifs_err("too small LEB size %d, at least %d needed", + ubifs_err(c, "too small LEB size %d, at least %d needed", c->leb_size, tmp); return -EINVAL; } @@ -680,7 +680,7 @@ static int init_constants_sb(struct ubifs_info *c) tmp /= c->leb_size; tmp += 1; if (c->log_lebs < tmp) { - ubifs_err("too small log %d LEBs, required min. %d LEBs", + ubifs_err(c, "too small log %d LEBs, required min. %d LEBs", c->log_lebs, tmp); return -EINVAL; } @@ -772,7 +772,7 @@ static int take_gc_lnum(struct ubifs_info *c) int err; if (c->gc_lnum == -1) { - ubifs_err("no LEB for GC"); + ubifs_err(c, "no LEB for GC"); return -EINVAL; } @@ -857,7 +857,7 @@ static void free_orphans(struct ubifs_info *c) orph = list_entry(c->orph_list.next, struct ubifs_orphan, list); list_del(&orph->list); kfree(orph); - ubifs_err("orphan list not empty at unmount"); + ubifs_err(c, "orphan list not empty at unmount"); } vfree(c->orph_buf); @@ -954,7 +954,8 @@ static const match_table_t tokens = { */ static int parse_standard_option(const char *option) { - ubifs_msg("parse %s", option); + + pr_notice("UBIFS: parse %s\n", option); if (!strcmp(option, "sync")) return MS_SYNCHRONOUS; return 0; @@ -1026,7 +1027,7 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, else if (!strcmp(name, "zlib")) c->mount_opts.compr_type = UBIFS_COMPR_ZLIB; else { - ubifs_err("unknown compressor \"%s\"", name); + ubifs_err(c, "unknown compressor \"%s\"", name); //FIXME: is c ready? kfree(name); return -EINVAL; } @@ -1042,7 +1043,7 @@ static int ubifs_parse_options(struct ubifs_info *c, char *options, flag = parse_standard_option(p); if (!flag) { - ubifs_err("unrecognized mount option \"%s\" or missing value", + ubifs_err(c, "unrecognized mount option \"%s\" or missing value", p); return -EINVAL; } @@ -1105,7 +1106,7 @@ again: } /* Just disable bulk-read */ - ubifs_warn("cannot allocate %d bytes of memory for bulk-read, disabling it", + ubifs_warn(c, "cannot allocate %d bytes of memory for bulk-read, disabling it", c->max_bu_buf_len); c->mount_opts.bulk_read = 1; c->bulk_read = 0; @@ -1124,7 +1125,7 @@ static int check_free_space(struct ubifs_info *c) { ubifs_assert(c->dark_wm > 0); if (c->lst.total_free + c->lst.total_dirty < c->dark_wm) { - ubifs_err("insufficient free space to mount in R/W mode"); + ubifs_err(c, "insufficient free space to mount in R/W mode"); ubifs_dump_budg(c, &c->bi); ubifs_dump_lprops(c); return -ENOSPC; @@ -1166,14 +1167,14 @@ static int mount_ubifs(struct ubifs_info *c) * This UBI volume is empty, and read-only, or the file system * is mounted read-only - we cannot format it. */ - ubifs_err("can't format empty UBI volume: read-only %s", + ubifs_err(c, "can't format empty UBI volume: read-only %s", c->ro_media ? "UBI volume" : "mount"); err = -EROFS; goto out_free; } if (c->ro_media && !c->ro_mount) { - ubifs_err("cannot mount read-write - read-only media"); + ubifs_err(c, "cannot mount read-write - read-only media"); err = -EROFS; goto out_free; } @@ -1221,7 +1222,7 @@ static int mount_ubifs(struct ubifs_info *c) * or overridden by mount options is actually compiled in. */ if (!ubifs_compr_present(c->default_compr)) { - ubifs_err("'compressor \"%s\" is not compiled in", + ubifs_err(c, "'compressor \"%s\" is not compiled in", ubifs_compr_name(c->default_compr)); err = -ENOTSUPP; goto out_free; @@ -1250,7 +1251,7 @@ static int mount_ubifs(struct ubifs_info *c) if (IS_ERR(c->bgt)) { err = PTR_ERR(c->bgt); c->bgt = NULL; - ubifs_err("cannot spawn \"%s\", error %d", + ubifs_err(c, "cannot spawn \"%s\", error %d", c->bgt_name, err); goto out_wbufs; } @@ -1264,7 +1265,7 @@ static int mount_ubifs(struct ubifs_info *c) init_constants_master(c); if ((c->mst_node->flags & cpu_to_le32(UBIFS_MST_DIRTY)) != 0) { - ubifs_msg("recovery needed"); + ubifs_msg(c, "recovery needed"); c->need_recovery = 1; } @@ -1284,7 +1285,7 @@ static int mount_ubifs(struct ubifs_info *c) goto out_lpt; } - if (!c->ro_mount) { + if (!c->ro_mount && !c->need_recovery) { /* * Set the "dirty" flag so that if we reboot uncleanly we * will notice this immediately on the next mount. @@ -1373,10 +1374,10 @@ static int mount_ubifs(struct ubifs_info *c) if (c->need_recovery) { if (c->ro_mount) - ubifs_msg("recovery deferred"); + ubifs_msg(c, "recovery deferred"); else { c->need_recovery = 0; - ubifs_msg("recovery completed"); + ubifs_msg(c, "recovery completed"); /* * GC LEB has to be empty and taken at this point. But * the journal head LEBs may also be accounted as @@ -1397,20 +1398,20 @@ static int mount_ubifs(struct ubifs_info *c) c->mounting = 0; - ubifs_msg("mounted UBI device %d, volume %d, name \"%s\"%s", + ubifs_msg(c, "UBIFS: mounted UBI device %d, volume %d, name \"%s\"%s", c->vi.ubi_num, c->vi.vol_id, c->vi.name, c->ro_mount ? ", R/O mode" : ""); x = (long long)c->main_lebs * c->leb_size; y = (long long)c->log_lebs * c->leb_size + c->max_bud_bytes; - ubifs_msg("LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes", + ubifs_msg(c, "LEB size: %d bytes (%d KiB), min./max. I/O unit sizes: %d bytes/%d bytes", c->leb_size, c->leb_size >> 10, c->min_io_size, c->max_write_size); - ubifs_msg("FS size: %lld bytes (%lld MiB, %d LEBs), journal size %lld bytes (%lld MiB, %d LEBs)", + ubifs_msg(c, "FS size: %lld bytes (%lld MiB, %d LEBs), journal size %lld bytes (%lld MiB, %d LEBs)", x, x >> 20, c->main_lebs, y, y >> 20, c->log_lebs + c->max_bud_cnt); - ubifs_msg("reserved for root: %llu bytes (%llu KiB)", + ubifs_msg(c, "reserved for root: %llu bytes (%llu KiB)", c->report_rp_size, c->report_rp_size >> 10); - ubifs_msg("media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s", + ubifs_msg(c, "media format: w%d/r%d (latest is w%d/r%d), UUID %pUB%s", c->fmt_version, c->ro_compat_version, UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION, c->uuid, c->big_lpt ? ", big LPT model" : ", small LPT model"); @@ -1543,8 +1544,8 @@ static int ubifs_remount_rw(struct ubifs_info *c) int err, lnum; if (c->rw_incompat) { - ubifs_err("the file-system is not R/W-compatible"); - ubifs_msg("on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", + ubifs_err(c, "the file-system is not R/W-compatible"); + ubifs_msg(c, "on-flash format version is w%d/r%d, but software only supports up to version w%d/r%d", c->fmt_version, c->ro_compat_version, UBIFS_FORMAT_VERSION, UBIFS_RO_COMPAT_VERSION); return -EROFS; @@ -1581,7 +1582,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) } if (c->need_recovery) { - ubifs_msg("completing deferred recovery"); + ubifs_msg(c, "completing deferred recovery"); err = ubifs_write_rcvrd_mst_node(c); if (err) goto out; @@ -1630,7 +1631,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (IS_ERR(c->bgt)) { err = PTR_ERR(c->bgt); c->bgt = NULL; - ubifs_err("cannot spawn \"%s\", error %d", + ubifs_err(c, "cannot spawn \"%s\", error %d", c->bgt_name, err); goto out; } @@ -1664,7 +1665,7 @@ static int ubifs_remount_rw(struct ubifs_info *c) if (c->need_recovery) { c->need_recovery = 0; - ubifs_msg("deferred recovery completed"); + ubifs_msg(c, "deferred recovery completed"); } else { /* * Do not run the debugging space check if the were doing @@ -1752,8 +1753,7 @@ static void ubifs_put_super(struct super_block *sb) int i; struct ubifs_info *c = sb->s_fs_info; - ubifs_msg("un-mount UBI device %d, volume %d", c->vi.ubi_num, - c->vi.vol_id); + ubifs_msg(c, "un-mount UBI device %d", c->vi.ubi_num); /* * The following asserts are only valid if there has not been a failure @@ -1809,7 +1809,7 @@ static void ubifs_put_super(struct super_block *sb) * next mount, so we just print a message and * continue to unmount normally. */ - ubifs_err("failed to write master node, error %d", + ubifs_err(c, "failed to write master node, error %d", err); } else { for (i = 0; i < c->jhead_cnt; i++) @@ -1834,17 +1834,17 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) err = ubifs_parse_options(c, data, 1); if (err) { - ubifs_err("invalid or unknown remount parameter"); + ubifs_err(c, "invalid or unknown remount parameter"); return err; } if (c->ro_mount && !(*flags & MS_RDONLY)) { if (c->ro_error) { - ubifs_msg("cannot re-mount R/W due to prior errors"); + ubifs_msg(c, "cannot re-mount R/W due to prior errors"); return -EROFS; } if (c->ro_media) { - ubifs_msg("cannot re-mount R/W - UBI volume is R/O"); + ubifs_msg(c, "cannot re-mount R/W - UBI volume is R/O"); return -EROFS; } err = ubifs_remount_rw(c); @@ -1852,7 +1852,7 @@ static int ubifs_remount_fs(struct super_block *sb, int *flags, char *data) return err; } else if (!c->ro_mount && (*flags & MS_RDONLY)) { if (c->ro_error) { - ubifs_msg("cannot re-mount R/O due to prior errors"); + ubifs_msg(c, "cannot re-mount R/O due to prior errors"); return -EROFS; } ubifs_remount_ro(c); @@ -2104,8 +2104,8 @@ static struct dentry *ubifs_mount(struct file_system_type *fs_type, int flags, */ ubi = open_ubi(name, UBI_READONLY); if (IS_ERR(ubi)) { - ubifs_err("cannot open \"%s\", error %d", - name, (int)PTR_ERR(ubi)); + pr_err("UBIFS error (pid: %d): cannot open \"%s\", error %d", + current->pid, name, (int)PTR_ERR(ubi)); return ERR_CAST(ubi); } @@ -2233,8 +2233,8 @@ static int __init ubifs_init(void) * UBIFS_BLOCK_SIZE. It is assumed that both are powers of 2. */ if (PAGE_CACHE_SIZE < UBIFS_BLOCK_SIZE) { - ubifs_err("VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes", - (unsigned int)PAGE_CACHE_SIZE); + pr_err("UBIFS error (pid %d): VFS page cache size is %u bytes, but UBIFS requires at least 4096 bytes", + current->pid, (unsigned int)PAGE_CACHE_SIZE); return -EINVAL; } @@ -2257,7 +2257,8 @@ static int __init ubifs_init(void) err = register_filesystem(&ubifs_fs_type); if (err) { - ubifs_err("cannot register file system, error %d", err); + pr_err("UBIFS error (pid %d): cannot register file system, error %d", + current->pid, err); goto out_dbg; } return 0; diff --git a/fs/ubifs/tnc.c b/fs/ubifs/tnc.c index 6793db0754f6..957f5757f374 100644 --- a/fs/ubifs/tnc.c +++ b/fs/ubifs/tnc.c @@ -98,7 +98,7 @@ static int insert_old_idx(struct ubifs_info *c, int lnum, int offs) else if (offs > o->offs) p = &(*p)->rb_right; else { - ubifs_err("old idx added twice!"); + ubifs_err(c, "old idx added twice!"); kfree(old_idx); return 0; } @@ -447,7 +447,7 @@ static int try_read_node(const struct ubifs_info *c, void *buf, int type, err = ubifs_leb_read(c, lnum, buf, offs, len, 1); if (err) { - ubifs_err("cannot read node type %d from LEB %d:%d, error %d", + ubifs_err(c, "cannot read node type %d from LEB %d:%d, error %d", type, lnum, offs, err); return err; } @@ -1684,27 +1684,27 @@ static int validate_data_node(struct ubifs_info *c, void *buf, int err, len; if (ch->node_type != UBIFS_DATA_NODE) { - ubifs_err("bad node type (%d but expected %d)", + ubifs_err(c, "bad node type (%d but expected %d)", ch->node_type, UBIFS_DATA_NODE); goto out_err; } err = ubifs_check_node(c, buf, zbr->lnum, zbr->offs, 0, 0); if (err) { - ubifs_err("expected node type %d", UBIFS_DATA_NODE); + ubifs_err(c, "expected node type %d", UBIFS_DATA_NODE); goto out; } len = le32_to_cpu(ch->len); if (len != zbr->len) { - ubifs_err("bad node length %d, expected %d", len, zbr->len); + ubifs_err(c, "bad node length %d, expected %d", len, zbr->len); goto out_err; } /* Make sure the key of the read node is correct */ key_read(c, buf + UBIFS_KEY_OFFSET, &key1); if (!keys_eq(c, &zbr->key, &key1)) { - ubifs_err("bad key in node at LEB %d:%d", + ubifs_err(c, "bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); dbg_tnck(&zbr->key, "looked for key "); dbg_tnck(&key1, "found node's key "); @@ -1716,7 +1716,7 @@ static int validate_data_node(struct ubifs_info *c, void *buf, out_err: err = -EINVAL; out: - ubifs_err("bad node at LEB %d:%d", zbr->lnum, zbr->offs); + ubifs_err(c, "bad node at LEB %d:%d", zbr->lnum, zbr->offs); ubifs_dump_node(c, buf); dump_stack(); return err; @@ -1741,7 +1741,7 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) len = bu->zbranch[bu->cnt - 1].offs; len += bu->zbranch[bu->cnt - 1].len - offs; if (len > bu->buf_len) { - ubifs_err("buffer too small %d vs %d", bu->buf_len, len); + ubifs_err(c, "buffer too small %d vs %d", bu->buf_len, len); return -EINVAL; } @@ -1757,7 +1757,7 @@ int ubifs_tnc_bulk_read(struct ubifs_info *c, struct bu_info *bu) return -EAGAIN; if (err && err != -EBADMSG) { - ubifs_err("failed to read from LEB %d:%d, error %d", + ubifs_err(c, "failed to read from LEB %d:%d, error %d", lnum, offs, err); dump_stack(); dbg_tnck(&bu->key, "key "); @@ -3313,7 +3313,7 @@ int dbg_check_inode_size(struct ubifs_info *c, const struct inode *inode, out_dump: block = key_block(c, key); - ubifs_err("inode %lu has size %lld, but there are data at offset %lld", + ubifs_err(c, "inode %lu has size %lld, but there are data at offset %lld", (unsigned long)inode->i_ino, size, ((loff_t)block) << UBIFS_BLOCK_SHIFT); mutex_unlock(&c->tnc_mutex); diff --git a/fs/ubifs/tnc_commit.c b/fs/ubifs/tnc_commit.c index 7a205e046776..b45345d701e7 100644 --- a/fs/ubifs/tnc_commit.c +++ b/fs/ubifs/tnc_commit.c @@ -53,7 +53,7 @@ static int make_idx_node(struct ubifs_info *c, struct ubifs_idx_node *idx, br->offs = cpu_to_le32(zbr->offs); br->len = cpu_to_le32(zbr->len); if (!zbr->lnum || !zbr->len) { - ubifs_err("bad ref in znode"); + ubifs_err(c, "bad ref in znode"); ubifs_dump_znode(c, znode); if (zbr->znode) ubifs_dump_znode(c, zbr->znode); @@ -384,7 +384,7 @@ static int layout_in_gaps(struct ubifs_info *c, int cnt) * Do not print scary warnings if the debugging * option which forces in-the-gaps is enabled. */ - ubifs_warn("out of space"); + ubifs_warn(c, "out of space"); ubifs_dump_budg(c, &c->bi); ubifs_dump_lprops(c); } @@ -441,7 +441,7 @@ static int layout_in_empty_space(struct ubifs_info *c) /* Determine the index node position */ if (lnum == -1) { if (c->ileb_nxt >= c->ileb_cnt) { - ubifs_err("out of space"); + ubifs_err(c, "out of space"); return -ENOSPC; } lnum = c->ilebs[c->ileb_nxt++]; @@ -855,7 +855,7 @@ static int write_index(struct ubifs_info *c) br->offs = cpu_to_le32(zbr->offs); br->len = cpu_to_le32(zbr->len); if (!zbr->lnum || !zbr->len) { - ubifs_err("bad ref in znode"); + ubifs_err(c, "bad ref in znode"); ubifs_dump_znode(c, znode); if (zbr->znode) ubifs_dump_znode(c, zbr->znode); @@ -875,7 +875,7 @@ static int write_index(struct ubifs_info *c) if (lnum != znode->lnum || offs != znode->offs || len != znode->len) { - ubifs_err("inconsistent znode posn"); + ubifs_err(c, "inconsistent znode posn"); return -EINVAL; } @@ -973,7 +973,7 @@ static int write_index(struct ubifs_info *c) if (lnum != c->dbg->new_ihead_lnum || buf_offs != c->dbg->new_ihead_offs) { - ubifs_err("inconsistent ihead"); + ubifs_err(c, "inconsistent ihead"); return -EINVAL; } diff --git a/fs/ubifs/tnc_misc.c b/fs/ubifs/tnc_misc.c index f6bf8995c7b1..93f5b7859e6f 100644 --- a/fs/ubifs/tnc_misc.c +++ b/fs/ubifs/tnc_misc.c @@ -293,9 +293,9 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, lnum, offs, znode->level, znode->child_cnt); if (znode->child_cnt > c->fanout || znode->level > UBIFS_MAX_LEVELS) { - ubifs_err("current fanout %d, branch count %d", + ubifs_err(c, "current fanout %d, branch count %d", c->fanout, znode->child_cnt); - ubifs_err("max levels %d, znode level %d", + ubifs_err(c, "max levels %d, znode level %d", UBIFS_MAX_LEVELS, znode->level); err = 1; goto out_dump; @@ -316,7 +316,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, if (zbr->lnum < c->main_first || zbr->lnum >= c->leb_cnt || zbr->offs < 0 || zbr->offs + zbr->len > c->leb_size || zbr->offs & 7) { - ubifs_err("bad branch %d", i); + ubifs_err(c, "bad branch %d", i); err = 2; goto out_dump; } @@ -328,7 +328,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, case UBIFS_XENT_KEY: break; default: - ubifs_err("bad key type at slot %d: %d", + ubifs_err(c, "bad key type at slot %d: %d", i, key_type(c, &zbr->key)); err = 3; goto out_dump; @@ -340,17 +340,17 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, type = key_type(c, &zbr->key); if (c->ranges[type].max_len == 0) { if (zbr->len != c->ranges[type].len) { - ubifs_err("bad target node (type %d) length (%d)", + ubifs_err(c, "bad target node (type %d) length (%d)", type, zbr->len); - ubifs_err("have to be %d", c->ranges[type].len); + ubifs_err(c, "have to be %d", c->ranges[type].len); err = 4; goto out_dump; } } else if (zbr->len < c->ranges[type].min_len || zbr->len > c->ranges[type].max_len) { - ubifs_err("bad target node (type %d) length (%d)", + ubifs_err(c, "bad target node (type %d) length (%d)", type, zbr->len); - ubifs_err("have to be in range of %d-%d", + ubifs_err(c, "have to be in range of %d-%d", c->ranges[type].min_len, c->ranges[type].max_len); err = 5; @@ -370,12 +370,12 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, cmp = keys_cmp(c, key1, key2); if (cmp > 0) { - ubifs_err("bad key order (keys %d and %d)", i, i + 1); + ubifs_err(c, "bad key order (keys %d and %d)", i, i + 1); err = 6; goto out_dump; } else if (cmp == 0 && !is_hash_key(c, key1)) { /* These can only be keys with colliding hash */ - ubifs_err("keys %d and %d are not hashed but equivalent", + ubifs_err(c, "keys %d and %d are not hashed but equivalent", i, i + 1); err = 7; goto out_dump; @@ -386,7 +386,7 @@ static int read_znode(struct ubifs_info *c, int lnum, int offs, int len, return 0; out_dump: - ubifs_err("bad indexing node at LEB %d:%d, error %d", lnum, offs, err); + ubifs_err(c, "bad indexing node at LEB %d:%d, error %d", lnum, offs, err); ubifs_dump_node(c, idx); kfree(idx); return -EINVAL; @@ -482,7 +482,7 @@ int ubifs_tnc_read_node(struct ubifs_info *c, struct ubifs_zbranch *zbr, /* Make sure the key of the read node is correct */ key_read(c, node + UBIFS_KEY_OFFSET, &key1); if (!keys_eq(c, key, &key1)) { - ubifs_err("bad key in node at LEB %d:%d", + ubifs_err(c, "bad key in node at LEB %d:%d", zbr->lnum, zbr->offs); dbg_tnck(key, "looked for key "); dbg_tnck(&key1, "but found node's key "); diff --git a/fs/ubifs/ubifs.h b/fs/ubifs/ubifs.h index bc04b9c69891..de759022f3d6 100644 --- a/fs/ubifs/ubifs.h +++ b/fs/ubifs/ubifs.h @@ -43,15 +43,19 @@ #define UBIFS_VERSION 1 /* Normal UBIFS messages */ -#define ubifs_msg(fmt, ...) pr_notice("UBIFS: " fmt "\n", ##__VA_ARGS__) +#define ubifs_msg(c, fmt, ...) \ + pr_notice("UBIFS (ubi%d:%d): " fmt "\n", \ + (c)->vi.ubi_num, (c)->vi.vol_id, ##__VA_ARGS__) /* UBIFS error messages */ -#define ubifs_err(fmt, ...) \ - pr_err("UBIFS error (pid %d): %s: " fmt "\n", current->pid, \ +#define ubifs_err(c, fmt, ...) \ + pr_err("UBIFS error (ubi%d:%d pid %d): %s: " fmt "\n", \ + (c)->vi.ubi_num, (c)->vi.vol_id, current->pid, \ __func__, ##__VA_ARGS__) /* UBIFS warning messages */ -#define ubifs_warn(fmt, ...) \ - pr_warn("UBIFS warning (pid %d): %s: " fmt "\n", \ - current->pid, __func__, ##__VA_ARGS__) +#define ubifs_warn(c, fmt, ...) \ + pr_warn("UBIFS warning (ubi%d:%d pid %d): %s: " fmt "\n", \ + (c)->vi.ubi_num, (c)->vi.vol_id, current->pid, \ + __func__, ##__VA_ARGS__) /* * A variant of 'ubifs_err()' which takes the UBIFS file-sytem description * object as an argument. @@ -59,7 +63,7 @@ #define ubifs_errc(c, fmt, ...) \ do { \ if (!(c)->probing) \ - ubifs_err(fmt, ##__VA_ARGS__); \ + ubifs_err(c, fmt, ##__VA_ARGS__); \ } while (0) /* UBIFS file system VFS magic number */ @@ -158,7 +162,7 @@ #define WORST_COMPR_FACTOR 2 /* - * How much memory is needed for a buffer where we comress a data node. + * How much memory is needed for a buffer where we compress a data node. */ #define COMPRESSED_DATA_NODE_BUF_SZ \ (UBIFS_DATA_NODE_SZ + UBIFS_BLOCK_SIZE * WORST_COMPR_FACTOR) @@ -664,7 +668,7 @@ typedef int (*ubifs_lpt_scan_callback)(struct ubifs_info *c, * @lock: serializes @buf, @lnum, @offs, @avail, @used, @next_ino and @inodes * fields * @softlimit: soft write-buffer timeout interval - * @delta: hard and soft timeouts delta (the timer expire inteval is @softlimit + * @delta: hard and soft timeouts delta (the timer expire interval is @softlimit * and @softlimit + @delta) * @timer: write-buffer timer * @no_timer: non-zero if this write-buffer does not have a timer @@ -930,9 +934,9 @@ struct ubifs_orphan { /** * struct ubifs_mount_opts - UBIFS-specific mount options information. * @unmount_mode: selected unmount mode (%0 default, %1 normal, %2 fast) - * @bulk_read: enable/disable bulk-reads (%0 default, %1 disabe, %2 enable) + * @bulk_read: enable/disable bulk-reads (%0 default, %1 disable, %2 enable) * @chk_data_crc: enable/disable CRC data checking when reading data nodes - * (%0 default, %1 disabe, %2 enable) + * (%0 default, %1 disable, %2 enable) * @override_compr: override default compressor (%0 - do not override and use * superblock compressor, %1 - override and use compressor * specified in @compr_type) @@ -962,9 +966,9 @@ struct ubifs_mount_opts { * optimization) * @nospace_rp: the same as @nospace, but additionally means that even reserved * pool is full - * @page_budget: budget for a page (constant, nenver changed after mount) - * @inode_budget: budget for an inode (constant, nenver changed after mount) - * @dent_budget: budget for a directory entry (constant, nenver changed after + * @page_budget: budget for a page (constant, never changed after mount) + * @inode_budget: budget for an inode (constant, never changed after mount) + * @dent_budget: budget for a directory entry (constant, never changed after * mount) */ struct ubifs_budg_info { @@ -1787,10 +1791,10 @@ long ubifs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); /* compressor.c */ int __init ubifs_compressors_init(void); void ubifs_compressors_exit(void); -void ubifs_compress(const void *in_buf, int in_len, void *out_buf, int *out_len, - int *compr_type); -int ubifs_decompress(const void *buf, int len, void *out, int *out_len, - int compr_type); +void ubifs_compress(const struct ubifs_info *c, const void *in_buf, int in_len, + void *out_buf, int *out_len, int *compr_type); +int ubifs_decompress(const struct ubifs_info *c, const void *buf, int len, + void *out, int *out_len, int compr_type); #include "debug.h" #include "misc.h" diff --git a/fs/ubifs/xattr.c b/fs/ubifs/xattr.c index a92be244a6fb..3659b1934500 100644 --- a/fs/ubifs/xattr.c +++ b/fs/ubifs/xattr.c @@ -108,7 +108,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, .dirtied_ino_d = ALIGN(host_ui->data_len, 8) }; if (host_ui->xattr_cnt >= MAX_XATTRS_PER_INODE) { - ubifs_err("inode %lu already has too many xattrs (%d), cannot create more", + ubifs_err(c, "inode %lu already has too many xattrs (%d), cannot create more", host->i_ino, host_ui->xattr_cnt); return -ENOSPC; } @@ -120,7 +120,7 @@ static int create_xattr(struct ubifs_info *c, struct inode *host, */ names_len = host_ui->xattr_names + host_ui->xattr_cnt + nm->len + 1; if (names_len > XATTR_LIST_MAX) { - ubifs_err("cannot add one more xattr name to inode %lu, total names length would become %d, max. is %d", + ubifs_err(c, "cannot add one more xattr name to inode %lu, total names length would become %d, max. is %d", host->i_ino, names_len, XATTR_LIST_MAX); return -ENOSPC; } @@ -288,13 +288,13 @@ static struct inode *iget_xattr(struct ubifs_info *c, ino_t inum) inode = ubifs_iget(c->vfs_sb, inum); if (IS_ERR(inode)) { - ubifs_err("dead extended attribute entry, error %d", + ubifs_err(c, "dead extended attribute entry, error %d", (int)PTR_ERR(inode)); return inode; } if (ubifs_inode(inode)->xattr) return inode; - ubifs_err("corrupt extended attribute entry"); + ubifs_err(c, "corrupt extended attribute entry"); iput(inode); return ERR_PTR(-EINVAL); } @@ -412,7 +412,7 @@ ssize_t ubifs_getxattr(struct dentry *dentry, const char *name, void *buf, if (buf) { /* If @buf is %NULL we are supposed to return the length */ if (ui->data_len > size) { - ubifs_err("buffer size %zd, xattr len %d", + ubifs_err(c, "buffer size %zd, xattr len %d", size, ui->data_len); err = -ERANGE; goto out_iput; @@ -485,7 +485,7 @@ ssize_t ubifs_listxattr(struct dentry *dentry, char *buffer, size_t size) kfree(pxent); if (err != -ENOENT) { - ubifs_err("cannot find next direntry, error %d", err); + ubifs_err(c, "cannot find next direntry, error %d", err); return err; } @@ -657,8 +657,10 @@ int ubifs_init_security(struct inode *dentry, struct inode *inode, &init_xattrs, 0); mutex_unlock(&inode->i_mutex); - if (err) - ubifs_err("cannot initialize security for inode %lu, error %d", + if (err) { + struct ubifs_info *c = dentry->i_sb->s_fs_info; + ubifs_err(c, "cannot initialize security for inode %lu, error %d", inode->i_ino, err); + } return err; } diff --git a/fs/udf/file.c b/fs/udf/file.c index 7f885cc8b0b7..74050bff64f4 100644 --- a/fs/udf/file.c +++ b/fs/udf/file.c @@ -240,12 +240,10 @@ static int udf_release_file(struct inode *inode, struct file *filp) } const struct file_operations udf_file_operations = { - .read = new_sync_read, .read_iter = generic_file_read_iter, .unlocked_ioctl = udf_ioctl, .open = generic_file_open, .mmap = generic_file_mmap, - .write = new_sync_write, .write_iter = udf_file_write_iter, .release = udf_release_file, .fsync = generic_file_fsync, diff --git a/fs/ufs/file.c b/fs/ufs/file.c index c84ec010a676..042ddbf110cc 100644 --- a/fs/ufs/file.c +++ b/fs/ufs/file.c @@ -35,9 +35,7 @@ const struct file_operations ufs_file_operations = { .llseek = generic_file_llseek, - .read = new_sync_read, .read_iter = generic_file_read_iter, - .write = new_sync_write, .write_iter = generic_file_write_iter, .mmap = generic_file_mmap, .open = generic_file_open, diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index f44212fae653..44856c3b9617 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1386,8 +1386,6 @@ xfs_file_llseek( const struct file_operations xfs_file_operations = { .llseek = xfs_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = xfs_file_read_iter, .write_iter = xfs_file_write_iter, .splice_read = xfs_file_splice_read, diff --git a/include/crypto/algapi.h b/include/crypto/algapi.h index 623a59c1ff5a..0ecb7688af71 100644 --- a/include/crypto/algapi.h +++ b/include/crypto/algapi.h @@ -137,7 +137,7 @@ struct crypto_template *crypto_lookup_template(const char *name); int crypto_register_instance(struct crypto_template *tmpl, struct crypto_instance *inst); -int crypto_unregister_instance(struct crypto_alg *alg); +int crypto_unregister_instance(struct crypto_instance *inst); int crypto_init_spawn(struct crypto_spawn *spawn, struct crypto_alg *alg, struct crypto_instance *inst, u32 mask); diff --git a/include/crypto/rng.h b/include/crypto/rng.h index a16fb10142bf..6e28ea5be9f1 100644 --- a/include/crypto/rng.h +++ b/include/crypto/rng.h @@ -103,8 +103,7 @@ static inline void crypto_free_rng(struct crypto_rng *tfm) * This function fills the caller-allocated buffer with random numbers using the * random number generator referenced by the cipher handle. * - * Return: > 0 function was successful and returns the number of generated - * bytes; < 0 if an error occurred + * Return: 0 function was successful; < 0 if an error occurred */ static inline int crypto_rng_get_bytes(struct crypto_rng *tfm, u8 *rdata, unsigned int dlen) diff --git a/include/crypto/sha.h b/include/crypto/sha.h index 190f8a0e0242..dd7905a3c22e 100644 --- a/include/crypto/sha.h +++ b/include/crypto/sha.h @@ -65,20 +65,20 @@ #define SHA512_H7 0x5be0cd19137e2179ULL struct sha1_state { - u64 count; u32 state[SHA1_DIGEST_SIZE / 4]; + u64 count; u8 buffer[SHA1_BLOCK_SIZE]; }; struct sha256_state { - u64 count; u32 state[SHA256_DIGEST_SIZE / 4]; + u64 count; u8 buf[SHA256_BLOCK_SIZE]; }; struct sha512_state { - u64 count[2]; u64 state[SHA512_DIGEST_SIZE / 8]; + u64 count[2]; u8 buf[SHA512_BLOCK_SIZE]; }; @@ -87,9 +87,18 @@ struct shash_desc; extern int crypto_sha1_update(struct shash_desc *desc, const u8 *data, unsigned int len); +extern int crypto_sha1_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); + extern int crypto_sha256_update(struct shash_desc *desc, const u8 *data, unsigned int len); +extern int crypto_sha256_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); + extern int crypto_sha512_update(struct shash_desc *desc, const u8 *data, unsigned int len); + +extern int crypto_sha512_finup(struct shash_desc *desc, const u8 *data, + unsigned int len, u8 *hash); #endif diff --git a/include/crypto/sha1_base.h b/include/crypto/sha1_base.h new file mode 100644 index 000000000000..d0df431f9a97 --- /dev/null +++ b/include/crypto/sha1_base.h @@ -0,0 +1,106 @@ +/* + * sha1_base.h - core logic for SHA-1 implementations + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <linux/crypto.h> +#include <linux/module.h> + +#include <asm/unaligned.h> + +typedef void (sha1_block_fn)(struct sha1_state *sst, u8 const *src, int blocks); + +static inline int sha1_base_init(struct shash_desc *desc) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA1_H0; + sctx->state[1] = SHA1_H1; + sctx->state[2] = SHA1_H2; + sctx->state[3] = SHA1_H3; + sctx->state[4] = SHA1_H4; + sctx->count = 0; + + return 0; +} + +static inline int sha1_base_do_update(struct shash_desc *desc, + const u8 *data, + unsigned int len, + sha1_block_fn *block_fn) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; + + sctx->count += len; + + if (unlikely((partial + len) >= SHA1_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA1_BLOCK_SIZE - partial; + + memcpy(sctx->buffer + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buffer, 1); + } + + blocks = len / SHA1_BLOCK_SIZE; + len %= SHA1_BLOCK_SIZE; + + if (blocks) { + block_fn(sctx, data, blocks); + data += blocks * SHA1_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buffer + partial, data, len); + + return 0; +} + +static inline int sha1_base_do_finalize(struct shash_desc *desc, + sha1_block_fn *block_fn) +{ + const int bit_offset = SHA1_BLOCK_SIZE - sizeof(__be64); + struct sha1_state *sctx = shash_desc_ctx(desc); + __be64 *bits = (__be64 *)(sctx->buffer + bit_offset); + unsigned int partial = sctx->count % SHA1_BLOCK_SIZE; + + sctx->buffer[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buffer + partial, 0x0, SHA1_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buffer, 1); + } + + memset(sctx->buffer + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buffer, 1); + + return 0; +} + +static inline int sha1_base_finish(struct shash_desc *desc, u8 *out) +{ + struct sha1_state *sctx = shash_desc_ctx(desc); + __be32 *digest = (__be32 *)out; + int i; + + for (i = 0; i < SHA1_DIGEST_SIZE / sizeof(__be32); i++) + put_unaligned_be32(sctx->state[i], digest++); + + *sctx = (struct sha1_state){}; + return 0; +} diff --git a/include/crypto/sha256_base.h b/include/crypto/sha256_base.h new file mode 100644 index 000000000000..d1f2195bb7de --- /dev/null +++ b/include/crypto/sha256_base.h @@ -0,0 +1,128 @@ +/* + * sha256_base.h - core logic for SHA-256 implementations + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <linux/crypto.h> +#include <linux/module.h> + +#include <asm/unaligned.h> + +typedef void (sha256_block_fn)(struct sha256_state *sst, u8 const *src, + int blocks); + +static inline int sha224_base_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA224_H0; + sctx->state[1] = SHA224_H1; + sctx->state[2] = SHA224_H2; + sctx->state[3] = SHA224_H3; + sctx->state[4] = SHA224_H4; + sctx->state[5] = SHA224_H5; + sctx->state[6] = SHA224_H6; + sctx->state[7] = SHA224_H7; + sctx->count = 0; + + return 0; +} + +static inline int sha256_base_init(struct shash_desc *desc) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA256_H0; + sctx->state[1] = SHA256_H1; + sctx->state[2] = SHA256_H2; + sctx->state[3] = SHA256_H3; + sctx->state[4] = SHA256_H4; + sctx->state[5] = SHA256_H5; + sctx->state[6] = SHA256_H6; + sctx->state[7] = SHA256_H7; + sctx->count = 0; + + return 0; +} + +static inline int sha256_base_do_update(struct shash_desc *desc, + const u8 *data, + unsigned int len, + sha256_block_fn *block_fn) +{ + struct sha256_state *sctx = shash_desc_ctx(desc); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->count += len; + + if (unlikely((partial + len) >= SHA256_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA256_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buf, 1); + } + + blocks = len / SHA256_BLOCK_SIZE; + len %= SHA256_BLOCK_SIZE; + + if (blocks) { + block_fn(sctx, data, blocks); + data += blocks * SHA256_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buf + partial, data, len); + + return 0; +} + +static inline int sha256_base_do_finalize(struct shash_desc *desc, + sha256_block_fn *block_fn) +{ + const int bit_offset = SHA256_BLOCK_SIZE - sizeof(__be64); + struct sha256_state *sctx = shash_desc_ctx(desc); + __be64 *bits = (__be64 *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count % SHA256_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA256_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + *bits = cpu_to_be64(sctx->count << 3); + block_fn(sctx, sctx->buf, 1); + + return 0; +} + +static inline int sha256_base_finish(struct shash_desc *desc, u8 *out) +{ + unsigned int digest_size = crypto_shash_digestsize(desc->tfm); + struct sha256_state *sctx = shash_desc_ctx(desc); + __be32 *digest = (__be32 *)out; + int i; + + for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be32)) + put_unaligned_be32(sctx->state[i], digest++); + + *sctx = (struct sha256_state){}; + return 0; +} diff --git a/include/crypto/sha512_base.h b/include/crypto/sha512_base.h new file mode 100644 index 000000000000..6c5341e005ea --- /dev/null +++ b/include/crypto/sha512_base.h @@ -0,0 +1,131 @@ +/* + * sha512_base.h - core logic for SHA-512 implementations + * + * Copyright (C) 2015 Linaro Ltd <ard.biesheuvel@linaro.org> + * + * 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 <crypto/internal/hash.h> +#include <crypto/sha.h> +#include <linux/crypto.h> +#include <linux/module.h> + +#include <asm/unaligned.h> + +typedef void (sha512_block_fn)(struct sha512_state *sst, u8 const *src, + int blocks); + +static inline int sha384_base_init(struct shash_desc *desc) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA384_H0; + sctx->state[1] = SHA384_H1; + sctx->state[2] = SHA384_H2; + sctx->state[3] = SHA384_H3; + sctx->state[4] = SHA384_H4; + sctx->state[5] = SHA384_H5; + sctx->state[6] = SHA384_H6; + sctx->state[7] = SHA384_H7; + sctx->count[0] = sctx->count[1] = 0; + + return 0; +} + +static inline int sha512_base_init(struct shash_desc *desc) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + + sctx->state[0] = SHA512_H0; + sctx->state[1] = SHA512_H1; + sctx->state[2] = SHA512_H2; + sctx->state[3] = SHA512_H3; + sctx->state[4] = SHA512_H4; + sctx->state[5] = SHA512_H5; + sctx->state[6] = SHA512_H6; + sctx->state[7] = SHA512_H7; + sctx->count[0] = sctx->count[1] = 0; + + return 0; +} + +static inline int sha512_base_do_update(struct shash_desc *desc, + const u8 *data, + unsigned int len, + sha512_block_fn *block_fn) +{ + struct sha512_state *sctx = shash_desc_ctx(desc); + unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE; + + sctx->count[0] += len; + if (sctx->count[0] < len) + sctx->count[1]++; + + if (unlikely((partial + len) >= SHA512_BLOCK_SIZE)) { + int blocks; + + if (partial) { + int p = SHA512_BLOCK_SIZE - partial; + + memcpy(sctx->buf + partial, data, p); + data += p; + len -= p; + + block_fn(sctx, sctx->buf, 1); + } + + blocks = len / SHA512_BLOCK_SIZE; + len %= SHA512_BLOCK_SIZE; + + if (blocks) { + block_fn(sctx, data, blocks); + data += blocks * SHA512_BLOCK_SIZE; + } + partial = 0; + } + if (len) + memcpy(sctx->buf + partial, data, len); + + return 0; +} + +static inline int sha512_base_do_finalize(struct shash_desc *desc, + sha512_block_fn *block_fn) +{ + const int bit_offset = SHA512_BLOCK_SIZE - sizeof(__be64[2]); + struct sha512_state *sctx = shash_desc_ctx(desc); + __be64 *bits = (__be64 *)(sctx->buf + bit_offset); + unsigned int partial = sctx->count[0] % SHA512_BLOCK_SIZE; + + sctx->buf[partial++] = 0x80; + if (partial > bit_offset) { + memset(sctx->buf + partial, 0x0, SHA512_BLOCK_SIZE - partial); + partial = 0; + + block_fn(sctx, sctx->buf, 1); + } + + memset(sctx->buf + partial, 0x0, bit_offset - partial); + bits[0] = cpu_to_be64(sctx->count[1] << 3 | sctx->count[0] >> 61); + bits[1] = cpu_to_be64(sctx->count[0] << 3); + block_fn(sctx, sctx->buf, 1); + + return 0; +} + +static inline int sha512_base_finish(struct shash_desc *desc, u8 *out) +{ + unsigned int digest_size = crypto_shash_digestsize(desc->tfm); + struct sha512_state *sctx = shash_desc_ctx(desc); + __be64 *digest = (__be64 *)out; + int i; + + for (i = 0; digest_size > 0; i++, digest_size -= sizeof(__be64)) + put_unaligned_be64(sctx->state[i], digest++); + + *sctx = (struct sha512_state){}; + return 0; +} diff --git a/include/dt-bindings/leds/common.h b/include/dt-bindings/leds/common.h new file mode 100644 index 000000000000..79fcef72ef57 --- /dev/null +++ b/include/dt-bindings/leds/common.h @@ -0,0 +1,21 @@ +/* + * This header provides macros for the common LEDs device tree bindings. + * + * Copyright (C) 2015, Samsung Electronics Co., Ltd. + * + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + */ + +#ifndef __DT_BINDINGS_LEDS_H__ +#define __DT_BINDINGS_LEDS_H + +/* External trigger type */ +#define LEDS_TRIG_TYPE_EDGE 0 +#define LEDS_TRIG_TYPE_LEVEL 1 + +/* Boost modes */ +#define LEDS_BOOST_OFF 0 +#define LEDS_BOOST_ADAPTIVE 1 +#define LEDS_BOOST_FIXED 2 + +#endif /* __DT_BINDINGS_LEDS_H */ diff --git a/include/linux/crypto.h b/include/linux/crypto.h index fb5ef16d6a12..10df5d2d093a 100644 --- a/include/linux/crypto.h +++ b/include/linux/crypto.h @@ -95,6 +95,12 @@ #define CRYPTO_ALG_KERN_DRIVER_ONLY 0x00001000 /* + * Mark a cipher as a service implementation only usable by another + * cipher and never by a normal user of the kernel crypto API + */ +#define CRYPTO_ALG_INTERNAL 0x00002000 + +/* * Transform masks and values (for crt_flags). */ #define CRYPTO_TFM_REQ_MASK 0x000fff00 diff --git a/include/linux/fs.h b/include/linux/fs.h index 0f696328f218..f4fc60727b8d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -915,8 +915,8 @@ struct file_lock_operations { struct lock_manager_operations { int (*lm_compare_owner)(struct file_lock *, struct file_lock *); unsigned long (*lm_owner_key)(struct file_lock *); - void (*lm_get_owner)(struct file_lock *, struct file_lock *); - void (*lm_put_owner)(struct file_lock *); + fl_owner_t (*lm_get_owner)(fl_owner_t); + void (*lm_put_owner)(fl_owner_t); void (*lm_notify)(struct file_lock *); /* unblock callback */ int (*lm_grant)(struct file_lock *, int); bool (*lm_break)(struct file_lock *); @@ -1562,8 +1562,6 @@ struct file_operations { loff_t (*llseek) (struct file *, loff_t, int); ssize_t (*read) (struct file *, char __user *, size_t, loff_t *); ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *); - ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t); - ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t); ssize_t (*read_iter) (struct kiocb *, struct iov_iter *); ssize_t (*write_iter) (struct kiocb *, struct iov_iter *); int (*iterate) (struct file *, struct dir_context *); @@ -1639,6 +1637,7 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector, struct iovec **ret_pointer); extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *); +extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *); extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *); extern ssize_t vfs_readv(struct file *, const struct iovec __user *, @@ -2573,10 +2572,6 @@ extern ssize_t __generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_write_iter(struct kiocb *, struct iov_iter *); extern ssize_t generic_file_direct_write(struct kiocb *, struct iov_iter *, loff_t); extern ssize_t generic_perform_write(struct file *, struct iov_iter *, loff_t); -extern ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); -extern ssize_t do_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); -extern ssize_t new_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos); -extern ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos); ssize_t vfs_iter_read(struct file *file, struct iov_iter *iter, loff_t *ppos); ssize_t vfs_iter_write(struct file *file, struct iov_iter *iter, loff_t *ppos); diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h index eb7b414d232b..4f7d8f4b1e9a 100644 --- a/include/linux/hw_random.h +++ b/include/linux/hw_random.h @@ -50,10 +50,14 @@ struct hwrng { struct completion cleanup_done; }; +struct device; + /** Register a new Hardware Random Number Generator driver. */ extern int hwrng_register(struct hwrng *rng); +extern int devm_hwrng_register(struct device *dev, struct hwrng *rng); /** Unregister a Hardware Random Number Generator driver. */ extern void hwrng_unregister(struct hwrng *rng); +extern void devm_hwrng_unregister(struct device *dve, struct hwrng *rng); /** Feed random bits into the pool. */ extern void add_hwgenerator_randomness(const char *buffer, size_t count, size_t entropy); diff --git a/include/linux/led-class-flash.h b/include/linux/led-class-flash.h index 5ba2facd7a51..e97966d1fb8d 100644 --- a/include/linux/led-class-flash.h +++ b/include/linux/led-class-flash.h @@ -13,7 +13,6 @@ #define __LINUX_FLASH_LEDS_H_INCLUDED #include <linux/leds.h> -#include <uapi/linux/v4l2-controls.h> struct device_node; struct led_classdev_flash; @@ -33,7 +32,7 @@ struct led_classdev_flash; #define LED_FAULT_LED_OVER_TEMPERATURE (1 << 8) #define LED_NUM_FLASH_FAULTS 9 -#define LED_FLASH_MAX_SYSFS_GROUPS 7 +#define LED_FLASH_SYSFS_GROUPS_SIZE 5 struct led_flash_ops { /* set flash brightness */ @@ -81,21 +80,7 @@ struct led_classdev_flash { struct led_flash_setting timeout; /* LED Flash class sysfs groups */ - const struct attribute_group *sysfs_groups[LED_FLASH_MAX_SYSFS_GROUPS]; - - /* LEDs available for flash strobe synchronization */ - struct led_classdev_flash **sync_leds; - - /* Number of LEDs available for flash strobe synchronization */ - int num_sync_leds; - - /* - * The identifier of the sub-led to synchronize the flash strobe with. - * Identifiers start from 1, which reflects the first element from the - * sync_leds array. 0 means that the flash strobe should not be - * synchronized. - */ - u32 sync_led_id; + const struct attribute_group *sysfs_groups[LED_FLASH_SYSFS_GROUPS_SIZE]; }; static inline struct led_classdev_flash *lcdev_to_flcdev( diff --git a/include/linux/leds.h b/include/linux/leds.h index f70f84f35674..9a2b000094cf 100644 --- a/include/linux/leds.h +++ b/include/linux/leds.h @@ -47,7 +47,6 @@ struct led_classdev { #define SET_BRIGHTNESS_ASYNC (1 << 21) #define SET_BRIGHTNESS_SYNC (1 << 22) #define LED_DEV_CAP_FLASH (1 << 23) -#define LED_DEV_CAP_SYNC_STROBE (1 << 24) /* Set LED brightness level */ /* Must not sleep, use a workqueue if needed */ @@ -105,7 +104,11 @@ struct led_classdev { extern int led_classdev_register(struct device *parent, struct led_classdev *led_cdev); +extern int devm_led_classdev_register(struct device *parent, + struct led_classdev *led_cdev); extern void led_classdev_unregister(struct led_classdev *led_cdev); +extern void devm_led_classdev_unregister(struct device *parent, + struct led_classdev *led_cdev); extern void led_classdev_suspend(struct led_classdev *led_cdev); extern void led_classdev_resume(struct led_classdev *led_cdev); diff --git a/include/linux/personality.h b/include/linux/personality.h index 646c0a7d50fa..aeb7892b2468 100644 --- a/include/linux/personality.h +++ b/include/linux/personality.h @@ -3,52 +3,14 @@ #include <uapi/linux/personality.h> - -/* - * Handling of different ABIs (personalities). - */ - -struct exec_domain; -struct pt_regs; - -extern int register_exec_domain(struct exec_domain *); -extern int unregister_exec_domain(struct exec_domain *); -extern int __set_personality(unsigned int); - - -/* - * Description of an execution domain. - * - * The first two members are refernced from assembly source - * and should stay where they are unless explicitly needed. - */ -typedef void (*handler_t)(int, struct pt_regs *); - -struct exec_domain { - const char *name; /* name of the execdomain */ - handler_t handler; /* handler for syscalls */ - unsigned char pers_low; /* lowest personality */ - unsigned char pers_high; /* highest personality */ - unsigned long *signal_map; /* signal mapping */ - unsigned long *signal_invmap; /* reverse signal mapping */ - struct map_segment *err_map; /* error mapping */ - struct map_segment *socktype_map; /* socket type mapping */ - struct map_segment *sockopt_map; /* socket option mapping */ - struct map_segment *af_map; /* address family mapping */ - struct module *module; /* module context of the ed. */ - struct exec_domain *next; /* linked list (internal) */ -}; - /* * Return the base personality without flags. */ #define personality(pers) (pers & PER_MASK) - /* * Change personality of the currently running process. */ -#define set_personality(pers) \ - ((current->personality == (pers)) ? 0 : __set_personality(pers)) +#define set_personality(pers) (current->personality = (pers)) #endif /* _LINUX_PERSONALITY_H */ diff --git a/include/linux/platform_data/tpm_stm_st33.h b/include/linux/platform_data/st33zp24.h index ff75310c0f47..817dfdb37885 100644 --- a/include/linux/platform_data/tpm_stm_st33.h +++ b/include/linux/platform_data/st33zp24.h @@ -1,6 +1,6 @@ /* - * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24 - * Copyright (C) 2009, 2010 STMicroelectronics + * STMicroelectronics TPM Linux driver for TPM 1.2 ST33ZP24 + * Copyright (C) 2009 - 2015 STMicroelectronics * * 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 @@ -14,20 +14,9 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <http://www.gnu.org/licenses/>. - * - * STMicroelectronics version 1.2.0, Copyright (C) 2010 - * STMicroelectronics comes with ABSOLUTELY NO WARRANTY. - * This is free software, and you are welcome to redistribute it - * under certain conditions. - * - * @Author: Christophe RICARD tpmsupport@st.com - * - * @File: stm_st33_tpm.h - * - * @Date: 09/15/2010 */ -#ifndef __STM_ST33_TPM_H__ -#define __STM_ST33_TPM_H__ +#ifndef __ST33ZP24_H__ +#define __ST33ZP24_H__ #define TPM_ST33_I2C "st33zp24-i2c" #define TPM_ST33_SPI "st33zp24-spi" @@ -36,4 +25,4 @@ struct st33zp24_platform_data { int io_lpcpd; }; -#endif /* __STM_ST33_TPM_H__ */ +#endif /* __ST33ZP24_H__ */ diff --git a/include/linux/sched.h b/include/linux/sched.h index f74d4cc3a3e5..8222ae40ecb0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -125,7 +125,6 @@ struct sched_attr { u64 sched_period; }; -struct exec_domain; struct futex_pi_state; struct robust_list_head; struct bio_list; @@ -2311,11 +2310,6 @@ extern void set_curr_task(int cpu, struct task_struct *p); void yield(void); -/* - * The default (Linux) execution domain. - */ -extern struct exec_domain default_exec_domain; - union thread_union { struct thread_info thread_info; unsigned long stack[THREAD_SIZE/sizeof(long)]; diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 6fab66c5c5af..c6b97e58cf84 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h @@ -211,6 +211,8 @@ struct p9_dirent { char d_name[256]; }; +struct iov_iter; + int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, const char *name); @@ -236,10 +238,8 @@ int p9_client_clunk(struct p9_fid *fid); int p9_client_fsync(struct p9_fid *fid, int datasync); int p9_client_remove(struct p9_fid *fid); int p9_client_unlinkat(struct p9_fid *dfid, const char *name, int flags); -int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, - u64 offset, u32 count); -int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, - u64 offset, u32 count); +int p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err); +int p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err); int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset); int p9dirent_read(struct p9_client *clnt, char *buf, int len, struct p9_dirent *dirent); diff --git a/include/net/9p/transport.h b/include/net/9p/transport.h index 2a25dec30211..5122b5e40f78 100644 --- a/include/net/9p/transport.h +++ b/include/net/9p/transport.h @@ -61,7 +61,7 @@ struct p9_trans_module { int (*cancel) (struct p9_client *, struct p9_req_t *req); int (*cancelled)(struct p9_client *, struct p9_req_t *req); int (*zc_request)(struct p9_client *, struct p9_req_t *, - char *, char *, int , int, int, int); + struct iov_iter *, struct iov_iter *, int , int, int); }; void v9fs_register_trans(struct p9_trans_module *m); diff --git a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h index d315a08d6c6d..0e9d75b49bed 100644 --- a/include/sound/ac97_codec.h +++ b/include/sound/ac97_codec.h @@ -608,7 +608,9 @@ struct ac97_quirk { int type; /* quirk type above */ }; -int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override); +int snd_ac97_tune_hardware(struct snd_ac97 *ac97, + const struct ac97_quirk *quirk, + const char *override); int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate); /* diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index f48089d364c5..fa1d05512c09 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -70,7 +70,7 @@ struct snd_compr_runtime { * @device: device pointer * @direction: stream direction, playback/recording * @metadata_set: metadata set flag, true when set - * @next_track: has userspace signall next track transistion, true when set + * @next_track: has userspace signal next track transition, true when set * @private_data: pointer to DSP private data */ struct snd_compr_stream { @@ -95,7 +95,7 @@ struct snd_compr_stream { * and the stream properties * @get_params: retrieve the codec parameters, mandatory * @set_metadata: Set the metadata values for a stream - * @get_metadata: retreives the requested metadata values from stream + * @get_metadata: retrieves the requested metadata values from stream * @trigger: Trigger operations like start, pause, resume, drain, stop. * This callback is mandatory * @pointer: Retrieve current h/w pointer information. Mandatory diff --git a/include/sound/control.h b/include/sound/control.h index 75f3054023f7..95aad6d3fd1a 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -227,7 +227,7 @@ snd_ctl_add_slave(struct snd_kcontrol *master, struct snd_kcontrol *slave) * 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. + * at each time queried 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, diff --git a/include/sound/core.h b/include/sound/core.h index da5748289968..b12931f513f4 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -278,7 +278,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, void *device_data, struct snd_device_ops *ops); int snd_device_register(struct snd_card *card, void *device_data); int snd_device_register_all(struct snd_card *card); -int snd_device_disconnect_all(struct snd_card *card); +void snd_device_disconnect(struct snd_card *card, void *device_data); +void snd_device_disconnect_all(struct snd_card *card); void snd_device_free(struct snd_card *card, void *device_data); void snd_device_free_all(struct snd_card *card); diff --git a/include/sound/hda_regmap.h b/include/sound/hda_regmap.h new file mode 100644 index 000000000000..53a18b3635e2 --- /dev/null +++ b/include/sound/hda_regmap.h @@ -0,0 +1,217 @@ +/* + * HD-audio regmap helpers + */ + +#ifndef __SOUND_HDA_REGMAP_H +#define __SOUND_HDA_REGMAP_H + +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/hdaudio.h> + +int snd_hdac_regmap_init(struct hdac_device *codec); +void snd_hdac_regmap_exit(struct hdac_device *codec); +int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, + unsigned int verb); +int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, + unsigned int *val); +int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, + unsigned int val); +int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val); + +/** + * snd_hdac_regmap_encode_verb - encode the verb to a pseudo register + * @nid: widget NID + * @verb: codec verb + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_verb(nid, verb) \ + (((verb) << 8) | 0x80000 | ((unsigned int)(nid) << 20)) + +/** + * snd_hdac_regmap_encode_amp - encode the AMP verb to a pseudo register + * @nid: widget NID + * @ch: channel (left = 0, right = 1) + * @dir: direction (#HDA_INPUT, #HDA_OUTPUT) + * @idx: input index value + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_amp(nid, ch, dir, idx) \ + (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \ + ((ch) ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT) | \ + ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \ + (idx)) + +/** + * snd_hdac_regmap_encode_amp_stereo - encode a pseudo register for stereo AMPs + * @nid: widget NID + * @dir: direction (#HDA_INPUT, #HDA_OUTPUT) + * @idx: input index value + * + * Returns an encoded pseudo register. + */ +#define snd_hdac_regmap_encode_amp_stereo(nid, dir, idx) \ + (snd_hdac_regmap_encode_verb(nid, AC_VERB_GET_AMP_GAIN_MUTE) | \ + AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT | /* both bits set! */ \ + ((dir) == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT) | \ + (idx)) + +/** + * snd_hdac_regmap_write - Write a verb with caching + * @nid: codec NID + * @reg: verb to write + * @val: value to write + * + * For writing an amp value, use snd_hda_regmap_amp_update(). + */ +static inline int +snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_write_raw(codec, cmd, val); +} + +/** + * snd_hda_regmap_update - Update a verb value with caching + * @nid: codec NID + * @verb: verb to update + * @mask: bit mask to update + * @val: value to update + * + * For updating an amp value, use snd_hda_regmap_amp_update(). + */ +static inline int +snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int mask, + unsigned int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + +/** + * snd_hda_regmap_read - Read a verb with caching + * @nid: codec NID + * @verb: verb to read + * @val: pointer to store the value + * + * For reading an amp value, use snd_hda_regmap_get_amp(). + */ +static inline int +snd_hdac_regmap_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int *val) +{ + unsigned int cmd = snd_hdac_regmap_encode_verb(nid, verb); + + return snd_hdac_regmap_read_raw(codec, cmd, val); +} + +/** + * snd_hdac_regmap_get_amp - Read AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * @val: the pointer to store the value + * + * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. + * Returns the value or a negative error. + */ +static inline int +snd_hdac_regmap_get_amp(struct hdac_device *codec, hda_nid_t nid, + int ch, int dir, int idx) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + int err, val; + + err = snd_hdac_regmap_read_raw(codec, cmd, &val); + return err < 0 ? err : val; +} + +/** + * snd_hdac_regmap_update_amp - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + */ +static inline int +snd_hdac_regmap_update_amp(struct hdac_device *codec, hda_nid_t nid, + int ch, int dir, int idx, int mask, int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp(nid, ch, dir, idx); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + +/** + * snd_hdac_regmap_get_amp_stereo - Read stereo AMP values + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @index: the index value (only for input direction) + * @val: the pointer to store the value + * + * Read stereo AMP values. The lower byte is left, the upper byte is right. + * Returns the value or a negative error. + */ +static inline int +snd_hdac_regmap_get_amp_stereo(struct hdac_device *codec, hda_nid_t nid, + int dir, int idx) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx); + int err, val; + + err = snd_hdac_regmap_read_raw(codec, cmd, &val); + return err < 0 ? err : val; +} + +/** + * snd_hdac_regmap_update_amp_stereo - update the stereo AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the stereo AMP value with a bit mask. + * The lower byte is left, the upper byte is right. + * Returns 0 if the value is unchanged, 1 if changed, or a negative error. + */ +static inline int +snd_hdac_regmap_update_amp_stereo(struct hdac_device *codec, hda_nid_t nid, + int dir, int idx, int mask, int val) +{ + unsigned int cmd = snd_hdac_regmap_encode_amp_stereo(nid, dir, idx); + + return snd_hdac_regmap_update_raw(codec, cmd, mask, val); +} + +/** + * snd_hdac_regmap_sync_node - sync the widget node attributes + * @codec: HD-audio codec + * @nid: NID to sync + */ +static inline void +snd_hdac_regmap_sync_node(struct hdac_device *codec, hda_nid_t nid) +{ + regcache_mark_dirty(codec->regmap); + regcache_sync_region(codec->regmap, nid << 20, ((nid + 1) << 20) - 1); +} + +#endif /* __SOUND_HDA_REGMAP_H */ diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h new file mode 100644 index 000000000000..2a8aa9dfb83d --- /dev/null +++ b/include/sound/hdaudio.h @@ -0,0 +1,247 @@ +/* + * HD-audio core stuff + */ + +#ifndef __SOUND_HDAUDIO_H +#define __SOUND_HDAUDIO_H + +#include <linux/device.h> +#include <sound/hda_verbs.h> + +/* codec node id */ +typedef u16 hda_nid_t; + +struct hdac_bus; +struct hdac_device; +struct hdac_driver; +struct hdac_widget_tree; + +/* + * exported bus type + */ +extern struct bus_type snd_hda_bus_type; + +/* + * generic arrays + */ +struct snd_array { + unsigned int used; + unsigned int alloced; + unsigned int elem_size; + unsigned int alloc_align; + void *list; +}; + +/* + * HD-audio codec base device + */ +struct hdac_device { + struct device dev; + int type; + struct hdac_bus *bus; + unsigned int addr; /* codec address */ + struct list_head list; /* list point for bus codec_list */ + + hda_nid_t afg; /* AFG node id */ + hda_nid_t mfg; /* MFG node id */ + + /* ids */ + unsigned int vendor_id; + unsigned int subsystem_id; + unsigned int revision_id; + unsigned int afg_function_id; + unsigned int mfg_function_id; + unsigned int afg_unsol:1; + unsigned int mfg_unsol:1; + + unsigned int power_caps; /* FG power caps */ + + const char *vendor_name; /* codec vendor name */ + const char *chip_name; /* codec chip name */ + + /* verb exec op override */ + int (*exec_verb)(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res); + + /* widgets */ + unsigned int num_nodes; + hda_nid_t start_nid, end_nid; + + /* misc flags */ + atomic_t in_pm; /* suspend/resume being performed */ + + /* sysfs */ + struct hdac_widget_tree *widgets; + + /* regmap */ + struct regmap *regmap; + struct snd_array vendor_verbs; + bool lazy_cache:1; /* don't wake up for writes */ + bool caps_overwriting:1; /* caps overwrite being in process */ + bool cache_coef:1; /* cache COEF read/write too */ +}; + +/* device/driver type used for matching */ +enum { + HDA_DEV_CORE, + HDA_DEV_LEGACY, +}; + +/* direction */ +enum { + HDA_INPUT, HDA_OUTPUT +}; + +#define dev_to_hdac_dev(_dev) container_of(_dev, struct hdac_device, dev) + +int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus, + const char *name, unsigned int addr); +void snd_hdac_device_exit(struct hdac_device *dev); +int snd_hdac_device_register(struct hdac_device *codec); +void snd_hdac_device_unregister(struct hdac_device *codec); + +int snd_hdac_refresh_widgets(struct hdac_device *codec); + +unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm); +int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, + unsigned int flags, unsigned int *res); +int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res); +int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, + unsigned int *res); +int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, + int parm); +int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid, + unsigned int parm, unsigned int val); +int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns); +int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *start_id); + +/** + * snd_hdac_read_parm - read a codec parameter + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use _snd_hdac_read_parm() directly. + */ +static inline int snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, + int parm) +{ + unsigned int val; + + return _snd_hdac_read_parm(codec, nid, parm, &val) < 0 ? -1 : val; +} + +#ifdef CONFIG_PM +void snd_hdac_power_up(struct hdac_device *codec); +void snd_hdac_power_down(struct hdac_device *codec); +void snd_hdac_power_up_pm(struct hdac_device *codec); +void snd_hdac_power_down_pm(struct hdac_device *codec); +#else +static inline void snd_hdac_power_up(struct hdac_device *codec) {} +static inline void snd_hdac_power_down(struct hdac_device *codec) {} +static inline void snd_hdac_power_up_pm(struct hdac_device *codec) {} +static inline void snd_hdac_power_down_pm(struct hdac_device *codec) {} +#endif + +/* + * HD-audio codec base driver + */ +struct hdac_driver { + struct device_driver driver; + int type; + int (*match)(struct hdac_device *dev, struct hdac_driver *drv); + void (*unsol_event)(struct hdac_device *dev, unsigned int event); +}; + +#define drv_to_hdac_driver(_drv) container_of(_drv, struct hdac_driver, driver) + +/* + * HD-audio bus base driver + */ +struct hdac_bus_ops { + /* send a single command */ + int (*command)(struct hdac_bus *bus, unsigned int cmd); + /* get a response from the last command */ + int (*get_response)(struct hdac_bus *bus, unsigned int addr, + unsigned int *res); +}; + +#define HDA_UNSOL_QUEUE_SIZE 64 + +struct hdac_bus { + struct device *dev; + const struct hdac_bus_ops *ops; + + /* codec linked list */ + struct list_head codec_list; + unsigned int num_codecs; + + /* link caddr -> codec */ + struct hdac_device *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; + + /* unsolicited event queue */ + u32 unsol_queue[HDA_UNSOL_QUEUE_SIZE * 2]; /* ring buffer */ + unsigned int unsol_rp, unsol_wp; + struct work_struct unsol_work; + + /* bit flags of powered codecs */ + unsigned long codec_powered; + + /* flags */ + bool sync_write:1; /* sync after verb write */ + + /* locks */ + struct mutex cmd_mutex; +}; + +int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_bus_ops *ops); +void snd_hdac_bus_exit(struct hdac_bus *bus); +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); +int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res); +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex); + +int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec); +void snd_hdac_bus_remove_device(struct hdac_bus *bus, + struct hdac_device *codec); + +static inline void snd_hdac_codec_link_up(struct hdac_device *codec) +{ + set_bit(codec->addr, &codec->bus->codec_powered); +} + +static inline void snd_hdac_codec_link_down(struct hdac_device *codec) +{ + clear_bit(codec->addr, &codec->bus->codec_powered); +} + +/* + * generic array helpers + */ +void *snd_array_new(struct snd_array *array); +void snd_array_free(struct snd_array *array); +static inline void snd_array_init(struct snd_array *array, unsigned int size, + unsigned int align) +{ + array->elem_size = size; + array->alloc_align = align; +} + +static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) +{ + return array->list + idx * array->elem_size; +} + +static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) +{ + return (unsigned long)(ptr - array->list) / array->elem_size; +} + +#endif /* __SOUND_HDAUDIO_H */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index c0ddb7e69c28..0cb7f3f5df7b 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -60,6 +60,9 @@ struct snd_pcm_hardware { struct snd_pcm_substream; +struct snd_pcm_audio_tstamp_config; /* definitions further down */ +struct snd_pcm_audio_tstamp_report; + struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); @@ -71,8 +74,10 @@ struct snd_pcm_ops { int (*prepare)(struct snd_pcm_substream *substream); int (*trigger)(struct snd_pcm_substream *substream, int cmd); snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); - int (*wall_clock)(struct snd_pcm_substream *substream, - struct timespec *audio_ts); + int (*get_time_info)(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report); int (*copy)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count); @@ -281,6 +286,58 @@ struct snd_pcm_hw_constraint_ranges { struct snd_pcm_hwptr_log; +/* + * userspace-provided audio timestamp config to kernel, + * structure is for internal use only and filled with dedicated unpack routine + */ +struct snd_pcm_audio_tstamp_config { + /* 5 of max 16 bits used */ + u32 type_requested:4; + u32 report_delay:1; /* add total delay to A/D or D/A */ +}; + +static inline void snd_pcm_unpack_audio_tstamp_config(__u32 data, + struct snd_pcm_audio_tstamp_config *config) +{ + config->type_requested = data & 0xF; + config->report_delay = (data >> 4) & 1; +} + +/* + * kernel-provided audio timestamp report to user-space + * structure is for internal use only and read by dedicated pack routine + */ +struct snd_pcm_audio_tstamp_report { + /* 6 of max 16 bits used for bit-fields */ + + /* for backwards compatibility */ + u32 valid:1; + + /* actual type if hardware could not support requested timestamp */ + u32 actual_type:4; + + /* accuracy represented in ns units */ + u32 accuracy_report:1; /* 0 if accuracy unknown, 1 if accuracy field is valid */ + u32 accuracy; /* up to 4.29s, will be packed in separate field */ +}; + +static inline void snd_pcm_pack_audio_tstamp_report(__u32 *data, __u32 *accuracy, + const struct snd_pcm_audio_tstamp_report *report) +{ + u32 tmp; + + tmp = report->accuracy_report; + tmp <<= 4; + tmp |= report->actual_type; + tmp <<= 1; + tmp |= report->valid; + + *data &= 0xffff; /* zero-clear MSBs */ + *data |= (tmp << 16); + *accuracy = report->accuracy; +} + + struct snd_pcm_runtime { /* -- Status -- */ struct snd_pcm_substream *trigger_master; @@ -361,6 +418,11 @@ struct snd_pcm_runtime { struct snd_dma_buffer *dma_buffer_p; /* allocated buffer */ + /* -- audio timestamp config -- */ + struct snd_pcm_audio_tstamp_config audio_tstamp_config; + struct snd_pcm_audio_tstamp_report audio_tstamp_report; + struct timespec driver_tstamp; + #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE) /* -- OSS things -- */ struct snd_pcm_oss_runtime oss; diff --git a/include/sound/pcm_params.h b/include/sound/pcm_params.h index 3c45f3924ba7..c704357775fc 100644 --- a/include/sound/pcm_params.h +++ b/include/sound/pcm_params.h @@ -366,4 +366,11 @@ static inline int params_physical_width(const struct snd_pcm_hw_params *p) return snd_pcm_format_physical_width(params_format(p)); } +static inline void +params_set_format(struct snd_pcm_hw_params *p, snd_pcm_format_t fmt) +{ + snd_mask_set(hw_param_mask(p, SNDRV_PCM_HW_PARAM_FORMAT), + (__force int)fmt); +} + #endif /* __SOUND_PCM_PARAMS_H */ diff --git a/include/sound/rt5670.h b/include/sound/rt5670.h index bd311197a3b5..b7d60510819b 100644 --- a/include/sound/rt5670.h +++ b/include/sound/rt5670.h @@ -14,6 +14,7 @@ struct rt5670_platform_data { int jd_mode; bool in2_diff; + bool dev_gpio; bool dmic_en; unsigned int dmic1_data_pin; diff --git a/include/sound/seq_device.h b/include/sound/seq_device.h index 2b5f24cc7548..ddc0d504cf39 100644 --- a/include/sound/seq_device.h +++ b/include/sound/seq_device.h @@ -25,29 +25,26 @@ * registered device information */ -#define ID_LEN 32 - -/* status flag */ -#define SNDRV_SEQ_DEVICE_FREE 0 -#define SNDRV_SEQ_DEVICE_REGISTERED 1 - struct snd_seq_device { /* device info */ struct snd_card *card; /* sound card */ int device; /* device number */ - char id[ID_LEN]; /* driver id */ + const char *id; /* driver id */ char name[80]; /* device name */ int argsize; /* size of the argument */ void *driver_data; /* private data for driver */ - int status; /* flag - read only */ void *private_data; /* private data for the caller */ void (*private_free)(struct snd_seq_device *device); - struct list_head list; /* link to next device */ + struct device dev; }; +#define to_seq_dev(_dev) \ + container_of(_dev, struct snd_seq_device, dev) + +/* sequencer driver */ /* driver operators - * init_device: + * probe: * Initialize the device with given parameters. * Typically, * 1. call snd_hwdep_new @@ -55,25 +52,40 @@ struct snd_seq_device { * 3. call snd_hwdep_register * 4. store the instance to dev->driver_data pointer. * - * free_device: + * remove: * Release the private data. * Typically, call snd_device_free(dev->card, dev->driver_data) */ -struct snd_seq_dev_ops { - int (*init_device)(struct snd_seq_device *dev); - int (*free_device)(struct snd_seq_device *dev); +struct snd_seq_driver { + struct device_driver driver; + char *id; + int argsize; }; +#define to_seq_drv(_drv) \ + container_of(_drv, struct snd_seq_driver, driver) + /* * prototypes */ +#ifdef CONFIG_MODULES void snd_seq_device_load_drivers(void); -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, struct snd_seq_device **result); -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, int argsize); -int snd_seq_device_unregister_driver(char *id); +#else +#define snd_seq_device_load_drivers() +#endif +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result); #define SNDRV_SEQ_DEVICE_ARGPTR(dev) (void *)((char *)(dev) + sizeof(struct snd_seq_device)) +int __must_check __snd_seq_driver_register(struct snd_seq_driver *drv, + struct module *mod); +#define snd_seq_driver_register(drv) \ + __snd_seq_driver_register(drv, THIS_MODULE) +void snd_seq_driver_unregister(struct snd_seq_driver *drv); + +#define module_snd_seq_driver(drv) \ + module_driver(drv, snd_seq_driver_register, snd_seq_driver_unregister) /* * id strings for generic devices diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index 18a2ac58b88f..feb58d455560 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -99,13 +99,9 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp, int snd_seq_event_port_detach(int client, int port); #ifdef CONFIG_MODULES -void snd_seq_autoload_lock(void); -void snd_seq_autoload_unlock(void); void snd_seq_autoload_init(void); -#define snd_seq_autoload_exit() snd_seq_autoload_lock() +void snd_seq_autoload_exit(void); #else -#define snd_seq_autoload_lock() -#define snd_seq_autoload_unlock() #define snd_seq_autoload_init() #define snd_seq_autoload_exit() #endif diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 1255ddb1d3e2..b9b4f289fe6b 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -16,7 +16,6 @@ struct asoc_simple_dai { const char *name; - unsigned int fmt; unsigned int sysclk; int slots; int slot_width; diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 8d7416e46861..0bc83647d3fa 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -378,6 +378,7 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card); void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card); int snd_soc_dapm_new_pcm(struct snd_soc_card *card, const struct snd_soc_pcm_stream *params, + unsigned int num_params, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink); @@ -440,7 +441,6 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card); int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget_list **list); -struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol); struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( struct snd_kcontrol *kcontrol); @@ -531,6 +531,8 @@ struct snd_soc_dapm_widget { void *priv; /* widget specific data */ struct regulator *regulator; /* attached regulator */ const struct snd_soc_pcm_stream *params; /* params for dai links */ + unsigned int num_params; /* number of params for dai links */ + unsigned int params_select; /* currently selected param for dai link */ /* dapm control */ int reg; /* negative reg = no direct dapm */ @@ -586,8 +588,6 @@ struct snd_soc_dapm_update { /* DAPM context */ struct snd_soc_dapm_context { enum snd_soc_bias_level bias_level; - enum snd_soc_bias_level suspend_bias_level; - struct delayed_work delayed_work; unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */ /* Go to BIAS_OFF in suspend if the DAPM context is idle */ unsigned int suspend_bias_off:1; diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h index 98f2ade0266e..806059052bfc 100644 --- a/include/sound/soc-dpcm.h +++ b/include/sound/soc-dpcm.h @@ -135,7 +135,7 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream, /* internal use only */ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute); -int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); +void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd); int soc_dpcm_runtime_update(struct snd_soc_card *); int dpcm_path_get(struct snd_soc_pcm_runtime *fe, diff --git a/include/sound/soc.h b/include/sound/soc.h index 0d1ade195628..fcb312b3f258 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -450,8 +450,10 @@ int soc_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai); /* Jack reporting */ -int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type, - struct snd_soc_jack *jack); +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins); + 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); @@ -659,7 +661,7 @@ struct snd_soc_jack_gpio { struct snd_soc_jack { struct mutex mutex; struct snd_jack *jack; - struct snd_soc_codec *codec; + struct snd_soc_card *card; struct list_head pins; int status; struct blocking_notifier_head notifier; @@ -941,6 +943,7 @@ struct snd_soc_dai_link { int be_id; /* optional ID for machine driver BE identification */ const struct snd_soc_pcm_stream *params; + unsigned int num_params; unsigned int dai_fmt; /* format to set on init */ @@ -954,6 +957,9 @@ struct snd_soc_dai_link { unsigned int symmetric_channels:1; unsigned int symmetric_samplebits:1; + /* Mark this pcm with non atomic ops */ + bool nonatomic; + /* Do not create a PCM for this DAI link (Backend link) */ unsigned int no_pcm:1; @@ -1071,11 +1077,16 @@ struct snd_soc_card { /* * Card-specific routes and widgets. + * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in. */ const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; + const struct snd_soc_dapm_widget *of_dapm_widgets; + int num_of_dapm_widgets; + const struct snd_soc_dapm_route *of_dapm_routes; + int num_of_dapm_routes; bool fully_routed; struct work_struct deferred_resume_work; @@ -1258,6 +1269,19 @@ static inline struct snd_soc_dapm_context *snd_soc_component_get_dapm( return component->dapm_ptr; } +/** + * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol + * @kcontrol: The kcontrol + * + * This function must only be used on DAPM contexts that are known to be part of + * a CODEC (e.g. in a CODEC driver). Otherwise the behavior is undefined. + */ +static inline struct snd_soc_codec *snd_soc_dapm_kcontrol_codec( + struct snd_kcontrol *kcontrol) +{ + return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); +} + /* codec IO */ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg); int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg, @@ -1469,7 +1493,7 @@ static inline struct snd_soc_codec *snd_soc_kcontrol_codec( } /** - * snd_soc_kcontrol_platform() - Returns the platform that registerd the control + * snd_soc_kcontrol_platform() - Returns the platform that registered the control * @kcontrol: The control for which to get the platform * * Note: This function will only work correctly if the control has been diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h index 09c8a00ea503..5a5fa4956ebd 100644 --- a/include/uapi/sound/asequencer.h +++ b/include/uapi/sound/asequencer.h @@ -22,6 +22,7 @@ #ifndef _UAPI__SOUND_ASEQUENCER_H #define _UAPI__SOUND_ASEQUENCER_H +#include <sound/asound.h> /** version of the sequencer */ #define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h index 0e88e7a0f0eb..46145a5277fe 100644 --- a/include/uapi/sound/asound.h +++ b/include/uapi/sound/asound.h @@ -25,6 +25,9 @@ #include <linux/types.h> +#ifndef __KERNEL__ +#include <stdlib.h> +#endif /* * protocol version @@ -140,7 +143,7 @@ struct snd_hwdep_dsp_image { * * *****************************************************************************/ -#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 12) +#define SNDRV_PCM_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 13) typedef unsigned long snd_pcm_uframes_t; typedef signed long snd_pcm_sframes_t; @@ -267,10 +270,17 @@ typedef int __bitwise snd_pcm_subformat_t; #define SNDRV_PCM_INFO_JOINT_DUPLEX 0x00200000 /* playback and capture stream are somewhat correlated */ #define SNDRV_PCM_INFO_SYNC_START 0x00400000 /* pcm support some kind of sync go */ #define SNDRV_PCM_INFO_NO_PERIOD_WAKEUP 0x00800000 /* period wakeup can be disabled */ -#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_WALL_CLOCK 0x01000000 /* (Deprecated)has audio wall clock for audio/system time sync */ +#define SNDRV_PCM_INFO_HAS_LINK_ATIME 0x01000000 /* report hardware link audio time, reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ABSOLUTE_ATIME 0x02000000 /* report absolute hardware link audio time, not reset on startup */ +#define SNDRV_PCM_INFO_HAS_LINK_ESTIMATED_ATIME 0x04000000 /* report estimated link audio time */ +#define SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME 0x08000000 /* report synchronized audio/system time */ + #define SNDRV_PCM_INFO_DRAIN_TRIGGER 0x40000000 /* internal kernel flag - trigger in drain */ #define SNDRV_PCM_INFO_FIFO_IN_FRAMES 0x80000000 /* internal kernel flag - FIFO size is in frames */ + + typedef int __bitwise snd_pcm_state_t; #define SNDRV_PCM_STATE_OPEN ((__force snd_pcm_state_t) 0) /* stream is open */ #define SNDRV_PCM_STATE_SETUP ((__force snd_pcm_state_t) 1) /* stream has a setup */ @@ -408,6 +418,22 @@ struct snd_pcm_channel_info { unsigned int step; /* samples distance in bits */ }; +enum { + /* + * first definition for backwards compatibility only, + * maps to wallclock/link time for HDAudio playback and DEFAULT/DMA time for everything else + */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT = 0, + + /* timestamp definitions */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT = 1, /* DMA time, reported as per hw_ptr */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK = 2, /* link time reported by sample or wallclock counter, reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ABSOLUTE = 3, /* link time reported by sample or wallclock counter, not reset on startup */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_ESTIMATED = 4, /* link time estimated indirectly */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED = 5, /* link time synchronized with system time */ + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LAST = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED +}; + struct snd_pcm_status { snd_pcm_state_t state; /* stream state */ struct timespec trigger_tstamp; /* time when stream was started/stopped/paused */ @@ -419,9 +445,11 @@ struct snd_pcm_status { snd_pcm_uframes_t avail_max; /* max frames available on hw since last status */ snd_pcm_uframes_t overrange; /* count of ADC (capture) overrange detections from last status */ snd_pcm_state_t suspended_state; /* suspended stream state */ - __u32 reserved_alignment; /* must be filled with zero */ - struct timespec audio_tstamp; /* from sample counter or wall clock */ - unsigned char reserved[56-sizeof(struct timespec)]; /* must be filled with zero */ + __u32 audio_tstamp_data; /* needed for 64-bit alignment, used for configs/report to/from userspace */ + struct timespec audio_tstamp; /* sample counter, wall clock, PHC or on-demand sync'ed */ + struct timespec driver_tstamp; /* useful in case reference system tstamp is reported with delay */ + __u32 audio_tstamp_accuracy; /* in ns units, only valid if indicated in audio_tstamp_data */ + unsigned char reserved[52-2*sizeof(struct timespec)]; /* must be filled with zero */ }; struct snd_pcm_mmap_status { @@ -534,6 +562,7 @@ enum { #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_STATUS_EXT _IOWR('A', 0x24, struct snd_pcm_status) #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) diff --git a/include/uapi/sound/compress_offload.h b/include/uapi/sound/compress_offload.h index 22ed8cb7800b..e00d8cbfc628 100644 --- a/include/uapi/sound/compress_offload.h +++ b/include/uapi/sound/compress_offload.h @@ -75,7 +75,7 @@ struct snd_compr_tstamp { /** * struct snd_compr_avail - avail descriptor * @avail: Number of bytes available in ring buffer for writing/reading - * @tstamp: timestamp infomation + * @tstamp: timestamp information */ struct snd_compr_avail { __u64 avail; diff --git a/include/uapi/sound/emu10k1.h b/include/uapi/sound/emu10k1.h index d1bbaf78457a..ec1535bb6aed 100644 --- a/include/uapi/sound/emu10k1.h +++ b/include/uapi/sound/emu10k1.h @@ -23,8 +23,7 @@ #define _UAPI__SOUND_EMU10K1_H #include <linux/types.h> - - +#include <sound/asound.h> /* * ---- FX8010 ---- diff --git a/include/uapi/sound/hdspm.h b/include/uapi/sound/hdspm.h index b357f1a5e29c..5737332d38f2 100644 --- a/include/uapi/sound/hdspm.h +++ b/include/uapi/sound/hdspm.h @@ -20,6 +20,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#ifdef __KERNEL__ +#include <linux/types.h> +#else +#include <stdint.h> +#endif + /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */ #define HDSPM_MAX_CHANNELS 64 diff --git a/kernel/acct.c b/kernel/acct.c index e6c10d1a4058..74963d192c5d 100644 --- a/kernel/acct.c +++ b/kernel/acct.c @@ -213,7 +213,7 @@ static int acct_on(struct filename *pathname) return -EACCES; } - if (!file->f_op->write) { + if (!(file->f_mode & FMODE_CAN_WRITE)) { kfree(acct); filp_close(file, NULL); return -EIO; diff --git a/kernel/exec_domain.c b/kernel/exec_domain.c index 83d4382f5699..6873bb3e6b7e 100644 --- a/kernel/exec_domain.c +++ b/kernel/exec_domain.c @@ -20,145 +20,10 @@ #include <linux/types.h> #include <linux/fs_struct.h> - -static void default_handler(int, struct pt_regs *); - -static struct exec_domain *exec_domains = &default_exec_domain; -static DEFINE_RWLOCK(exec_domains_lock); - - -static unsigned long ident_map[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31 -}; - -struct exec_domain default_exec_domain = { - .name = "Linux", /* name */ - .handler = default_handler, /* lcall7 causes a seg fault. */ - .pers_low = 0, /* PER_LINUX personality. */ - .pers_high = 0, /* PER_LINUX personality. */ - .signal_map = ident_map, /* Identity map signals. */ - .signal_invmap = ident_map, /* - both ways. */ -}; - - -static void -default_handler(int segment, struct pt_regs *regp) -{ - set_personality(0); - - if (current_thread_info()->exec_domain->handler != default_handler) - current_thread_info()->exec_domain->handler(segment, regp); - else - send_sig(SIGSEGV, current, 1); -} - -static struct exec_domain * -lookup_exec_domain(unsigned int personality) -{ - unsigned int pers = personality(personality); - struct exec_domain *ep; - - read_lock(&exec_domains_lock); - for (ep = exec_domains; ep; ep = ep->next) { - if (pers >= ep->pers_low && pers <= ep->pers_high) - if (try_module_get(ep->module)) - goto out; - } - -#ifdef CONFIG_MODULES - read_unlock(&exec_domains_lock); - request_module("personality-%d", pers); - read_lock(&exec_domains_lock); - - for (ep = exec_domains; ep; ep = ep->next) { - if (pers >= ep->pers_low && pers <= ep->pers_high) - if (try_module_get(ep->module)) - goto out; - } -#endif - - ep = &default_exec_domain; -out: - read_unlock(&exec_domains_lock); - return ep; -} - -int -register_exec_domain(struct exec_domain *ep) -{ - struct exec_domain *tmp; - int err = -EBUSY; - - if (ep == NULL) - return -EINVAL; - - if (ep->next != NULL) - return -EBUSY; - - write_lock(&exec_domains_lock); - for (tmp = exec_domains; tmp; tmp = tmp->next) { - if (tmp == ep) - goto out; - } - - ep->next = exec_domains; - exec_domains = ep; - err = 0; - -out: - write_unlock(&exec_domains_lock); - return err; -} -EXPORT_SYMBOL(register_exec_domain); - -int -unregister_exec_domain(struct exec_domain *ep) -{ - struct exec_domain **epp; - - epp = &exec_domains; - write_lock(&exec_domains_lock); - for (epp = &exec_domains; *epp; epp = &(*epp)->next) { - if (ep == *epp) - goto unregister; - } - write_unlock(&exec_domains_lock); - return -EINVAL; - -unregister: - *epp = ep->next; - ep->next = NULL; - write_unlock(&exec_domains_lock); - return 0; -} -EXPORT_SYMBOL(unregister_exec_domain); - -int __set_personality(unsigned int personality) -{ - struct exec_domain *oep = current_thread_info()->exec_domain; - - current_thread_info()->exec_domain = lookup_exec_domain(personality); - current->personality = personality; - module_put(oep->module); - - return 0; -} -EXPORT_SYMBOL(__set_personality); - #ifdef CONFIG_PROC_FS static int execdomains_proc_show(struct seq_file *m, void *v) { - struct exec_domain *ep; - - read_lock(&exec_domains_lock); - for (ep = exec_domains; ep; ep = ep->next) - seq_printf(m, "%d-%d\t%-16s\t[%s]\n", - ep->pers_low, ep->pers_high, ep->name, - module_name(ep->module)); - read_unlock(&exec_domains_lock); + seq_puts(m, "0-0\tLinux \t[kernel]\n"); return 0; } diff --git a/kernel/exit.c b/kernel/exit.c index feff10bbb307..22fcc05dec40 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -756,8 +756,6 @@ void do_exit(long code) cgroup_exit(tsk); - module_put(task_thread_info(tsk)->exec_domain->module); - /* * FIXME: do that only when needed, using sched_exit tracepoint */ diff --git a/kernel/fork.c b/kernel/fork.c index cf65139615a0..f2c1e7352298 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1279,9 +1279,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, if (nr_threads >= max_threads) goto bad_fork_cleanup_count; - if (!try_module_get(task_thread_info(p)->exec_domain->module)) - goto bad_fork_cleanup_count; - delayacct_tsk_init(p); /* Must remain after dup_task_struct() */ p->flags &= ~(PF_SUPERPRIV | PF_WQ_WORKER); p->flags |= PF_FORKNOEXEC; @@ -1590,7 +1587,6 @@ bad_fork_cleanup_threadgroup_lock: if (clone_flags & CLONE_THREAD) threadgroup_change_end(current); delayacct_tsk_free(p); - module_put(task_thread_info(p)->exec_domain->module); bad_fork_cleanup_count: atomic_dec(&p->cred->user->processes); exit_creds(p); diff --git a/lib/string.c b/lib/string.c index ce81aaec3839..a5792019193c 100644 --- a/lib/string.c +++ b/lib/string.c @@ -607,7 +607,7 @@ EXPORT_SYMBOL(memset); void memzero_explicit(void *s, size_t count) { memset(s, 0, count); - OPTIMIZER_HIDE_VAR(s); + barrier(); } EXPORT_SYMBOL(memzero_explicit); diff --git a/mm/nommu.c b/mm/nommu.c index 3fba2dc97c44..e544508e2a4b 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -1016,7 +1016,7 @@ static int validate_mmap_request(struct file *file, * device */ if (!file->f_op->get_unmapped_area) capabilities &= ~NOMMU_MAP_DIRECT; - if (!file->f_op->read) + if (!(file->f_mode & FMODE_CAN_READ)) capabilities &= ~NOMMU_MAP_COPY; /* The file shall have been opened with read permission. */ @@ -1240,7 +1240,7 @@ static int do_mmap_private(struct vm_area_struct *vma, old_fs = get_fs(); set_fs(KERNEL_DS); - ret = vma->vm_file->f_op->read(vma->vm_file, base, len, &fpos); + ret = __vfs_read(vma->vm_file, base, len, &fpos); set_fs(old_fs); if (ret < 0) diff --git a/mm/shmem.c b/mm/shmem.c index 80b360c7bcd1..1ea2400b5245 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3118,8 +3118,6 @@ static const struct file_operations shmem_file_operations = { .mmap = shmem_mmap, #ifdef CONFIG_TMPFS .llseek = shmem_file_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = shmem_file_read_iter, .write_iter = generic_file_write_iter, .fsync = noop_fsync, diff --git a/net/9p/client.c b/net/9p/client.c index e86a9bea1d16..6f4c4c88db84 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -34,6 +34,7 @@ #include <linux/slab.h> #include <linux/sched.h> #include <linux/uaccess.h> +#include <linux/uio.h> #include <net/9p/9p.h> #include <linux/parser.h> #include <net/9p/client.h> @@ -555,7 +556,7 @@ out_err: */ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, - char *uidata, int in_hdrlen, int kern_buf) + struct iov_iter *uidata, int in_hdrlen) { int err; int ecode; @@ -591,16 +592,11 @@ static int p9_check_zc_errors(struct p9_client *c, struct p9_req_t *req, ename = &req->rc->sdata[req->rc->offset]; if (len > inline_len) { /* We have error in external buffer */ - if (kern_buf) { - memcpy(ename + inline_len, uidata, - len - inline_len); - } else { - err = copy_from_user(ename + inline_len, - uidata, len - inline_len); - if (err) { - err = -EFAULT; - goto out_err; - } + err = copy_from_iter(ename + inline_len, + len - inline_len, uidata); + if (err != len - inline_len) { + err = -EFAULT; + goto out_err; } } ename = NULL; @@ -806,8 +802,8 @@ reterr: * p9_client_zc_rpc - issue a request and wait for a response * @c: client session * @type: type of request - * @uidata: user bffer that should be ued for zero copy read - * @uodata: user buffer that shoud be user for zero copy write + * @uidata: destination for zero copy read + * @uodata: source for zero copy write * @inlen: read buffer size * @olen: write buffer size * @hdrlen: reader header size, This is the size of response protocol data @@ -816,9 +812,10 @@ reterr: * Returns request structure (which client must free using p9_free_req) */ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, - char *uidata, char *uodata, + struct iov_iter *uidata, + struct iov_iter *uodata, int inlen, int olen, int in_hdrlen, - int kern_buf, const char *fmt, ...) + const char *fmt, ...) { va_list ap; int sigpending, err; @@ -841,12 +838,8 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, } else sigpending = 0; - /* If we are called with KERNEL_DS force kern_buf */ - if (segment_eq(get_fs(), KERNEL_DS)) - kern_buf = 1; - err = c->trans_mod->zc_request(c, req, uidata, uodata, - inlen, olen, in_hdrlen, kern_buf); + inlen, olen, in_hdrlen); if (err < 0) { if (err == -EIO) c->status = Disconnected; @@ -876,7 +869,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, if (err < 0) goto reterr; - err = p9_check_zc_errors(c, req, uidata, in_hdrlen, kern_buf); + err = p9_check_zc_errors(c, req, uidata, in_hdrlen); trace_9p_client_res(c, type, req->rc->tag, err); if (!err) return req; @@ -1123,6 +1116,7 @@ struct p9_fid *p9_client_attach(struct p9_client *clnt, struct p9_fid *afid, fid = NULL; goto error; } + fid->uid = n_uname; req = p9_client_rpc(clnt, P9_TATTACH, "ddss?u", fid->fid, afid ? afid->fid : P9_NOFID, uname, aname, n_uname); @@ -1541,142 +1535,128 @@ error: EXPORT_SYMBOL(p9_client_unlinkat); int -p9_client_read(struct p9_fid *fid, char *data, char __user *udata, u64 offset, - u32 count) +p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err) { - char *dataptr; - int kernel_buf = 0; + struct p9_client *clnt = fid->clnt; struct p9_req_t *req; - struct p9_client *clnt; - int err, rsize, non_zc = 0; - + int total = 0; p9_debug(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", - fid->fid, (unsigned long long) offset, count); - err = 0; - clnt = fid->clnt; - - rsize = fid->iounit; - if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) - rsize = clnt->msize - P9_IOHDRSZ; - - if (count < rsize) - rsize = count; - - /* Don't bother zerocopy for small IO (< 1024) */ - if (clnt->trans_mod->zc_request && rsize > 1024) { - char *indata; - if (data) { - kernel_buf = 1; - indata = data; - } else - indata = (__force char *)udata; - /* - * response header len is 11 - * PDU Header(7) + IO Size (4) - */ - req = p9_client_zc_rpc(clnt, P9_TREAD, indata, NULL, rsize, 0, - 11, kernel_buf, "dqd", fid->fid, - offset, rsize); - } else { - non_zc = 1; - req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, - rsize); - } - if (IS_ERR(req)) { - err = PTR_ERR(req); - goto error; - } + fid->fid, (unsigned long long) offset, (int)iov_iter_count(to)); + + while (iov_iter_count(to)) { + int count = iov_iter_count(to); + int rsize, non_zc = 0; + char *dataptr; + + rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + if (count < rsize) + rsize = count; + + /* Don't bother zerocopy for small IO (< 1024) */ + if (clnt->trans_mod->zc_request && rsize > 1024) { + /* + * response header len is 11 + * PDU Header(7) + IO Size (4) + */ + req = p9_client_zc_rpc(clnt, P9_TREAD, to, NULL, rsize, + 0, 11, "dqd", fid->fid, + offset, rsize); + } else { + non_zc = 1; + req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset, + rsize); + } + if (IS_ERR(req)) { + *err = PTR_ERR(req); + break; + } - err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); - if (err) { - trace_9p_protocol_dump(clnt, req->rc); - goto free_and_error; - } + *err = p9pdu_readf(req->rc, clnt->proto_version, + "D", &count, &dataptr); + if (*err) { + trace_9p_protocol_dump(clnt, req->rc); + p9_free_req(clnt, req); + break; + } - p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count); + p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", count); + if (!count) { + p9_free_req(clnt, req); + break; + } - if (non_zc) { - if (data) { - memmove(data, dataptr, count); - } else { - err = copy_to_user(udata, dataptr, count); - if (err) { - err = -EFAULT; - goto free_and_error; + if (non_zc) { + int n = copy_to_iter(dataptr, count, to); + total += n; + offset += n; + if (n != count) { + *err = -EFAULT; + p9_free_req(clnt, req); + break; } + } else { + iov_iter_advance(to, count); + total += count; + offset += count; } + p9_free_req(clnt, req); } - p9_free_req(clnt, req); - return count; - -free_and_error: - p9_free_req(clnt, req); -error: - return err; + return total; } EXPORT_SYMBOL(p9_client_read); int -p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, - u64 offset, u32 count) +p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err) { - int err, rsize; - int kernel_buf = 0; - struct p9_client *clnt; + struct p9_client *clnt = fid->clnt; struct p9_req_t *req; + int total = 0; + + p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %zd\n", + fid->fid, (unsigned long long) offset, + iov_iter_count(from)); + + while (iov_iter_count(from)) { + int count = iov_iter_count(from); + int rsize = fid->iounit; + if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) + rsize = clnt->msize - P9_IOHDRSZ; + + if (count < rsize) + rsize = count; + + /* Don't bother zerocopy for small IO (< 1024) */ + if (clnt->trans_mod->zc_request && rsize > 1024) { + req = p9_client_zc_rpc(clnt, P9_TWRITE, NULL, from, 0, + rsize, P9_ZC_HDR_SZ, "dqd", + fid->fid, offset, rsize); + } else { + req = p9_client_rpc(clnt, P9_TWRITE, "dqV", fid->fid, + offset, rsize, from); + } + if (IS_ERR(req)) { + *err = PTR_ERR(req); + break; + } - p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d\n", - fid->fid, (unsigned long long) offset, count); - err = 0; - clnt = fid->clnt; - - rsize = fid->iounit; - if (!rsize || rsize > clnt->msize-P9_IOHDRSZ) - rsize = clnt->msize - P9_IOHDRSZ; + *err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count); + if (*err) { + trace_9p_protocol_dump(clnt, req->rc); + p9_free_req(clnt, req); + } - if (count < rsize) - rsize = count; + p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", count); - /* Don't bother zerocopy for small IO (< 1024) */ - if (clnt->trans_mod->zc_request && rsize > 1024) { - char *odata; - if (data) { - kernel_buf = 1; - odata = data; - } else - odata = (char *)udata; - req = p9_client_zc_rpc(clnt, P9_TWRITE, NULL, odata, 0, rsize, - P9_ZC_HDR_SZ, kernel_buf, "dqd", - fid->fid, offset, rsize); - } else { - if (data) - req = p9_client_rpc(clnt, P9_TWRITE, "dqD", fid->fid, - offset, rsize, data); - else - req = p9_client_rpc(clnt, P9_TWRITE, "dqU", fid->fid, - offset, rsize, udata); - } - if (IS_ERR(req)) { - err = PTR_ERR(req); - goto error; - } - - err = p9pdu_readf(req->rc, clnt->proto_version, "d", &count); - if (err) { - trace_9p_protocol_dump(clnt, req->rc); - goto free_and_error; + p9_free_req(clnt, req); + iov_iter_advance(from, count); + total += count; + offset += count; } - - p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", count); - - p9_free_req(clnt, req); - return count; - -free_and_error: - p9_free_req(clnt, req); -error: - return err; + return total; } EXPORT_SYMBOL(p9_client_write); @@ -2068,6 +2048,10 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) struct p9_client *clnt; struct p9_req_t *req; char *dataptr; + struct kvec kv = {.iov_base = data, .iov_len = count}; + struct iov_iter to; + + iov_iter_kvec(&to, READ | ITER_KVEC, &kv, 1, count); p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", fid->fid, (unsigned long long) offset, count); @@ -2088,8 +2072,8 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) * response header len is 11 * PDU Header(7) + IO Size (4) */ - req = p9_client_zc_rpc(clnt, P9_TREADDIR, data, NULL, rsize, 0, - 11, 1, "dqd", fid->fid, offset, rsize); + req = p9_client_zc_rpc(clnt, P9_TREADDIR, &to, NULL, rsize, 0, + 11, "dqd", fid->fid, offset, rsize); } else { non_zc = 1; req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, diff --git a/net/9p/protocol.c b/net/9p/protocol.c index ab9127ec5b7a..e9d0f0c1a048 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c @@ -33,6 +33,7 @@ #include <linux/sched.h> #include <linux/stddef.h> #include <linux/types.h> +#include <linux/uio.h> #include <net/9p/9p.h> #include <net/9p/client.h> #include "protocol.h" @@ -69,10 +70,11 @@ static size_t pdu_write(struct p9_fcall *pdu, const void *data, size_t size) } static size_t -pdu_write_u(struct p9_fcall *pdu, const char __user *udata, size_t size) +pdu_write_u(struct p9_fcall *pdu, struct iov_iter *from, size_t size) { size_t len = min(pdu->capacity - pdu->size, size); - if (copy_from_user(&pdu->sdata[pdu->size], udata, len)) + struct iov_iter i = *from; + if (copy_from_iter(&pdu->sdata[pdu->size], len, &i) != len) len = 0; pdu->size += len; @@ -437,23 +439,13 @@ p9pdu_vwritef(struct p9_fcall *pdu, int proto_version, const char *fmt, stbuf->extension, stbuf->n_uid, stbuf->n_gid, stbuf->n_muid); } break; - case 'D':{ - uint32_t count = va_arg(ap, uint32_t); - const void *data = va_arg(ap, const void *); - - errcode = p9pdu_writef(pdu, proto_version, "d", - count); - if (!errcode && pdu_write(pdu, data, count)) - errcode = -EFAULT; - } - break; - case 'U':{ + case 'V':{ int32_t count = va_arg(ap, int32_t); - const char __user *udata = - va_arg(ap, const void __user *); + struct iov_iter *from = + va_arg(ap, struct iov_iter *); errcode = p9pdu_writef(pdu, proto_version, "d", count); - if (!errcode && pdu_write_u(pdu, udata, count)) + if (!errcode && pdu_write_u(pdu, from, count)) errcode = -EFAULT; } break; diff --git a/net/9p/trans_common.c b/net/9p/trans_common.c index 2ee3879161b1..38aa6345bdfa 100644 --- a/net/9p/trans_common.c +++ b/net/9p/trans_common.c @@ -12,12 +12,8 @@ * */ -#include <linux/slab.h> +#include <linux/mm.h> #include <linux/module.h> -#include <net/9p/9p.h> -#include <net/9p/client.h> -#include <linux/scatterlist.h> -#include "trans_common.h" /** * p9_release_req_pages - Release pages after the transaction. @@ -31,39 +27,3 @@ void p9_release_pages(struct page **pages, int nr_pages) put_page(pages[i]); } EXPORT_SYMBOL(p9_release_pages); - -/** - * p9_nr_pages - Return number of pages needed to accommodate the payload. - */ -int p9_nr_pages(char *data, int len) -{ - unsigned long start_page, end_page; - start_page = (unsigned long)data >> PAGE_SHIFT; - end_page = ((unsigned long)data + len + PAGE_SIZE - 1) >> PAGE_SHIFT; - return end_page - start_page; -} -EXPORT_SYMBOL(p9_nr_pages); - -/** - * payload_gup - Translates user buffer into kernel pages and - * pins them either for read/write through get_user_pages_fast(). - * @req: Request to be sent to server. - * @pdata_off: data offset into the first page after translation (gup). - * @pdata_len: Total length of the IO. gup may not return requested # of pages. - * @nr_pages: number of pages to accommodate the payload - * @rw: Indicates if the pages are for read or write. - */ - -int p9_payload_gup(char *data, int *nr_pages, struct page **pages, int write) -{ - int nr_mapped_pages; - - nr_mapped_pages = get_user_pages_fast((unsigned long)data, - *nr_pages, write, pages); - if (nr_mapped_pages <= 0) - return nr_mapped_pages; - - *nr_pages = nr_mapped_pages; - return 0; -} -EXPORT_SYMBOL(p9_payload_gup); diff --git a/net/9p/trans_common.h b/net/9p/trans_common.h index 173bb550a9eb..c43babb3f635 100644 --- a/net/9p/trans_common.h +++ b/net/9p/trans_common.h @@ -13,5 +13,3 @@ */ void p9_release_pages(struct page **, int); -int p9_payload_gup(char *, int *, struct page **, int); -int p9_nr_pages(char *, int); diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index 36a1a739ad68..e62bcbbabb5e 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -217,15 +217,15 @@ static int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) * @start: which segment of the sg_list to start at * @pdata: a list of pages to add into sg. * @nr_pages: number of pages to pack into the scatter/gather list - * @data: data to pack into scatter/gather list + * @offs: amount of data in the beginning of first page _not_ to pack * @count: amount of data to pack into the scatter/gather list */ static int pack_sg_list_p(struct scatterlist *sg, int start, int limit, - struct page **pdata, int nr_pages, char *data, int count) + struct page **pdata, int nr_pages, size_t offs, int count) { int i = 0, s; - int data_off; + int data_off = offs; int index = start; BUG_ON(nr_pages > (limit - start)); @@ -233,16 +233,14 @@ pack_sg_list_p(struct scatterlist *sg, int start, int limit, * if the first page doesn't start at * page boundary find the offset */ - data_off = offset_in_page(data); while (nr_pages) { - s = rest_of_page(data); + s = PAGE_SIZE - data_off; if (s > count) s = count; /* Make sure we don't terminate early. */ sg_unmark_end(&sg[index]); sg_set_page(&sg[index++], pdata[i++], s, data_off); data_off = 0; - data += s; count -= s; nr_pages--; } @@ -314,11 +312,20 @@ req_retry: } static int p9_get_mapped_pages(struct virtio_chan *chan, - struct page **pages, char *data, - int nr_pages, int write, int kern_buf) + struct page ***pages, + struct iov_iter *data, + int count, + size_t *offs, + int *need_drop) { + int nr_pages; int err; - if (!kern_buf) { + + if (!iov_iter_count(data)) + return 0; + + if (!(data->type & ITER_KVEC)) { + int n; /* * We allow only p9_max_pages pinned. We wait for the * Other zc request to finish here @@ -329,26 +336,49 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, if (err == -ERESTARTSYS) return err; } - err = p9_payload_gup(data, &nr_pages, pages, write); - if (err < 0) - return err; + n = iov_iter_get_pages_alloc(data, pages, count, offs); + if (n < 0) + return n; + *need_drop = 1; + nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE); atomic_add(nr_pages, &vp_pinned); + return n; } else { /* kernel buffer, no need to pin pages */ - int s, index = 0; - int count = nr_pages; - while (nr_pages) { - s = rest_of_page(data); - if (is_vmalloc_addr(data)) - pages[index++] = vmalloc_to_page(data); + int index; + size_t len; + void *p; + + /* we'd already checked that it's non-empty */ + while (1) { + len = iov_iter_single_seg_count(data); + if (likely(len)) { + p = data->kvec->iov_base + data->iov_offset; + break; + } + iov_iter_advance(data, 0); + } + if (len > count) + len = count; + + nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) - + (unsigned long)p / PAGE_SIZE; + + *pages = kmalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); + if (!*pages) + return -ENOMEM; + + *need_drop = 0; + p -= (*offs = (unsigned long)p % PAGE_SIZE); + for (index = 0; index < nr_pages; index++) { + if (is_vmalloc_addr(p)) + (*pages)[index] = vmalloc_to_page(p); else - pages[index++] = kmap_to_page(data); - data += s; - nr_pages--; + (*pages)[index] = kmap_to_page(p); + p += PAGE_SIZE; } - nr_pages = count; + return len; } - return nr_pages; } /** @@ -364,8 +394,8 @@ static int p9_get_mapped_pages(struct virtio_chan *chan, */ static int p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, - char *uidata, char *uodata, int inlen, - int outlen, int in_hdr_len, int kern_buf) + struct iov_iter *uidata, struct iov_iter *uodata, + int inlen, int outlen, int in_hdr_len) { int in, out, err, out_sgs, in_sgs; unsigned long flags; @@ -373,41 +403,32 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, struct page **in_pages = NULL, **out_pages = NULL; struct virtio_chan *chan = client->trans; struct scatterlist *sgs[4]; + size_t offs; + int need_drop = 0; p9_debug(P9_DEBUG_TRANS, "virtio request\n"); if (uodata) { - out_nr_pages = p9_nr_pages(uodata, outlen); - out_pages = kmalloc(sizeof(struct page *) * out_nr_pages, - GFP_NOFS); - if (!out_pages) { - err = -ENOMEM; - goto err_out; - } - out_nr_pages = p9_get_mapped_pages(chan, out_pages, uodata, - out_nr_pages, 0, kern_buf); - if (out_nr_pages < 0) { - err = out_nr_pages; - kfree(out_pages); - out_pages = NULL; - goto err_out; + int n = p9_get_mapped_pages(chan, &out_pages, uodata, + outlen, &offs, &need_drop); + if (n < 0) + return n; + out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); + if (n != outlen) { + __le32 v = cpu_to_le32(n); + memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4); + outlen = n; } - } - if (uidata) { - in_nr_pages = p9_nr_pages(uidata, inlen); - in_pages = kmalloc(sizeof(struct page *) * in_nr_pages, - GFP_NOFS); - if (!in_pages) { - err = -ENOMEM; - goto err_out; - } - in_nr_pages = p9_get_mapped_pages(chan, in_pages, uidata, - in_nr_pages, 1, kern_buf); - if (in_nr_pages < 0) { - err = in_nr_pages; - kfree(in_pages); - in_pages = NULL; - goto err_out; + } else if (uidata) { + int n = p9_get_mapped_pages(chan, &in_pages, uidata, + inlen, &offs, &need_drop); + if (n < 0) + return n; + in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); + if (n != inlen) { + __le32 v = cpu_to_le32(n); + memcpy(&req->tc->sdata[req->tc->size - 4], &v, 4); + inlen = n; } } req->status = REQ_STATUS_SENT; @@ -426,7 +447,7 @@ req_retry_pinned: if (out_pages) { sgs[out_sgs++] = chan->sg + out; out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, - out_pages, out_nr_pages, uodata, outlen); + out_pages, out_nr_pages, offs, outlen); } /* @@ -444,7 +465,7 @@ req_retry_pinned: if (in_pages) { sgs[out_sgs + in_sgs++] = chan->sg + out + in; in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, - in_pages, in_nr_pages, uidata, inlen); + in_pages, in_nr_pages, offs, inlen); } BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs)); @@ -478,7 +499,7 @@ req_retry_pinned: * Non kernel buffers are pinned, unpin them */ err_out: - if (!kern_buf) { + if (need_drop) { if (in_pages) { p9_release_pages(in_pages, in_nr_pages); atomic_sub(in_nr_pages, &vp_pinned); diff --git a/net/socket.c b/net/socket.c index 5b0126234606..3e33959f3ce5 100644 --- a/net/socket.c +++ b/net/socket.c @@ -140,8 +140,6 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos, static const struct file_operations socket_file_ops = { .owner = THIS_MODULE, .llseek = no_llseek, - .read = new_sync_read, - .write = new_sync_write, .read_iter = sock_read_iter, .write_iter = sock_write_iter, .poll = sock_poll, diff --git a/scripts/Makefile.dtbinst b/scripts/Makefile.dtbinst index 909ed7a2ac61..1c15717e0d56 100644 --- a/scripts/Makefile.dtbinst +++ b/scripts/Makefile.dtbinst @@ -18,7 +18,7 @@ export dtbinst-root ?= $(obj) include include/config/auto.conf include scripts/Kbuild.include -include $(srctree)/$(obj)/Makefile +include $(src)/Makefile PHONY += __dtbs_install_prep __dtbs_install_prep: diff --git a/scripts/Makefile.fwinst b/scripts/Makefile.fwinst index 5b698add4f31..b27290035253 100644 --- a/scripts/Makefile.fwinst +++ b/scripts/Makefile.fwinst @@ -13,7 +13,7 @@ src := $(obj) -include $(objtree)/.config include scripts/Kbuild.include -include $(srctree)/$(obj)/Makefile +include $(src)/Makefile include scripts/Makefile.host diff --git a/scripts/kallsyms.c b/scripts/kallsyms.c index c6d33bd15b04..8fa81e84e295 100644 --- a/scripts/kallsyms.c +++ b/scripts/kallsyms.c @@ -212,15 +212,22 @@ static int symbol_valid(struct sym_entry *s) "_SDA_BASE_", /* ppc */ "_SDA2_BASE_", /* ppc */ NULL }; + + static char *special_suffixes[] = { + "_veneer", /* arm */ + NULL }; + int i; - int offset = 1; + char *sym_name = (char *)s->sym + 1; + if (s->addr < kernel_start_addr) return 0; /* skip prefix char */ - if (symbol_prefix_char && *(s->sym + 1) == symbol_prefix_char) - offset++; + if (symbol_prefix_char && *sym_name == symbol_prefix_char) + sym_name++; + /* if --all-symbols is not specified, then symbols outside the text * and inittext sections are discarded */ @@ -235,22 +242,26 @@ static int symbol_valid(struct sym_entry *s) * rules. */ if ((s->addr == text_range_text->end && - strcmp((char *)s->sym + offset, + strcmp(sym_name, text_range_text->end_sym)) || (s->addr == text_range_inittext->end && - strcmp((char *)s->sym + offset, + strcmp(sym_name, text_range_inittext->end_sym))) return 0; } /* Exclude symbols which vary between passes. */ - if (strstr((char *)s->sym + offset, "_compiled.")) - return 0; - for (i = 0; special_symbols[i]; i++) - if( strcmp((char *)s->sym + offset, special_symbols[i]) == 0 ) + if (strcmp(sym_name, special_symbols[i]) == 0) return 0; + for (i = 0; special_suffixes[i]; i++) { + int l = strlen(sym_name) - strlen(special_suffixes[i]); + + if (l >= 0 && strcmp(sym_name + l, special_suffixes[i]) == 0) + return 0; + } + return 1; } diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile index 9645c0739386..d9b1fef0c67e 100644 --- a/scripts/kconfig/Makefile +++ b/scripts/kconfig/Makefile @@ -2,7 +2,7 @@ # Kernel configuration targets # These targets are used from top-level makefile -PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \ +PHONY += xconfig gconfig menuconfig config silentoldconfig update-po-config \ localmodconfig localyesconfig ifdef KBUILD_KCONFIG @@ -11,30 +11,31 @@ else Kconfig := Kconfig endif +ifeq ($(quiet),silent_) +silent := -s +endif + # We need this, in case the user has it in its environment unexport CONFIG_ xconfig: $(obj)/qconf - $< $(Kconfig) + $< $(silent) $(Kconfig) gconfig: $(obj)/gconf - $< $(Kconfig) + $< $(silent) $(Kconfig) menuconfig: $(obj)/mconf - $< $(Kconfig) + $< $(silent) $(Kconfig) config: $(obj)/conf - $< --oldaskconfig $(Kconfig) + $< $(silent) --oldaskconfig $(Kconfig) nconfig: $(obj)/nconf - $< $(Kconfig) - -oldconfig: $(obj)/conf - $< --$@ $(Kconfig) + $< $(silent) $(Kconfig) silentoldconfig: $(obj)/conf $(Q)mkdir -p include/config include/generated - $< --$@ $(Kconfig) + $< $(silent) --$@ $(Kconfig) localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf $(Q)mkdir -p include/config include/generated @@ -43,18 +44,18 @@ localyesconfig localmodconfig: $(obj)/streamline_config.pl $(obj)/conf cmp -s .tmp.config .config || \ (mv -f .config .config.old.1; \ mv -f .tmp.config .config; \ - $(obj)/conf --silentoldconfig $(Kconfig); \ + $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ mv -f .config.old.1 .config.old) \ else \ mv -f .tmp.config .config; \ - $(obj)/conf --silentoldconfig $(Kconfig); \ + $(obj)/conf $(silent) --silentoldconfig $(Kconfig); \ fi $(Q)rm -f .tmp.config # Create new linux.pot file # Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files update-po-config: $(obj)/kxgettext $(obj)/gconf.glade.h - $(Q)echo " GEN config.pot" + $(Q)$(kecho) " GEN config.pot" $(Q)xgettext --default-domain=linux \ --add-comments --keyword=_ --keyword=N_ \ --from-code=UTF-8 \ @@ -65,61 +66,58 @@ update-po-config: $(obj)/kxgettext $(obj)/gconf.glade.h $(Q)(for i in `ls $(srctree)/arch/*/Kconfig \ $(srctree)/arch/*/um/Kconfig`; \ do \ - echo " GEN $$i"; \ + $(kecho) " GEN $$i"; \ $(obj)/kxgettext $$i \ >> $(obj)/config.pot; \ done ) - $(Q)echo " GEN linux.pot" + $(Q)$(kecho) " GEN linux.pot" $(Q)msguniq --sort-by-file --to-code=UTF-8 $(obj)/config.pot \ --output $(obj)/linux.pot $(Q)rm -f $(obj)/config.pot -PHONY += allnoconfig allyesconfig allmodconfig alldefconfig randconfig - -allnoconfig allyesconfig allmodconfig alldefconfig randconfig: $(obj)/conf - $< --$@ $(Kconfig) +# These targets map 1:1 to the commandline options of 'conf' +simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \ + alldefconfig randconfig listnewconfig olddefconfig +PHONY += $(simple-targets) -PHONY += listnewconfig olddefconfig oldnoconfig savedefconfig defconfig +$(simple-targets): $(obj)/conf + $< $(silent) --$@ $(Kconfig) -listnewconfig olddefconfig: $(obj)/conf - $< --$@ $(Kconfig) +PHONY += oldnoconfig savedefconfig defconfig # oldnoconfig is an alias of olddefconfig, because people already are dependent # on its behavior(sets new symbols to their default value but not 'n') with the # counter-intuitive name. -oldnoconfig: $(obj)/conf - $< --olddefconfig $(Kconfig) +oldnoconfig: olddefconfig savedefconfig: $(obj)/conf - $< --$@=defconfig $(Kconfig) + $< $(silent) --$@=defconfig $(Kconfig) defconfig: $(obj)/conf ifeq ($(KBUILD_DEFCONFIG),) - $< --defconfig $(Kconfig) + $< $(silent) --defconfig $(Kconfig) else - @echo "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" - $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) + @$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'" + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig) endif %_defconfig: $(obj)/conf - $(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) + $(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) -configfiles=$(wildcard $(srctree)/kernel/configs/$(1).config $(srctree)/arch/$(SRCARCH)/configs/$(1).config) +configfiles=$(wildcard $(srctree)/kernel/configs/$@ $(srctree)/arch/$(SRCARCH)/configs/$@) -define mergeconfig -$(if $(wildcard $(objtree)/.config),, $(error You need an existing .config for this target)) -$(if $(call configfiles,$(1)),, $(error No configuration exists for this target on this architecture)) -$(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m -O $(objtree) $(objtree)/.config $(call configfiles,$(1)) -$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig -endef +%.config: $(obj)/conf + $(if $(call configfiles),, $(error No configuration exists for this target on this architecture)) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/kconfig/merge_config.sh -m .config $(configfiles) + +$(Q)yes "" | $(MAKE) -f $(srctree)/Makefile oldconfig PHONY += kvmconfig -kvmconfig: - $(call mergeconfig,kvm_guest) +kvmconfig: kvm_guest.config + @: PHONY += tinyconfig -tinyconfig: allnoconfig - $(call mergeconfig,tiny) +tinyconfig: + $(Q)$(MAKE) -f $(srctree)/Makefile allnoconfig tiny.config # Help text used by make help help: @@ -221,7 +219,7 @@ $(obj)/.tmp_qtcheck: $(src)/Makefile # QT needs some extra effort... $(obj)/.tmp_qtcheck: - @set -e; echo " CHECK qt"; dir=""; pkg=""; \ + @set -e; $(kecho) " CHECK qt"; dir=""; pkg=""; \ if ! pkg-config --exists QtCore 2> /dev/null; then \ echo "* Unable to find the QT4 tool qmake. Trying to use QT3"; \ pkg-config --exists qt 2> /dev/null && pkg=qt; \ diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index fef75fc756f4..6c204318bc94 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -471,7 +471,7 @@ static struct option long_opts[] = { static void conf_usage(const char *progname) { - printf("Usage: %s [option] <kconfig-file>\n", progname); + printf("Usage: %s [-s] [option] <kconfig-file>\n", progname); printf("[option] is _one_ of the following:\n"); printf(" --listnewconfig List new options\n"); printf(" --oldaskconfig Start a new configuration using a line-oriented program\n"); @@ -501,7 +501,11 @@ int main(int ac, char **av) tty_stdio = isatty(0) && isatty(1) && isatty(2); - while ((opt = getopt_long(ac, av, "", long_opts, NULL)) != -1) { + while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) { + if (opt == 's') { + conf_set_message_callback(NULL); + continue; + } input_mode = (enum input_mode)opt; switch (opt) { case silentoldconfig: diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 28df18dd1147..c814f57672fc 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -16,6 +16,11 @@ #include "lkc.h" +struct conf_printer { + void (*print_symbol)(FILE *, struct symbol *, const char *, void *); + void (*print_comment)(FILE *, const char *, void *); +}; + static void conf_warning(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); diff --git a/scripts/kconfig/expr.c b/scripts/kconfig/expr.c index d6626521f9b9..fb0a2a286dca 100644 --- a/scripts/kconfig/expr.c +++ b/scripts/kconfig/expr.c @@ -11,6 +11,12 @@ #define DEBUG_EXPR 0 +static int expr_eq(struct expr *e1, struct expr *e2); +static struct expr *expr_eliminate_yn(struct expr *e); +static struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); +static struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); +static void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); + struct expr *expr_alloc_symbol(struct symbol *sym) { struct expr *e = xcalloc(1, sizeof(*e)); @@ -186,7 +192,7 @@ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2) #undef e1 #undef e2 -int expr_eq(struct expr *e1, struct expr *e2) +static int expr_eq(struct expr *e1, struct expr *e2) { int res, old_count; @@ -228,7 +234,7 @@ int expr_eq(struct expr *e1, struct expr *e2) return 0; } -struct expr *expr_eliminate_yn(struct expr *e) +static struct expr *expr_eliminate_yn(struct expr *e) { struct expr *tmp; @@ -823,7 +829,7 @@ bool expr_depends_symbol(struct expr *dep, struct symbol *sym) return false; } -struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) +static struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) { struct expr *tmp = NULL; expr_extract_eq(E_AND, &tmp, ep1, ep2); @@ -834,7 +840,7 @@ struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2) return tmp; } -struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) +static struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) { struct expr *tmp = NULL; expr_extract_eq(E_OR, &tmp, ep1, ep2); @@ -845,7 +851,7 @@ struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2) return tmp; } -void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) +static void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2) { #define e1 (*ep1) #define e2 (*ep2) @@ -976,11 +982,8 @@ tristate expr_calc_value(struct expr *e) } } -int expr_compare_type(enum expr_type t1, enum expr_type t2) +static int expr_compare_type(enum expr_type t1, enum expr_type t2) { -#if 0 - return 1; -#else if (t1 == t2) return 0; switch (t1) { @@ -1005,7 +1008,6 @@ int expr_compare_type(enum expr_type t1, enum expr_type t2) } printf("[%dgt%d?]", t1, t2); return 0; -#endif } static inline struct expr * diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h index 412ea8a2abb8..a2fc96a2bd2c 100644 --- a/scripts/kconfig/expr.h +++ b/scripts/kconfig/expr.h @@ -205,18 +205,13 @@ struct expr *expr_alloc_and(struct expr *e1, struct expr *e2); struct expr *expr_alloc_or(struct expr *e1, struct expr *e2); struct expr *expr_copy(const struct expr *org); void expr_free(struct expr *e); -int expr_eq(struct expr *e1, struct expr *e2); void expr_eliminate_eq(struct expr **ep1, struct expr **ep2); tristate expr_calc_value(struct expr *e); -struct expr *expr_eliminate_yn(struct expr *e); struct expr *expr_trans_bool(struct expr *e); struct expr *expr_eliminate_dups(struct expr *e); struct expr *expr_transform(struct expr *e); int expr_contains_symbol(struct expr *dep, struct symbol *sym); bool expr_depends_symbol(struct expr *dep, struct symbol *sym); -struct expr *expr_extract_eq_and(struct expr **ep1, struct expr **ep2); -struct expr *expr_extract_eq_or(struct expr **ep1, struct expr **ep2); -void expr_extract_eq(enum expr_type type, struct expr **ep, struct expr **ep1, struct expr **ep2); struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym); struct expr *expr_simplify_unmet_dep(struct expr *e1, struct expr *e2); diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d0a35b21f308..26d208b435a0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -169,14 +169,6 @@ void init_main_window(const gchar * glade_file) style = gtk_widget_get_style(main_wnd); widget = glade_xml_get_widget(xml, "toolbar1"); -#if 0 /* Use stock Gtk icons instead */ - replace_button_icon(xml, main_wnd->window, style, - "button1", (gchar **) xpm_back); - replace_button_icon(xml, main_wnd->window, style, - "button2", (gchar **) xpm_load); - replace_button_icon(xml, main_wnd->window, style, - "button3", (gchar **) xpm_save); -#endif replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); replace_button_icon(xml, main_wnd->window, style, @@ -184,22 +176,6 @@ void init_main_window(const gchar * glade_file) replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); -#if 0 - switch (view_mode) { - case SINGLE_VIEW: - widget = glade_xml_get_widget(xml, "button4"); - g_signal_emit_by_name(widget, "clicked"); - break; - case SPLIT_VIEW: - widget = glade_xml_get_widget(xml, "button5"); - g_signal_emit_by_name(widget, "clicked"); - break; - case FULL_VIEW: - widget = glade_xml_get_widget(xml, "button6"); - g_signal_emit_by_name(widget, "clicked"); - break; - } -#endif txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", "foreground", "red", @@ -1498,9 +1474,12 @@ int main(int ac, char *av[]) case 'a': //showAll = 1; break; + case 's': + conf_set_message_callback(NULL); + break; case 'h': case '?': - printf("%s <config>\n", av[0]); + printf("%s [-s] <config>\n", av[0]); exit(0); } name = av[2]; diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index d5daa7af8b49..91ca126ea080 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -21,9 +21,7 @@ static inline char *bind_textdomain_codeset(const char *dn, char *c) { return c; extern "C" { #endif -#define P(name,type,arg) extern type name arg #include "lkc_proto.h" -#undef P #define SRCTREE "srctree" @@ -70,9 +68,6 @@ struct kconf_id { enum symbol_type stype; }; -extern int zconfdebug; - -int zconfparse(void); void zconfdump(FILE *out); void zconf_starthelp(void); FILE *zconf_fopen(const char *name); @@ -90,11 +85,6 @@ void sym_add_change_count(int count); bool conf_set_all_new_symbols(enum conf_def_mode mode); void set_all_choice_values(struct symbol *csym); -struct conf_printer { - void (*print_symbol)(FILE *, struct symbol *, const char *, void *); - void (*print_comment)(FILE *, const char *, void *); -}; - /* confdata.c and expr.c */ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out) { @@ -113,7 +103,6 @@ void menu_add_entry(struct symbol *sym); void menu_end_entry(void); void menu_add_dep(struct expr *dep); void menu_add_visibility(struct expr *dep); -struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep); struct property *menu_add_prompt(enum prop_type type, char *prompt, struct expr *dep); void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep); void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep); @@ -137,7 +126,6 @@ struct gstr { int max_width; }; struct gstr str_new(void); -struct gstr str_assign(const char *s); void str_free(struct gstr *gs); void str_append(struct gstr *gs, const char *s); void str_printf(struct gstr *gs, const char *fmt, ...); @@ -148,8 +136,6 @@ extern struct expr *sym_env_list; void sym_init(void); void sym_clear_all_valid(void); -void sym_set_all_changed(void); -void sym_set_changed(struct symbol *sym); struct symbol *sym_choice_default(struct symbol *sym); const char *sym_get_string_default(struct symbol *sym); struct symbol *sym_check_deps(struct symbol *sym); diff --git a/scripts/kconfig/lkc_proto.h b/scripts/kconfig/lkc_proto.h index ecdb9659b67d..d5398718ec2a 100644 --- a/scripts/kconfig/lkc_proto.h +++ b/scripts/kconfig/lkc_proto.h @@ -1,57 +1,52 @@ #include <stdarg.h> /* confdata.c */ -P(conf_parse,void,(const char *name)); -P(conf_read,int,(const char *name)); -P(conf_read_simple,int,(const char *name, int)); -P(conf_write_defconfig,int,(const char *name)); -P(conf_write,int,(const char *name)); -P(conf_write_autoconf,int,(void)); -P(conf_get_changed,bool,(void)); -P(conf_set_changed_callback, void,(void (*fn)(void))); -P(conf_set_message_callback, void,(void (*fn)(const char *fmt, va_list ap))); +void conf_parse(const char *name); +int conf_read(const char *name); +int conf_read_simple(const char *name, int); +int conf_write_defconfig(const char *name); +int conf_write(const char *name); +int conf_write_autoconf(void); +bool conf_get_changed(void); +void conf_set_changed_callback(void (*fn)(void)); +void conf_set_message_callback(void (*fn)(const char *fmt, va_list ap)); /* menu.c */ -P(rootmenu,struct menu,); +extern struct menu rootmenu; -P(menu_is_empty, bool, (struct menu *menu)); -P(menu_is_visible, bool, (struct menu *menu)); -P(menu_has_prompt, bool, (struct menu *menu)); -P(menu_get_prompt,const char *,(struct menu *menu)); -P(menu_get_root_menu,struct menu *,(struct menu *menu)); -P(menu_get_parent_menu,struct menu *,(struct menu *menu)); -P(menu_has_help,bool,(struct menu *menu)); -P(menu_get_help,const char *,(struct menu *menu)); -P(get_symbol_str, void, (struct gstr *r, struct symbol *sym, struct list_head - *head)); -P(get_relations_str, struct gstr, (struct symbol **sym_arr, struct list_head - *head)); -P(menu_get_ext_help,void,(struct menu *menu, struct gstr *help)); +bool menu_is_empty(struct menu *menu); +bool menu_is_visible(struct menu *menu); +bool menu_has_prompt(struct menu *menu); +const char * menu_get_prompt(struct menu *menu); +struct menu * menu_get_root_menu(struct menu *menu); +struct menu * menu_get_parent_menu(struct menu *menu); +bool menu_has_help(struct menu *menu); +const char * menu_get_help(struct menu *menu); +struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); +void menu_get_ext_help(struct menu *menu, struct gstr *help); /* symbol.c */ -P(symbol_hash,struct symbol *,[SYMBOL_HASHSIZE]); +extern struct symbol * symbol_hash[SYMBOL_HASHSIZE]; -P(sym_lookup,struct symbol *,(const char *name, int flags)); -P(sym_find,struct symbol *,(const char *name)); -P(sym_expand_string_value,const char *,(const char *in)); -P(sym_escape_string_value, const char *,(const char *in)); -P(sym_re_search,struct symbol **,(const char *pattern)); -P(sym_type_name,const char *,(enum symbol_type type)); -P(sym_calc_value,void,(struct symbol *sym)); -P(sym_get_type,enum symbol_type,(struct symbol *sym)); -P(sym_tristate_within_range,bool,(struct symbol *sym,tristate tri)); -P(sym_set_tristate_value,bool,(struct symbol *sym,tristate tri)); -P(sym_toggle_tristate_value,tristate,(struct symbol *sym)); -P(sym_string_valid,bool,(struct symbol *sym, const char *newval)); -P(sym_string_within_range,bool,(struct symbol *sym, const char *str)); -P(sym_set_string_value,bool,(struct symbol *sym, const char *newval)); -P(sym_is_changable,bool,(struct symbol *sym)); -P(sym_get_choice_prop,struct property *,(struct symbol *sym)); -P(sym_get_default_prop,struct property *,(struct symbol *sym)); -P(sym_get_string_value,const char *,(struct symbol *sym)); +struct symbol * sym_lookup(const char *name, int flags); +struct symbol * sym_find(const char *name); +const char * sym_expand_string_value(const char *in); +const char * sym_escape_string_value(const char *in); +struct symbol ** sym_re_search(const char *pattern); +const char * sym_type_name(enum symbol_type type); +void sym_calc_value(struct symbol *sym); +enum symbol_type sym_get_type(struct symbol *sym); +bool sym_tristate_within_range(struct symbol *sym,tristate tri); +bool sym_set_tristate_value(struct symbol *sym,tristate tri); +tristate sym_toggle_tristate_value(struct symbol *sym); +bool sym_string_valid(struct symbol *sym, const char *newval); +bool sym_string_within_range(struct symbol *sym, const char *str); +bool sym_set_string_value(struct symbol *sym, const char *newval); +bool sym_is_changable(struct symbol *sym); +struct property * sym_get_choice_prop(struct symbol *sym); +const char * sym_get_string_value(struct symbol *sym); -P(prop_get_type_name,const char *,(enum prop_type type)); +const char * prop_get_type_name(enum prop_type type); /* expr.c */ -P(expr_compare_type,int,(enum expr_type t1, enum expr_type t2)); -P(expr_print,void,(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken)); +void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken); diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 4dd37552abc2..315ce2c7cb9d 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c @@ -279,6 +279,7 @@ static int child_count; static int single_menu_mode; static int show_all_options; static int save_and_exit; +static int silent; static void conf(struct menu *menu, struct menu *active_menu); static void conf_choice(struct menu *menu); @@ -777,10 +778,12 @@ static void conf_message_callback(const char *fmt, va_list ap) char buf[PATH_MAX+1]; vsnprintf(buf, sizeof(buf), fmt, ap); - if (save_and_exit) - printf("%s", buf); - else + if (save_and_exit) { + if (!silent) + printf("%s", buf); + } else { show_textbox(NULL, buf, 6, 60); + } } static void show_help(struct menu *menu) @@ -977,16 +980,18 @@ static int handle_exit(void) } /* fall through */ case -1: - printf(_("\n\n" - "*** End of the configuration.\n" - "*** Execute 'make' to start the build or try 'make help'." - "\n\n")); + if (!silent) + printf(_("\n\n" + "*** End of the configuration.\n" + "*** Execute 'make' to start the build or try 'make help'." + "\n\n")); res = 0; break; default: - fprintf(stderr, _("\n\n" - "Your configuration changes were NOT saved." - "\n\n")); + if (!silent) + fprintf(stderr, _("\n\n" + "Your configuration changes were NOT saved." + "\n\n")); if (res != KEY_ESC) res = 0; } @@ -1010,6 +1015,12 @@ int main(int ac, char **av) signal(SIGINT, sig_handler); + if (ac > 1 && strcmp(av[1], "-s") == 0) { + silent = 1; + /* Silence conf_read() until the real callback is set up */ + conf_set_message_callback(NULL); + av++; + } conf_parse(av[1]); conf_read(NULL); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 72c9dba84c5d..b05cc3d4a9be 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -125,7 +125,7 @@ void menu_set_type(int type) sym_type_name(sym->type), sym_type_name(type)); } -struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) +static struct property *menu_add_prop(enum prop_type type, char *prompt, struct expr *expr, struct expr *dep) { struct property *prop = prop_alloc(type, current_entry->sym); @@ -615,7 +615,7 @@ static struct property *get_symbol_prop(struct symbol *sym) /* * head is optional and may be NULL */ -void get_symbol_str(struct gstr *r, struct symbol *sym, +static void get_symbol_str(struct gstr *r, struct symbol *sym, struct list_head *head) { bool hit; diff --git a/scripts/kconfig/merge_config.sh b/scripts/kconfig/merge_config.sh index 2ab91b9b100d..ec8e20350a64 100755 --- a/scripts/kconfig/merge_config.sh +++ b/scripts/kconfig/merge_config.sh @@ -35,7 +35,7 @@ usage() { echo " -O dir to put generated output files" } -MAKE=true +RUNMAKE=true ALLTARGET=alldefconfig WARNREDUN=false OUTPUT=. @@ -48,7 +48,7 @@ while true; do continue ;; "-m") - MAKE=false + RUNMAKE=false shift continue ;; @@ -85,6 +85,11 @@ fi INITFILE=$1 shift; +if [ ! -r "$INITFILE" ]; then + echo "The base file '$INITFILE' does not exist. Exit." >&2 + exit 1 +fi + MERGE_LIST=$* SED_CONFIG_EXP="s/^\(# \)\{0,1\}\(CONFIG_[a-zA-Z0-9_]*\)[= ].*/\2/p" TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) @@ -92,31 +97,29 @@ TMP_FILE=$(mktemp ./.tmp.config.XXXXXXXXXX) echo "Using $INITFILE as base" cat $INITFILE > $TMP_FILE -# Merge files, printing warnings on overrided values +# Merge files, printing warnings on overridden values for MERGE_FILE in $MERGE_LIST ; do echo "Merging $MERGE_FILE" CFG_LIST=$(sed -n "$SED_CONFIG_EXP" $MERGE_FILE) for CFG in $CFG_LIST ; do - grep -q -w $CFG $TMP_FILE - if [ $? -eq 0 ] ; then - PREV_VAL=$(grep -w $CFG $TMP_FILE) - NEW_VAL=$(grep -w $CFG $MERGE_FILE) - if [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then + grep -q -w $CFG $TMP_FILE || continue + PREV_VAL=$(grep -w $CFG $TMP_FILE) + NEW_VAL=$(grep -w $CFG $MERGE_FILE) + if [ "x$PREV_VAL" != "x$NEW_VAL" ] ; then echo Value of $CFG is redefined by fragment $MERGE_FILE: echo Previous value: $PREV_VAL echo New value: $NEW_VAL echo - elif [ "$WARNREDUN" = "true" ]; then + elif [ "$WARNREDUN" = "true" ]; then echo Value of $CFG is redundant by fragment $MERGE_FILE: - fi - sed -i "/$CFG[ =]/d" $TMP_FILE fi + sed -i "/$CFG[ =]/d" $TMP_FILE done cat $MERGE_FILE >> $TMP_FILE done -if [ "$MAKE" = "false" ]; then +if [ "$RUNMAKE" = "false" ]; then cp $TMP_FILE $OUTPUT/.config echo "#" echo "# merged configuration written to $OUTPUT/.config (needs make)" diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index 984489ef2b46..d42d534a66cd 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -1482,6 +1482,11 @@ int main(int ac, char **av) bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); + if (ac > 1 && strcmp(av[1], "-s") == 0) { + /* Silence conf_read() until the real callback is set up */ + conf_set_message_callback(NULL); + av++; + } conf_parse(av[1]); conf_read(NULL); diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 9d3b04b0769c..c3bb7fe8dfa6 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -1746,7 +1746,7 @@ static const char *progname; static void usage(void) { - printf(_("%s <config>\n"), progname); + printf(_("%s [-s] <config>\n"), progname); exit(0); } @@ -1762,6 +1762,9 @@ int main(int ac, char** av) configApp = new QApplication(ac, av); if (ac > 1 && av[1][0] == '-') { switch (av[1][1]) { + case 's': + conf_set_message_callback(NULL); + break; case 'h': case '?': usage(); diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 7caabdb51c64..6731377f9bb2 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -112,7 +112,7 @@ struct property *sym_get_env_prop(struct symbol *sym) return NULL; } -struct property *sym_get_default_prop(struct symbol *sym) +static struct property *sym_get_default_prop(struct symbol *sym) { struct property *prop; @@ -186,6 +186,26 @@ static void sym_validate_range(struct symbol *sym) sym->curr.val = strdup(str); } +static void sym_set_changed(struct symbol *sym) +{ + struct property *prop; + + sym->flags |= SYMBOL_CHANGED; + for (prop = sym->prop; prop; prop = prop->next) { + if (prop->menu) + prop->menu->flags |= MENU_CHANGED; + } +} + +static void sym_set_all_changed(void) +{ + struct symbol *sym; + int i; + + for_all_symbols(i, sym) + sym_set_changed(sym); +} + static void sym_calc_visibility(struct symbol *sym) { struct property *prop; @@ -451,26 +471,6 @@ void sym_clear_all_valid(void) sym_calc_value(modules_sym); } -void sym_set_changed(struct symbol *sym) -{ - struct property *prop; - - sym->flags |= SYMBOL_CHANGED; - for (prop = sym->prop; prop; prop = prop->next) { - if (prop->menu) - prop->menu->flags |= MENU_CHANGED; - } -} - -void sym_set_all_changed(void) -{ - struct symbol *sym; - int i; - - for_all_symbols(i, sym) - sym_set_changed(sym); -} - bool sym_tristate_within_range(struct symbol *sym, tristate val) { int type = sym_get_type(sym); diff --git a/scripts/kconfig/util.c b/scripts/kconfig/util.c index 94f9c83e324f..0e76042473cc 100644 --- a/scripts/kconfig/util.c +++ b/scripts/kconfig/util.c @@ -88,16 +88,6 @@ struct gstr str_new(void) return gs; } -/* Allocate and assign growable string */ -struct gstr str_assign(const char *s) -{ - struct gstr gs; - gs.s = strdup(s); - gs.len = strlen(s) + 1; - gs.max_width = 0; - return gs; -} - /* Free storage for growable string */ void str_free(struct gstr *gs) { diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 69fdf3bc765b..b526ddc3add5 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -211,7 +211,7 @@ static inline void print_ipv4_addr(struct audit_buffer *ab, __be32 addr, static void dump_common_audit_data(struct audit_buffer *ab, struct common_audit_data *a) { - struct task_struct *tsk = current; + char comm[sizeof(current->comm)]; /* * To keep stack sizes in check force programers to notice if they @@ -220,8 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab, */ BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); - audit_log_format(ab, " pid=%d comm=", task_pid_nr(tsk)); - audit_log_untrustedstring(ab, tsk->comm); + audit_log_format(ab, " pid=%d comm=", task_pid_nr(current)); + audit_log_untrustedstring(ab, memcpy(comm, current->comm, sizeof(comm))); switch (a->type) { case LSM_AUDIT_DATA_NONE: @@ -276,16 +276,19 @@ static void dump_common_audit_data(struct audit_buffer *ab, audit_log_format(ab, " ino=%lu", inode->i_ino); break; } - case LSM_AUDIT_DATA_TASK: - tsk = a->u.tsk; + case LSM_AUDIT_DATA_TASK: { + struct task_struct *tsk = a->u.tsk; if (tsk) { pid_t pid = task_pid_nr(tsk); if (pid) { + char comm[sizeof(tsk->comm)]; audit_log_format(ab, " pid=%d comm=", pid); - audit_log_untrustedstring(ab, tsk->comm); + audit_log_untrustedstring(ab, + memcpy(comm, tsk->comm, sizeof(comm))); } } break; + } case LSM_AUDIT_DATA_NET: if (a->u.net->sk) { struct sock *sk = a->u.net->sk; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index afcc0aed9393..3c17dda9571d 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -724,12 +724,10 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid, rcu_read_lock(); node = avc_lookup(ssid, tsid, tclass); - if (unlikely(!node)) { + if (unlikely(!node)) node = avc_compute_av(ssid, tsid, tclass, avd); - } else { + else memcpy(avd, &node->ae.avd, sizeof(*avd)); - avd = &node->ae.avd; - } denied = requested & ~(avd->allowed); if (unlikely(denied)) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index a3dd9faa19c0..b64f2772b030 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -25,10 +25,43 @@ static struct kmem_cache *avtab_node_cachep; -static inline int avtab_hash(struct avtab_key *keyp, u16 mask) +/* Based on MurmurHash3, written by Austin Appleby and placed in the + * public domain. + */ +static inline int avtab_hash(struct avtab_key *keyp, u32 mask) { - return ((keyp->target_class + (keyp->target_type << 2) + - (keyp->source_type << 9)) & mask); + static const u32 c1 = 0xcc9e2d51; + static const u32 c2 = 0x1b873593; + static const u32 r1 = 15; + static const u32 r2 = 13; + static const u32 m = 5; + static const u32 n = 0xe6546b64; + + u32 hash = 0; + +#define mix(input) { \ + u32 v = input; \ + v *= c1; \ + v = (v << r1) | (v >> (32 - r1)); \ + v *= c2; \ + hash ^= v; \ + hash = (hash << r2) | (hash >> (32 - r2)); \ + hash = hash * m + n; \ +} + + mix(keyp->target_class); + mix(keyp->target_type); + mix(keyp->source_type); + +#undef mix + + hash ^= hash >> 16; + hash *= 0x85ebca6b; + hash ^= hash >> 13; + hash *= 0xc2b2ae35; + hash ^= hash >> 16; + + return hash & mask; } static struct avtab_node* @@ -46,8 +79,12 @@ avtab_insert_node(struct avtab *h, int hvalue, newnode->next = prev->next; prev->next = newnode; } else { - newnode->next = h->htable[hvalue]; - h->htable[hvalue] = newnode; + newnode->next = flex_array_get_ptr(h->htable, hvalue); + if (flex_array_put_ptr(h->htable, hvalue, newnode, + GFP_KERNEL|__GFP_ZERO)) { + kmem_cache_free(avtab_node_cachep, newnode); + return NULL; + } } h->nel++; @@ -64,7 +101,7 @@ static int avtab_insert(struct avtab *h, struct avtab_key *key, struct avtab_dat return -EINVAL; hvalue = avtab_hash(key, h->mask); - for (prev = NULL, cur = h->htable[hvalue]; + for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); cur; prev = cur, cur = cur->next) { if (key->source_type == cur->key.source_type && @@ -104,7 +141,7 @@ avtab_insert_nonunique(struct avtab *h, struct avtab_key *key, struct avtab_datu if (!h || !h->htable) return NULL; hvalue = avtab_hash(key, h->mask); - for (prev = NULL, cur = h->htable[hvalue]; + for (prev = NULL, cur = flex_array_get_ptr(h->htable, hvalue); cur; prev = cur, cur = cur->next) { if (key->source_type == cur->key.source_type && @@ -135,7 +172,8 @@ struct avtab_datum *avtab_search(struct avtab *h, struct avtab_key *key) return NULL; hvalue = avtab_hash(key, h->mask); - for (cur = h->htable[hvalue]; cur; cur = cur->next) { + for (cur = flex_array_get_ptr(h->htable, hvalue); cur; + cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && key->target_class == cur->key.target_class && @@ -170,7 +208,8 @@ avtab_search_node(struct avtab *h, struct avtab_key *key) return NULL; hvalue = avtab_hash(key, h->mask); - for (cur = h->htable[hvalue]; cur; cur = cur->next) { + for (cur = flex_array_get_ptr(h->htable, hvalue); cur; + cur = cur->next) { if (key->source_type == cur->key.source_type && key->target_type == cur->key.target_type && key->target_class == cur->key.target_class && @@ -228,15 +267,14 @@ void avtab_destroy(struct avtab *h) return; for (i = 0; i < h->nslot; i++) { - cur = h->htable[i]; + cur = flex_array_get_ptr(h->htable, i); while (cur) { temp = cur; cur = cur->next; kmem_cache_free(avtab_node_cachep, temp); } - h->htable[i] = NULL; } - kfree(h->htable); + flex_array_free(h->htable); h->htable = NULL; h->nslot = 0; h->mask = 0; @@ -251,7 +289,7 @@ int avtab_init(struct avtab *h) int avtab_alloc(struct avtab *h, u32 nrules) { - u16 mask = 0; + u32 mask = 0; u32 shift = 0; u32 work = nrules; u32 nslot = 0; @@ -270,7 +308,8 @@ int avtab_alloc(struct avtab *h, u32 nrules) nslot = MAX_AVTAB_HASH_BUCKETS; mask = nslot - 1; - h->htable = kcalloc(nslot, sizeof(*(h->htable)), GFP_KERNEL); + h->htable = flex_array_alloc(sizeof(struct avtab_node *), nslot, + GFP_KERNEL | __GFP_ZERO); if (!h->htable) return -ENOMEM; @@ -293,7 +332,7 @@ void avtab_hash_eval(struct avtab *h, char *tag) max_chain_len = 0; chain2_len_sum = 0; for (i = 0; i < h->nslot; i++) { - cur = h->htable[i]; + cur = flex_array_get_ptr(h->htable, i); if (cur) { slots_used++; chain_len = 0; @@ -534,7 +573,8 @@ int avtab_write(struct policydb *p, struct avtab *a, void *fp) return rc; for (i = 0; i < a->nslot; i++) { - for (cur = a->htable[i]; cur; cur = cur->next) { + for (cur = flex_array_get_ptr(a->htable, i); cur; + cur = cur->next) { rc = avtab_write_item(p, cur, fp); if (rc) return rc; diff --git a/security/selinux/ss/avtab.h b/security/selinux/ss/avtab.h index 63ce2f9e441d..adb451cd44f9 100644 --- a/security/selinux/ss/avtab.h +++ b/security/selinux/ss/avtab.h @@ -23,6 +23,8 @@ #ifndef _SS_AVTAB_H_ #define _SS_AVTAB_H_ +#include <linux/flex_array.h> + struct avtab_key { u16 source_type; /* source type */ u16 target_type; /* target type */ @@ -51,10 +53,10 @@ struct avtab_node { }; struct avtab { - struct avtab_node **htable; + struct flex_array *htable; u32 nel; /* number of elements */ u32 nslot; /* number of hash slots */ - u16 mask; /* mask to compute hash func */ + u32 mask; /* mask to compute hash func */ }; @@ -84,7 +86,7 @@ struct avtab_node *avtab_search_node_next(struct avtab_node *node, int specified void avtab_cache_init(void); void avtab_cache_destroy(void); -#define MAX_AVTAB_HASH_BITS 11 +#define MAX_AVTAB_HASH_BITS 16 #define MAX_AVTAB_HASH_BUCKETS (1 << MAX_AVTAB_HASH_BITS) #endif /* _SS_AVTAB_H_ */ diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index d307b37ddc2b..e1088842232c 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -654,19 +654,15 @@ int mls_import_netlbl_cat(struct context *context, rc = ebitmap_netlbl_import(&context->range.level[0].cat, secattr->attr.mls.cat); - if (rc != 0) - goto import_netlbl_cat_failure; - - rc = ebitmap_cpy(&context->range.level[1].cat, - &context->range.level[0].cat); - if (rc != 0) + if (rc) goto import_netlbl_cat_failure; + memcpy(&context->range.level[1].cat, &context->range.level[0].cat, + sizeof(context->range.level[0].cat)); return 0; import_netlbl_cat_failure: ebitmap_destroy(&context->range.level[0].cat); - ebitmap_destroy(&context->range.level[1].cat); return rc; } #endif /* CONFIG_NETLABEL */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index a1d3944751b9..9e2d82070915 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -3179,13 +3179,9 @@ int security_netlbl_secattr_to_sid(struct netlbl_lsm_secattr *secattr, ctx_new.type = ctx->type; mls_import_netlbl_lvl(&ctx_new, secattr); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) { - rc = ebitmap_netlbl_import(&ctx_new.range.level[0].cat, - secattr->attr.mls.cat); + rc = mls_import_netlbl_cat(&ctx_new, secattr); if (rc) goto out; - memcpy(&ctx_new.range.level[1].cat, - &ctx_new.range.level[0].cat, - sizeof(ctx_new.range.level[0].cat)); } rc = -EIDRM; if (!mls_context_isvalid(&policydb, &ctx_new)) diff --git a/security/smack/smack.h b/security/smack/smack.h index 67ccb7b2b89b..49eada6266ec 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -105,6 +105,7 @@ struct task_smack { #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ #define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ #define SMK_INODE_CHANGED 0x04 /* smack was transmuted */ +#define SMK_INODE_IMPURE 0x08 /* involved in an impure transaction */ /* * A label access rule. @@ -193,6 +194,10 @@ struct smk_port_label { #define MAY_LOCK 0x00002000 /* Locks should be writes, but ... */ #define MAY_BRINGUP 0x00004000 /* Report use of this rule */ +#define SMACK_BRINGUP_ALLOW 1 /* Allow bringup mode */ +#define SMACK_UNCONFINED_SUBJECT 2 /* Allow unconfined label */ +#define SMACK_UNCONFINED_OBJECT 3 /* Allow unconfined label */ + /* * Just to make the common cases easier to deal with */ @@ -254,6 +259,9 @@ extern int smack_cipso_mapped; extern struct smack_known *smack_net_ambient; extern struct smack_known *smack_onlycap; extern struct smack_known *smack_syslog_label; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +extern struct smack_known *smack_unconfined; +#endif extern struct smack_known smack_cipso_option; extern int smack_ptrace_rule; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 1158430f5bb9..0f410fc56e33 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -130,7 +130,8 @@ int smk_access(struct smack_known *subject, struct smack_known *object, /* * Hardcoded comparisons. - * + */ + /* * A star subject can't access any object. */ if (subject == &smack_known_star) { @@ -189,10 +190,20 @@ int smk_access(struct smack_known *subject, struct smack_known *object, * succeed because of "b" rules. */ if (may & MAY_BRINGUP) - rc = MAY_BRINGUP; + rc = SMACK_BRINGUP_ALLOW; #endif out_audit: + +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + if (rc < 0) { + if (object == smack_unconfined) + rc = SMACK_UNCONFINED_OBJECT; + if (subject == smack_unconfined) + rc = SMACK_UNCONFINED_SUBJECT; + } +#endif + #ifdef CONFIG_AUDIT if (a) smack_log(subject->smk_known, object->smk_known, @@ -338,19 +349,16 @@ static void smack_log_callback(struct audit_buffer *ab, void *a) void smack_log(char *subject_label, char *object_label, int request, int result, struct smk_audit_info *ad) { +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + char request_buffer[SMK_NUM_ACCESS_TYPE + 5]; +#else char request_buffer[SMK_NUM_ACCESS_TYPE + 1]; +#endif struct smack_audit_data *sad; struct common_audit_data *a = &ad->a; -#ifdef CONFIG_SECURITY_SMACK_BRINGUP - /* - * The result may be positive in bringup mode. - */ - if (result > 0) - result = 0; -#endif /* check if we have to log the current event */ - if (result != 0 && (log_policy & SMACK_AUDIT_DENIED) == 0) + if (result < 0 && (log_policy & SMACK_AUDIT_DENIED) == 0) return; if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0) return; @@ -364,6 +372,21 @@ void smack_log(char *subject_label, char *object_label, int request, smack_str_from_perm(request_buffer, request); sad->subject = subject_label; sad->object = object_label; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + /* + * The result may be positive in bringup mode. + * A positive result is an allow, but not for normal reasons. + * Mark it as successful, but don't filter it out even if + * the logging policy says to do so. + */ + if (result == SMACK_UNCONFINED_SUBJECT) + strcat(request_buffer, "(US)"); + else if (result == SMACK_UNCONFINED_OBJECT) + strcat(request_buffer, "(UO)"); + + if (result > 0) + result = 0; +#endif sad->request = request_buffer; sad->result = result; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 1511965549b8..69fdc384af30 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -57,6 +57,13 @@ static struct kmem_cache *smack_inode_cache; int smack_enabled; #ifdef CONFIG_SECURITY_SMACK_BRINGUP +static char *smk_bu_mess[] = { + "Bringup Error", /* Unused */ + "Bringup", /* SMACK_BRINGUP_ALLOW */ + "Unconfined Subject", /* SMACK_UNCONFINED_SUBJECT */ + "Unconfined Object", /* SMACK_UNCONFINED_OBJECT */ +}; + static void smk_bu_mode(int mode, char *s) { int i = 0; @@ -87,9 +94,11 @@ static int smk_bu_note(char *note, struct smack_known *sskp, if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) %s\n", + pr_info("Smack %s: (%s %s %s) %s\n", smk_bu_mess[rc], sskp->smk_known, oskp->smk_known, acc, note); return 0; } @@ -106,9 +115,11 @@ static int smk_bu_current(char *note, struct smack_known *oskp, if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) %s %s\n", + pr_info("Smack %s: (%s %s %s) %s %s\n", smk_bu_mess[rc], tsp->smk_task->smk_known, oskp->smk_known, acc, current->comm, note); return 0; @@ -126,9 +137,11 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc) if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) %s to %s\n", + pr_info("Smack %s: (%s %s %s) %s to %s\n", smk_bu_mess[rc], tsp->smk_task->smk_known, smk_task->smk_known, acc, current->comm, otp->comm); return 0; @@ -141,14 +154,25 @@ static int smk_bu_task(struct task_struct *otp, int mode, int rc) static int smk_bu_inode(struct inode *inode, int mode, int rc) { struct task_smack *tsp = current_security(); + struct inode_smack *isp = inode->i_security; char acc[SMK_NUM_ACCESS_TYPE + 1]; + if (isp->smk_flags & SMK_INODE_IMPURE) + pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n", + inode->i_sb->s_id, inode->i_ino, current->comm); + if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; + if (rc == SMACK_UNCONFINED_SUBJECT && + (mode & (MAY_WRITE | MAY_APPEND))) + isp->smk_flags |= SMK_INODE_IMPURE; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) inode=(%s %ld) %s\n", - tsp->smk_task->smk_known, smk_of_inode(inode)->smk_known, acc, + + pr_info("Smack %s: (%s %s %s) inode=(%s %ld) %s\n", smk_bu_mess[rc], + tsp->smk_task->smk_known, isp->smk_inode->smk_known, acc, inode->i_sb->s_id, inode->i_ino, current->comm); return 0; } @@ -162,13 +186,20 @@ static int smk_bu_file(struct file *file, int mode, int rc) struct task_smack *tsp = current_security(); struct smack_known *sskp = tsp->smk_task; struct inode *inode = file_inode(file); + struct inode_smack *isp = inode->i_security; char acc[SMK_NUM_ACCESS_TYPE + 1]; + if (isp->smk_flags & SMK_INODE_IMPURE) + pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n", + inode->i_sb->s_id, inode->i_ino, current->comm); + if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %pD) %s\n", + pr_info("Smack %s: (%s %s %s) file=(%s %ld %pD) %s\n", smk_bu_mess[rc], sskp->smk_known, smk_of_inode(inode)->smk_known, acc, inode->i_sb->s_id, inode->i_ino, file, current->comm); @@ -185,13 +216,20 @@ static int smk_bu_credfile(const struct cred *cred, struct file *file, struct task_smack *tsp = cred->security; struct smack_known *sskp = tsp->smk_task; struct inode *inode = file->f_inode; + struct inode_smack *isp = inode->i_security; char acc[SMK_NUM_ACCESS_TYPE + 1]; + if (isp->smk_flags & SMK_INODE_IMPURE) + pr_info("Smack Unconfined Corruption: inode=(%s %ld) %s\n", + inode->i_sb->s_id, inode->i_ino, current->comm); + if (rc <= 0) return rc; + if (rc > SMACK_UNCONFINED_OBJECT) + rc = 0; smk_bu_mode(mode, acc); - pr_info("Smack Bringup: (%s %s %s) file=(%s %ld %pD) %s\n", + pr_info("Smack %s: (%s %s %s) file=(%s %ld %pD) %s\n", smk_bu_mess[rc], sskp->smk_known, smk_of_inode(inode)->smk_known, acc, inode->i_sb->s_id, inode->i_ino, file, current->comm); @@ -2449,7 +2487,21 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, static int smack_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { - if (family != PF_INET || sock->sk == NULL) + struct socket_smack *ssp; + + if (sock->sk == NULL) + return 0; + + /* + * Sockets created by kernel threads receive web label. + */ + if (unlikely(current->flags & PF_KTHREAD)) { + ssp = sock->sk->sk_security; + ssp->smk_in = &smack_known_web; + ssp->smk_out = &smack_known_web; + } + + if (family != PF_INET) return 0; /* * Set the outbound netlbl. @@ -3983,6 +4035,36 @@ static int smack_key_permission(key_ref_t key_ref, rc = smk_bu_note("key access", tkp, keyp->security, request, rc); return rc; } + +/* + * smack_key_getsecurity - Smack label tagging the key + * @key points to the key to be queried + * @_buffer points to a pointer that should be set to point to the + * resulting string (if no label or an error occurs). + * Return the length of the string (including terminating NUL) or -ve if + * an error. + * May also return 0 (and a NULL buffer pointer) if there is no label. + */ +static int smack_key_getsecurity(struct key *key, char **_buffer) +{ + struct smack_known *skp = key->security; + size_t length; + char *copy; + + if (key->security == NULL) { + *_buffer = NULL; + return 0; + } + + copy = kstrdup(skp->smk_known, GFP_KERNEL); + if (copy == NULL) + return -ENOMEM; + length = strlen(copy) + 1; + + *_buffer = copy; + return length; +} + #endif /* CONFIG_KEYS */ /* @@ -4307,6 +4389,7 @@ struct security_operations smack_ops = { .key_alloc = smack_key_alloc, .key_free = smack_key_free, .key_permission = smack_key_permission, + .key_getsecurity = smack_key_getsecurity, #endif /* CONFIG_KEYS */ /* Audit hooks */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index bce4e8f1b267..06f719ed63c9 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -54,6 +54,9 @@ enum smk_inos { SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ SMK_SYSLOG = 20, /* change syslog label) */ SMK_PTRACE = 21, /* set ptrace rule */ +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + SMK_UNCONFINED = 22, /* define an unconfined label */ +#endif }; /* @@ -61,7 +64,6 @@ enum smk_inos { */ static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); -static DEFINE_MUTEX(smack_syslog_lock); static DEFINE_MUTEX(smk_netlbladdr_lock); /* @@ -95,6 +97,16 @@ int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; */ struct smack_known *smack_onlycap; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +/* + * Allow one label to be unconfined. This is for + * debugging and application bring-up purposes only. + * It is bad and wrong, but everyone seems to expect + * to have it. + */ +struct smack_known *smack_unconfined; +#endif + /* * If this value is set restrict syslog use to the label specified. * It can be reset via smackfs/syslog @@ -1717,6 +1729,85 @@ static const struct file_operations smk_onlycap_ops = { .llseek = default_llseek, }; +#ifdef CONFIG_SECURITY_SMACK_BRINGUP +/** + * smk_read_unconfined - read() for smackfs/unconfined + * @filp: file pointer, not actually used + * @buf: where to put the result + * @cn: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_unconfined(struct file *filp, char __user *buf, + size_t cn, loff_t *ppos) +{ + char *smack = ""; + ssize_t rc = -EINVAL; + int asize; + + if (*ppos != 0) + return 0; + + if (smack_unconfined != NULL) + smack = smack_unconfined->smk_known; + + asize = strlen(smack) + 1; + + if (cn >= asize) + rc = simple_read_from_buffer(buf, cn, ppos, smack, asize); + + return rc; +} + +/** + * smk_write_unconfined - write() for smackfs/unconfined + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_unconfined(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data; + int rc = count; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + /* + * Should the null string be passed in unset the unconfined value. + * This seems like something to be careful with as usually + * smk_import only expects to return NULL for errors. It + * is usually the case that a nullstring or "\n" would be + * bad to pass to smk_import but in fact this is useful here. + * + * smk_import will also reject a label beginning with '-', + * so "-confine" will also work. + */ + if (copy_from_user(data, buf, count) != 0) + rc = -EFAULT; + else + smack_unconfined = smk_import_entry(data, count); + + kfree(data); + return rc; +} + +static const struct file_operations smk_unconfined_ops = { + .read = smk_read_unconfined, + .write = smk_write_unconfined, + .llseek = default_llseek, +}; +#endif /* CONFIG_SECURITY_SMACK_BRINGUP */ + /** * smk_read_logging - read() for /smack/logging * @filp: file pointer, not actually used @@ -2384,6 +2475,10 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR}, [SMK_PTRACE] = { "ptrace", &smk_ptrace_ops, S_IRUGO|S_IWUSR}, +#ifdef CONFIG_SECURITY_SMACK_BRINGUP + [SMK_UNCONFINED] = { + "unconfined", &smk_unconfined_ops, S_IRUGO|S_IWUSR}, +#endif /* last one */ {""} }; diff --git a/security/tomoyo/.gitignore b/security/tomoyo/.gitignore index 5caf1a6f5907..dc0f220a210b 100644 --- a/security/tomoyo/.gitignore +++ b/security/tomoyo/.gitignore @@ -1,2 +1,2 @@ builtin-policy.h -policy/ +policy/*.conf diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index 604e718d68d3..404dce66952a 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -6,6 +6,7 @@ config SECURITY_TOMOYO select SECURITY_PATH select SECURITY_NETWORK select SRCU + select BUILD_BIN2C default n help This selects TOMOYO Linux, pathname-based access control. diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 56a0c7be409e..65dbcb2fd850 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1,48 +1,15 @@ obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o -$(obj)/policy/profile.conf: - @mkdir -p $(obj)/policy/ - @echo Creating an empty policy/profile.conf - @touch $@ - -$(obj)/policy/exception_policy.conf: - @mkdir -p $(obj)/policy/ - @echo Creating a default policy/exception_policy.conf - @echo initialize_domain /sbin/modprobe from any >> $@ - @echo initialize_domain /sbin/hotplug from any >> $@ - -$(obj)/policy/domain_policy.conf: - @mkdir -p $(obj)/policy/ - @echo Creating an empty policy/domain_policy.conf - @touch $@ - -$(obj)/policy/manager.conf: - @mkdir -p $(obj)/policy/ - @echo Creating an empty policy/manager.conf - @touch $@ - -$(obj)/policy/stat.conf: - @mkdir -p $(obj)/policy/ - @echo Creating an empty policy/stat.conf - @touch $@ - -$(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf - @echo Generating built-in policy for TOMOYO 2.5.x. - @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp - @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp - @echo "\"\";" >> $@.tmp - @echo "static char tomoyo_builtin_exception_policy[] __initdata =" >> $@.tmp - @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/exception_policy.conf >> $@.tmp - @echo "\"\";" >> $@.tmp - @echo "static char tomoyo_builtin_domain_policy[] __initdata =" >> $@.tmp - @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/domain_policy.conf >> $@.tmp - @echo "\"\";" >> $@.tmp - @echo "static char tomoyo_builtin_manager[] __initdata =" >> $@.tmp - @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/manager.conf >> $@.tmp - @echo "\"\";" >> $@.tmp - @echo "static char tomoyo_builtin_stat[] __initdata =" >> $@.tmp - @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/stat.conf >> $@.tmp - @echo "\"\";" >> $@.tmp - @mv $@.tmp $@ +targets += builtin-policy.h +define do_policy +echo "static char tomoyo_builtin_$(1)[] __initdata ="; \ +$(objtree)/scripts/basic/bin2c <$(firstword $(wildcard $(obj)/policy/$(1).conf $(srctree)/$(src)/policy/$(1).conf.default) /dev/null); \ +echo ";" +endef +quiet_cmd_policy = POLICY $@ + cmd_policy = ($(call do_policy,profile); $(call do_policy,exception_policy); $(call do_policy,domain_policy); $(call do_policy,manager); $(call do_policy,stat)) >$@ + +$(obj)/builtin-policy.h: $(wildcard $(obj)/policy/*.conf $(src)/policy/*.conf.default) FORCE + $(call if_changed,policy) $(obj)/common.o: $(obj)/builtin-policy.h diff --git a/security/tomoyo/policy/exception_policy.conf.default b/security/tomoyo/policy/exception_policy.conf.default new file mode 100644 index 000000000000..2678df4964ee --- /dev/null +++ b/security/tomoyo/policy/exception_policy.conf.default @@ -0,0 +1,2 @@ +initialize_domain /sbin/modprobe from any +initialize_domain /sbin/hotplug from any diff --git a/security/yama/Kconfig b/security/yama/Kconfig index 20ef5143c0c0..3123e1da2fed 100644 --- a/security/yama/Kconfig +++ b/security/yama/Kconfig @@ -1,8 +1,6 @@ config SECURITY_YAMA bool "Yama support" depends on SECURITY - select SECURITYFS - select SECURITY_PATH default n help This selects Yama, which extends DAC support with additional diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 13c88fbcf037..24aae2ae2b30 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -379,20 +379,17 @@ static struct security_operations yama_ops = { static int yama_dointvec_minmax(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - int rc; + struct ctl_table table_copy; if (write && !capable(CAP_SYS_PTRACE)) return -EPERM; - rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos); - if (rc) - return rc; - /* Lock the max value if it ever gets set. */ - if (write && *(int *)table->data == *(int *)table->extra2) - table->extra1 = table->extra2; + table_copy = *table; + if (*(int *)table_copy.data == *(int *)table_copy.extra2) + table_copy.extra1 = table_copy.extra2; - return rc; + return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos); } static int zero; diff --git a/sound/Kconfig b/sound/Kconfig index c710ce2c5c37..5a240e050ae6 100644 --- a/sound/Kconfig +++ b/sound/Kconfig @@ -76,6 +76,8 @@ source "sound/isa/Kconfig" source "sound/pci/Kconfig" +source "sound/hda/Kconfig" + source "sound/ppc/Kconfig" source "sound/aoa/Kconfig" diff --git a/sound/Makefile b/sound/Makefile index ce9132b1c395..77320709fd26 100644 --- a/sound/Makefile +++ b/sound/Makefile @@ -6,7 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \ - firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ + firewire/ sparc/ spi/ parisc/ pcmcia/ mips/ soc/ atmel/ hda/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c index b9737fae656a..1cbf210080a1 100644 --- a/sound/aoa/soundbus/i2sbus/core.c +++ b/sound/aoa/soundbus/i2sbus/core.c @@ -31,7 +31,7 @@ module_param(force, int, 0444); MODULE_PARM_DESC(force, "Force loading i2sbus even when" " no layout-id property is present"); -static struct of_device_id i2sbus_match[] = { +static const struct of_device_id i2sbus_match[] = { { .name = "i2s" }, { } }; diff --git a/sound/core/control.c b/sound/core/control.c index eeb691d1911f..196a6fe100ca 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -192,36 +192,41 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, EXPORT_SYMBOL(snd_ctl_notify); /** - * snd_ctl_new - create a control instance from the template - * @control: the control template - * @access: the default control access + * snd_ctl_new - create a new control instance with some elements + * @kctl: the pointer to store new control instance + * @count: the number of elements in this control + * @access: the default access flags for elements in this control + * @file: given when locking these elements * - * Allocates a new struct snd_kcontrol instance and copies the given template - * to the new instance. It does not copy volatile data (access). + * Allocates a memory object for a new control instance. The instance has + * elements as many as the given number (@count). Each element has given + * access permissions (@access). Each element is locked when @file is given. * - * Return: The pointer of the new instance, or %NULL on failure. + * Return: 0 on success, error code on failure */ -static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, - unsigned int access) +static int snd_ctl_new(struct snd_kcontrol **kctl, unsigned int count, + unsigned int access, struct snd_ctl_file *file) { - struct snd_kcontrol *kctl; + unsigned int size; unsigned int idx; - if (snd_BUG_ON(!control || !control->count)) - return NULL; + if (count == 0 || count > MAX_CONTROL_COUNT) + return -EINVAL; - if (control->count > MAX_CONTROL_COUNT) - return NULL; + size = sizeof(struct snd_kcontrol); + size += sizeof(struct snd_kcontrol_volatile) * count; - kctl = kzalloc(sizeof(*kctl) + sizeof(struct snd_kcontrol_volatile) * control->count, GFP_KERNEL); - if (kctl == NULL) { - pr_err("ALSA: Cannot allocate control instance\n"); - return NULL; + *kctl = kzalloc(size, GFP_KERNEL); + if (!*kctl) + return -ENOMEM; + + for (idx = 0; idx < count; idx++) { + (*kctl)->vd[idx].access = access; + (*kctl)->vd[idx].owner = file; } - *kctl = *control; - for (idx = 0; idx < kctl->count; idx++) - kctl->vd[idx].access = access; - return kctl; + (*kctl)->count = count; + + return 0; } /** @@ -238,37 +243,53 @@ static struct snd_kcontrol *snd_ctl_new(struct snd_kcontrol *control, struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol, void *private_data) { - struct snd_kcontrol kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; + int err; if (snd_BUG_ON(!ncontrol || !ncontrol->info)) return NULL; - memset(&kctl, 0, sizeof(kctl)); - kctl.id.iface = ncontrol->iface; - kctl.id.device = ncontrol->device; - kctl.id.subdevice = ncontrol->subdevice; + + count = ncontrol->count; + if (count == 0) + count = 1; + + access = ncontrol->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); + + err = snd_ctl_new(&kctl, count, access, NULL); + if (err < 0) + return NULL; + + /* The 'numid' member is decided when calling snd_ctl_add(). */ + kctl->id.iface = ncontrol->iface; + kctl->id.device = ncontrol->device; + kctl->id.subdevice = ncontrol->subdevice; if (ncontrol->name) { - strlcpy(kctl.id.name, ncontrol->name, sizeof(kctl.id.name)); - if (strcmp(ncontrol->name, kctl.id.name) != 0) + strlcpy(kctl->id.name, ncontrol->name, sizeof(kctl->id.name)); + if (strcmp(ncontrol->name, kctl->id.name) != 0) pr_warn("ALSA: Control name '%s' truncated to '%s'\n", - ncontrol->name, kctl.id.name); + ncontrol->name, kctl->id.name); } - kctl.id.index = ncontrol->index; - kctl.count = ncontrol->count ? ncontrol->count : 1; - access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_VOLATILE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE| - SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND| - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)); - kctl.info = ncontrol->info; - kctl.get = ncontrol->get; - kctl.put = ncontrol->put; - kctl.tlv.p = ncontrol->tlv.p; - kctl.private_value = ncontrol->private_value; - kctl.private_data = private_data; - return snd_ctl_new(&kctl, access); + kctl->id.index = ncontrol->index; + + kctl->info = ncontrol->info; + kctl->get = ncontrol->get; + kctl->put = ncontrol->put; + kctl->tlv.p = ncontrol->tlv.p; + + kctl->private_value = ncontrol->private_value; + kctl->private_data = private_data; + + return kctl; } EXPORT_SYMBOL(snd_ctl_new1); @@ -557,6 +578,7 @@ error: * * Finds the control instance with the given id, and activate or * inactivate the control together with notification, if changed. + * The given ID data is filled with full information. * * Return: 0 if unchanged, 1 if changed, or a negative error code on failure. */ @@ -586,6 +608,7 @@ int snd_ctl_activate_id(struct snd_card *card, struct snd_ctl_elem_id *id, goto unlock; vd->access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; } + snd_ctl_build_ioff(id, kctl, index_offset); ret = 1; unlock: up_write(&card->controls_rwsem); @@ -1006,7 +1029,7 @@ static int snd_ctl_elem_unlock(struct snd_ctl_file *file, struct user_element { struct snd_ctl_elem_info info; struct snd_card *card; - void *elem_data; /* element data */ + char *elem_data; /* element data */ unsigned long elem_data_size; /* size of element data in bytes */ void *tlv_data; /* TLV data */ unsigned long tlv_data_size; /* TLV data size */ @@ -1017,8 +1040,12 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { struct user_element *ue = kcontrol->private_data; + unsigned int offset; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); + return 0; } @@ -1028,10 +1055,13 @@ static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol, struct user_element *ue = kcontrol->private_data; const char *names; unsigned int item; + unsigned int offset; item = uinfo->value.enumerated.item; + offset = snd_ctl_get_ioff(kcontrol, &uinfo->id); *uinfo = ue->info; + snd_ctl_build_ioff(&uinfo->id, kcontrol, offset); item = min(item, uinfo->value.enumerated.items - 1); uinfo->value.enumerated.item = item; @@ -1048,9 +1078,12 @@ static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *src = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - memcpy(&ucontrol->value, ue->elem_data, ue->elem_data_size); + memcpy(&ucontrol->value, src, size); mutex_unlock(&ue->card->user_ctl_lock); return 0; } @@ -1060,11 +1093,14 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol, { int change; struct user_element *ue = kcontrol->private_data; + unsigned int size = ue->elem_data_size; + char *dst = ue->elem_data + + snd_ctl_get_ioff(kcontrol, &ucontrol->id) * size; mutex_lock(&ue->card->user_ctl_lock); - change = memcmp(&ucontrol->value, ue->elem_data, ue->elem_data_size) != 0; + change = memcmp(&ucontrol->value, dst, size) != 0; if (change) - memcpy(ue->elem_data, &ucontrol->value, ue->elem_data_size); + memcpy(dst, &ucontrol->value, size); mutex_unlock(&ue->card->user_ctl_lock); return change; } @@ -1078,7 +1114,7 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol, int change = 0; void *new_data; - if (op_flag > 0) { + if (op_flag == SNDRV_CTL_TLV_OP_WRITE) { if (size > 1024 * 128) /* sane value */ return -EINVAL; @@ -1161,84 +1197,103 @@ static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol) static int snd_ctl_elem_add(struct snd_ctl_file *file, struct snd_ctl_elem_info *info, int replace) { + /* The capacity of struct snd_ctl_elem_value.value.*/ + static const unsigned int value_sizes[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_INTEGER] = sizeof(long), + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = sizeof(unsigned int), + [SNDRV_CTL_ELEM_TYPE_BYTES] = sizeof(unsigned char), + [SNDRV_CTL_ELEM_TYPE_IEC958] = sizeof(struct snd_aes_iec958), + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = sizeof(long long), + }; + static const unsigned int max_value_counts[] = { + [SNDRV_CTL_ELEM_TYPE_BOOLEAN] = 128, + [SNDRV_CTL_ELEM_TYPE_INTEGER] = 128, + [SNDRV_CTL_ELEM_TYPE_ENUMERATED] = 128, + [SNDRV_CTL_ELEM_TYPE_BYTES] = 512, + [SNDRV_CTL_ELEM_TYPE_IEC958] = 1, + [SNDRV_CTL_ELEM_TYPE_INTEGER64] = 64, + }; struct snd_card *card = file->card; - struct snd_kcontrol kctl, *_kctl; + struct snd_kcontrol *kctl; + unsigned int count; unsigned int access; long private_size; struct user_element *ue; - int idx, err; + unsigned int offset; + int err; - if (info->count < 1) - return -EINVAL; if (!*info->id.name) return -EINVAL; if (strnlen(info->id.name, sizeof(info->id.name)) >= sizeof(info->id.name)) return -EINVAL; - access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE : - (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE| - SNDRV_CTL_ELEM_ACCESS_INACTIVE| - SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE)); - info->id.numid = 0; - memset(&kctl, 0, sizeof(kctl)); + /* Delete a control to replace them if needed. */ if (replace) { + info->id.numid = 0; err = snd_ctl_remove_user_ctl(file, &info->id); if (err) return err; } - if (card->user_ctl_count >= MAX_USER_CONTROLS) + /* + * The number of userspace controls are counted control by control, + * not element by element. + */ + if (card->user_ctl_count + 1 > MAX_USER_CONTROLS) return -ENOMEM; - memcpy(&kctl.id, &info->id, sizeof(info->id)); - kctl.count = info->owner ? info->owner : 1; - access |= SNDRV_CTL_ELEM_ACCESS_USER; - if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) - kctl.info = snd_ctl_elem_user_enum_info; - else - kctl.info = snd_ctl_elem_user_info; - if (access & SNDRV_CTL_ELEM_ACCESS_READ) - kctl.get = snd_ctl_elem_user_get; - if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) - kctl.put = snd_ctl_elem_user_put; - if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { - kctl.tlv.c = snd_ctl_elem_user_tlv; + /* Check the number of elements for this userspace control. */ + count = info->owner; + if (count == 0) + count = 1; + + /* Arrange access permissions if needed. */ + access = info->access; + if (access == 0) + access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE); + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK; - } - switch (info->type) { - case SNDRV_CTL_ELEM_TYPE_BOOLEAN: - case SNDRV_CTL_ELEM_TYPE_INTEGER: - private_size = sizeof(long); - if (info->count > 128) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_INTEGER64: - private_size = sizeof(long long); - if (info->count > 64) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_ENUMERATED: - private_size = sizeof(unsigned int); - if (info->count > 128 || info->value.enumerated.items == 0) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_BYTES: - private_size = sizeof(unsigned char); - if (info->count > 512) - return -EINVAL; - break; - case SNDRV_CTL_ELEM_TYPE_IEC958: - private_size = sizeof(struct snd_aes_iec958); - if (info->count != 1) - return -EINVAL; - break; - default: + access |= SNDRV_CTL_ELEM_ACCESS_USER; + + /* + * Check information and calculate the size of data specific to + * this userspace control. + */ + if (info->type < SNDRV_CTL_ELEM_TYPE_BOOLEAN || + info->type > SNDRV_CTL_ELEM_TYPE_INTEGER64) return -EINVAL; - } - private_size *= info->count; - ue = kzalloc(sizeof(struct user_element) + private_size, GFP_KERNEL); - if (ue == NULL) + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED && + info->value.enumerated.items == 0) + return -EINVAL; + if (info->count < 1 || + info->count > max_value_counts[info->type]) + return -EINVAL; + private_size = value_sizes[info->type] * info->count; + + /* + * Keep memory object for this userspace control. After passing this + * code block, the instance should be freed by snd_ctl_free_one(). + * + * Note that these elements in this control are locked. + */ + err = snd_ctl_new(&kctl, count, access, file); + if (err < 0) + return err; + memcpy(&kctl->id, &info->id, sizeof(kctl->id)); + kctl->private_data = kzalloc(sizeof(struct user_element) + private_size * count, + GFP_KERNEL); + if (kctl->private_data == NULL) { + kfree(kctl); return -ENOMEM; + } + kctl->private_free = snd_ctl_elem_user_free; + + /* Set private data for this userspace control. */ + ue = (struct user_element *)kctl->private_data; ue->card = card; ue->info = *info; ue->info.access = 0; @@ -1247,23 +1302,36 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file, if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) { err = snd_ctl_elem_init_enum_names(ue); if (err < 0) { - kfree(ue); + snd_ctl_free_one(kctl); return err; } } - kctl.private_free = snd_ctl_elem_user_free; - _kctl = snd_ctl_new(&kctl, access); - if (_kctl == NULL) { - kfree(ue->priv_data); - kfree(ue); - return -ENOMEM; - } - _kctl->private_data = ue; - for (idx = 0; idx < _kctl->count; idx++) - _kctl->vd[idx].owner = file; - err = snd_ctl_add(card, _kctl); + + /* Set callback functions. */ + if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) + kctl->info = snd_ctl_elem_user_enum_info; + else + kctl->info = snd_ctl_elem_user_info; + if (access & SNDRV_CTL_ELEM_ACCESS_READ) + kctl->get = snd_ctl_elem_user_get; + if (access & SNDRV_CTL_ELEM_ACCESS_WRITE) + kctl->put = snd_ctl_elem_user_put; + if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) + kctl->tlv.c = snd_ctl_elem_user_tlv; + + /* This function manage to free the instance on failure. */ + err = snd_ctl_add(card, kctl); if (err < 0) return err; + offset = snd_ctl_get_ioff(kctl, &info->id); + snd_ctl_build_ioff(&info->id, kctl, offset); + /* + * Here we cannot fill any field for the number of elements added by + * this operation because there're no specific fields. The usage of + * 'owner' field for this purpose may cause any bugs to userspace + * applications because the field originally means PID of a process + * which locks the element. + */ down_write(&card->controls_rwsem); card->user_ctl_count++; @@ -1276,9 +1344,19 @@ static int snd_ctl_elem_add_user(struct snd_ctl_file *file, struct snd_ctl_elem_info __user *_info, int replace) { struct snd_ctl_elem_info info; + int err; + if (copy_from_user(&info, _info, sizeof(info))) return -EFAULT; - return snd_ctl_elem_add(file, &info, replace); + err = snd_ctl_elem_add(file, &info, replace); + if (err < 0) + return err; + if (copy_to_user(_info, &info, sizeof(info))) { + snd_ctl_remove_user_ctl(file, &info.id); + return -EFAULT; + } + + return 0; } static int snd_ctl_elem_remove(struct snd_ctl_file *file, @@ -1338,9 +1416,12 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, goto __kctl_end; } vd = &kctl->vd[tlv.numid - kctl->id.numid]; - if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || - (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || - (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { + if ((op_flag == SNDRV_CTL_TLV_OP_READ && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) || + (op_flag == SNDRV_CTL_TLV_OP_WRITE && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) || + (op_flag == SNDRV_CTL_TLV_OP_CMD && + (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) { err = -ENXIO; goto __kctl_end; } @@ -1357,7 +1438,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file, return 0; } } else { - if (op_flag) { + if (op_flag != SNDRV_CTL_TLV_OP_READ) { err = -ENXIO; goto __kctl_end; } diff --git a/sound/core/device.c b/sound/core/device.c index 41bec3075ae5..8918838b1999 100644 --- a/sound/core/device.c +++ b/sound/core/device.c @@ -50,10 +50,8 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, if (snd_BUG_ON(!card || !device_data || !ops)) return -ENXIO; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (dev == NULL) { - dev_err(card->dev, "Cannot allocate device, type=%d\n", type); + if (!dev) return -ENOMEM; - } INIT_LIST_HEAD(&dev->list); dev->card = card; dev->type = type; @@ -73,7 +71,7 @@ int snd_device_new(struct snd_card *card, enum snd_device_type type, } EXPORT_SYMBOL(snd_device_new); -static int __snd_device_disconnect(struct snd_device *dev) +static void __snd_device_disconnect(struct snd_device *dev) { if (dev->state == SNDRV_DEV_REGISTERED) { if (dev->ops->dev_disconnect && @@ -81,7 +79,6 @@ static int __snd_device_disconnect(struct snd_device *dev) dev_err(dev->card->dev, "device disconnect failure\n"); dev->state = SNDRV_DEV_DISCONNECTED; } - return 0; } static void __snd_device_free(struct snd_device *dev) @@ -109,6 +106,34 @@ static struct snd_device *look_for_dev(struct snd_card *card, void *device_data) } /** + * snd_device_disconnect - disconnect the device + * @card: the card instance + * @device_data: the data pointer to disconnect + * + * Turns the device into the disconnection state, invoking + * dev_disconnect callback, if the device was already registered. + * + * Usually called from snd_card_disconnect(). + * + * Return: Zero if successful, or a negative error code on failure or if the + * device not found. + */ +void snd_device_disconnect(struct snd_card *card, void *device_data) +{ + struct snd_device *dev; + + if (snd_BUG_ON(!card || !device_data)) + return; + dev = look_for_dev(card, device_data); + if (dev) + __snd_device_disconnect(dev); + else + dev_dbg(card->dev, "device disconnect %p (from %pF), not found\n", + device_data, __builtin_return_address(0)); +} +EXPORT_SYMBOL_GPL(snd_device_disconnect); + +/** * snd_device_free - release the device from the card * @card: the card instance * @device_data: the data pointer to release @@ -195,18 +220,14 @@ int snd_device_register_all(struct snd_card *card) * disconnect all the devices on the card. * called from init.c */ -int snd_device_disconnect_all(struct snd_card *card) +void snd_device_disconnect_all(struct snd_card *card) { struct snd_device *dev; - int err = 0; if (snd_BUG_ON(!card)) - return -ENXIO; - list_for_each_entry_reverse(dev, &card->devices, list) { - if (__snd_device_disconnect(dev) < 0) - err = -ENXIO; - } - return err; + return; + list_for_each_entry_reverse(dev, &card->devices, list) + __snd_device_disconnect(dev); } /* diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 84244a5143cf..51692c8a39ea 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -378,10 +378,8 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device, if (rhwdep) *rhwdep = NULL; hwdep = kzalloc(sizeof(*hwdep), GFP_KERNEL); - if (hwdep == NULL) { - dev_err(card->dev, "hwdep: cannot allocate\n"); + if (!hwdep) return -ENOMEM; - } init_waitqueue_head(&hwdep->open_wait); mutex_init(&hwdep->open_mutex); diff --git a/sound/core/init.c b/sound/core/init.c index 35419054821c..04734e047bfe 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -400,7 +400,6 @@ static const struct file_operations snd_shutdown_f_ops = int snd_card_disconnect(struct snd_card *card) { struct snd_monitor_file *mfile; - int err; if (!card) return -EINVAL; @@ -445,9 +444,7 @@ int snd_card_disconnect(struct snd_card *card) #endif /* notify all devices that we are disconnected */ - err = snd_device_disconnect_all(card); - if (err < 0) - dev_err(card->dev, "not all devices for card %i can be disconnected\n", card->number); + snd_device_disconnect_all(card); snd_info_card_disconnect(card); if (card->registered) { diff --git a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c index 5e6349f00ecd..056f8e274851 100644 --- a/sound/core/oss/mixer_oss.c +++ b/sound/core/oss/mixer_oss.c @@ -1212,10 +1212,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry, /* not changed */ goto __unlock; tbl = kmalloc(sizeof(*tbl), GFP_KERNEL); - if (! tbl) { - pr_err("ALSA: mixer_oss: no memory\n"); + if (!tbl) goto __unlock; - } tbl->oss_id = ch; tbl->name = kstrdup(str, GFP_KERNEL); if (! tbl->name) { diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 80423a4ccab6..58550cc93f28 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -854,7 +854,6 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream) params = kmalloc(sizeof(*params), GFP_KERNEL); sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); if (!sw_params || !params || !sparams) { - pcm_dbg(substream->pcm, "No memory\n"); err = -ENOMEM; goto failure; } diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 0345e53a340c..b25bcf5b8644 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -49,8 +49,6 @@ static struct snd_pcm *snd_pcm_get(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { - if (pcm->internal) - continue; if (pcm->card == card && pcm->device == device) return pcm; } @@ -62,8 +60,6 @@ static int snd_pcm_next(struct snd_card *card, int device) struct snd_pcm *pcm; list_for_each_entry(pcm, &snd_pcm_devices, list) { - if (pcm->internal) - continue; if (pcm->card == card && pcm->device > device) return pcm->device; else if (pcm->card->number > card->number) @@ -76,6 +72,9 @@ static int snd_pcm_add(struct snd_pcm *newpcm) { struct snd_pcm *pcm; + if (newpcm->internal) + return 0; + list_for_each_entry(pcm, &snd_pcm_devices, list) { if (pcm->card == newpcm->card && pcm->device == newpcm->device) return -EBUSY; @@ -344,11 +343,8 @@ static void snd_pcm_proc_info_read(struct snd_pcm_substream *substream, return; info = kmalloc(sizeof(*info), GFP_KERNEL); - if (! info) { - pcm_dbg(substream->pcm, - "snd_pcm_proc_info_read: cannot malloc\n"); + if (!info) return; - } err = snd_pcm_info(substream, info); if (err < 0) { @@ -718,10 +714,8 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count) prev = NULL; for (idx = 0, prev = NULL; idx < substream_count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - pcm_err(pcm, "Cannot allocate PCM substream\n"); + if (!substream) return -ENOMEM; - } substream->pcm = pcm; substream->pstr = pstr; substream->number = idx; @@ -775,13 +769,14 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, if (rpcm) *rpcm = NULL; pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); - if (pcm == NULL) { - dev_err(card->dev, "Cannot allocate PCM\n"); + if (!pcm) return -ENOMEM; - } pcm->card = card; pcm->device = device; pcm->internal = internal; + mutex_init(&pcm->open_mutex); + init_waitqueue_head(&pcm->open_wait); + INIT_LIST_HEAD(&pcm->list); if (id) strlcpy(pcm->id, id, sizeof(pcm->id)); if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) { @@ -792,8 +787,6 @@ static int _snd_pcm_new(struct snd_card *card, const char *id, int device, snd_pcm_free(pcm); return err; } - mutex_init(&pcm->open_mutex); - init_waitqueue_head(&pcm->open_wait); if ((err = snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops)) < 0) { snd_pcm_free(pcm); return err; @@ -888,8 +881,9 @@ static int snd_pcm_free(struct snd_pcm *pcm) if (!pcm) return 0; - list_for_each_entry(notify, &snd_pcm_notify_list, list) { - notify->n_unregister(pcm); + if (!pcm->internal) { + list_for_each_entry(notify, &snd_pcm_notify_list, list) + notify->n_unregister(pcm); } if (pcm->private_free) pcm->private_free(pcm); @@ -919,6 +913,9 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, if (snd_BUG_ON(!pcm || !rsubstream)) return -ENXIO; + if (snd_BUG_ON(stream != SNDRV_PCM_STREAM_PLAYBACK && + stream != SNDRV_PCM_STREAM_CAPTURE)) + return -EINVAL; *rsubstream = NULL; pstr = &pcm->streams[stream]; if (pstr->substream == NULL || pstr->substream_count == 0) @@ -927,25 +924,14 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, card = pcm->card; prefer_subdevice = snd_ctl_get_preferred_subdevice(card, SND_CTL_SUBDEV_PCM); - switch (stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { - for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { - if (SUBSTREAM_BUSY(substream)) - return -EAGAIN; - } - } - break; - case SNDRV_PCM_STREAM_CAPTURE: - if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { - for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { - if (SUBSTREAM_BUSY(substream)) - return -EAGAIN; - } + if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) { + int opposite = !stream; + + for (substream = pcm->streams[opposite].substream; substream; + substream = substream->next) { + if (SUBSTREAM_BUSY(substream)) + return -EAGAIN; } - break; - default: - return -EINVAL; } if (file->f_flags & O_APPEND) { @@ -968,15 +954,12 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, return 0; } - if (prefer_subdevice >= 0) { - for (substream = pstr->substream; substream; substream = substream->next) - if (!SUBSTREAM_BUSY(substream) && substream->number == prefer_subdevice) - goto __ok; - } - for (substream = pstr->substream; substream; substream = substream->next) - if (!SUBSTREAM_BUSY(substream)) + for (substream = pstr->substream; substream; substream = substream->next) { + if (!SUBSTREAM_BUSY(substream) && + (prefer_subdevice == -1 || + substream->number == prefer_subdevice)) break; - __ok: + } if (substream == NULL) return -EAGAIN; @@ -1086,15 +1069,16 @@ static int snd_pcm_dev_register(struct snd_device *device) if (snd_BUG_ON(!device || !device->device_data)) return -ENXIO; pcm = device->device_data; + if (pcm->internal) + return 0; + mutex_lock(®ister_mutex); err = snd_pcm_add(pcm); - if (err) { - mutex_unlock(®ister_mutex); - return err; - } + if (err) + goto unlock; for (cidx = 0; cidx < 2; cidx++) { int devtype = -1; - if (pcm->streams[cidx].substream == NULL || pcm->internal) + if (pcm->streams[cidx].substream == NULL) continue; switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: @@ -1109,9 +1093,8 @@ static int snd_pcm_dev_register(struct snd_device *device) &snd_pcm_f_ops[cidx], pcm, &pcm->streams[cidx].dev); if (err < 0) { - list_del(&pcm->list); - mutex_unlock(®ister_mutex); - return err; + list_del_init(&pcm->list); + goto unlock; } for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) @@ -1121,8 +1104,9 @@ static int snd_pcm_dev_register(struct snd_device *device) list_for_each_entry(notify, &snd_pcm_notify_list, list) notify->n_register(pcm); + unlock: mutex_unlock(®ister_mutex); - return 0; + return err; } static int snd_pcm_dev_disconnect(struct snd_device *device) @@ -1133,13 +1117,10 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) int cidx; mutex_lock(®ister_mutex); - if (list_empty(&pcm->list)) - goto unlock; - mutex_lock(&pcm->open_mutex); wake_up(&pcm->open_wait); list_del_init(&pcm->list); - for (cidx = 0; cidx < 2; cidx++) + for (cidx = 0; cidx < 2; cidx++) { for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) { snd_pcm_stream_lock_irq(substream); if (substream->runtime) { @@ -1149,18 +1130,20 @@ static int snd_pcm_dev_disconnect(struct snd_device *device) } snd_pcm_stream_unlock_irq(substream); } - list_for_each_entry(notify, &snd_pcm_notify_list, list) { - notify->n_disconnect(pcm); + } + if (!pcm->internal) { + list_for_each_entry(notify, &snd_pcm_notify_list, list) + notify->n_disconnect(pcm); } for (cidx = 0; cidx < 2; cidx++) { - snd_unregister_device(&pcm->streams[cidx].dev); + if (!pcm->internal) + snd_unregister_device(&pcm->streams[cidx].dev); if (pcm->streams[cidx].chmap_kctl) { snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl); pcm->streams[cidx].chmap_kctl = NULL; } } mutex_unlock(&pcm->open_mutex); - unlock: mutex_unlock(®ister_mutex); return 0; } diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c index 2d957ba63557..b48b434444ed 100644 --- a/sound/core/pcm_compat.c +++ b/sound/core/pcm_compat.c @@ -194,18 +194,30 @@ struct snd_pcm_status32 { u32 avail_max; u32 overrange; s32 suspended_state; - u32 reserved_alignment; + u32 audio_tstamp_data; struct compat_timespec audio_tstamp; - unsigned char reserved[56-sizeof(struct compat_timespec)]; + struct compat_timespec driver_tstamp; + u32 audio_tstamp_accuracy; + unsigned char reserved[52-2*sizeof(struct compat_timespec)]; } __attribute__((packed)); static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, - struct snd_pcm_status32 __user *src) + struct snd_pcm_status32 __user *src, + bool ext) { struct snd_pcm_status status; int err; + memset(&status, 0, sizeof(status)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&src->audio_tstamp_data))) + return -EFAULT; err = snd_pcm_status(substream, &status); if (err < 0) return err; @@ -222,7 +234,10 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream, put_user(status.avail_max, &src->avail_max) || put_user(status.overrange, &src->overrange) || put_user(status.suspended_state, &src->suspended_state) || - compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp)) + put_user(status.audio_tstamp_data, &src->audio_tstamp_data) || + compat_put_timespec(&status.audio_tstamp, &src->audio_tstamp) || + compat_put_timespec(&status.driver_tstamp, &src->driver_tstamp) || + put_user(status.audio_tstamp_accuracy, &src->audio_tstamp_accuracy)) return -EFAULT; return err; @@ -457,6 +472,7 @@ enum { SNDRV_PCM_IOCTL_HW_PARAMS32 = _IOWR('A', 0x11, struct snd_pcm_hw_params32), SNDRV_PCM_IOCTL_SW_PARAMS32 = _IOWR('A', 0x13, struct snd_pcm_sw_params32), SNDRV_PCM_IOCTL_STATUS32 = _IOR('A', 0x20, struct snd_pcm_status32), + SNDRV_PCM_IOCTL_STATUS_EXT32 = _IOWR('A', 0x24, struct snd_pcm_status32), SNDRV_PCM_IOCTL_DELAY32 = _IOR('A', 0x21, s32), SNDRV_PCM_IOCTL_CHANNEL_INFO32 = _IOR('A', 0x32, struct snd_pcm_channel_info32), SNDRV_PCM_IOCTL_REWIND32 = _IOW('A', 0x46, u32), @@ -517,7 +533,9 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l case SNDRV_PCM_IOCTL_SW_PARAMS32: return snd_pcm_ioctl_sw_params_compat(substream, argp); case SNDRV_PCM_IOCTL_STATUS32: - return snd_pcm_status_user_compat(substream, argp); + return snd_pcm_status_user_compat(substream, argp, false); + case SNDRV_PCM_IOCTL_STATUS_EXT32: + return snd_pcm_status_user_compat(substream, argp, true); case SNDRV_PCM_IOCTL_SYNC_PTR32: return snd_pcm_ioctl_sync_ptr_compat(substream, argp); case SNDRV_PCM_IOCTL_CHANNEL_INFO32: diff --git a/sound/core/pcm_dmaengine.c b/sound/core/pcm_dmaengine.c index 6542c4083594..fba365a78390 100644 --- a/sound/core/pcm_dmaengine.c +++ b/sound/core/pcm_dmaengine.c @@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_request_channel); * * The function should usually be called from the pcm open callback. Note that * this function will use private_data field of the substream's runtime. So it - * is not availabe to your pcm driver implementation. + * is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open(struct snd_pcm_substream *substream, struct dma_chan *chan) @@ -328,7 +328,7 @@ EXPORT_SYMBOL_GPL(snd_dmaengine_pcm_open); * This function will request a DMA channel using the passed filter function and * data. The function should usually be called from the pcm open callback. Note * that this function will use private_data field of the substream's runtime. So - * it is not availabe to your pcm driver implementation. + * it is not available to your pcm driver implementation. */ int snd_dmaengine_pcm_open_request_chan(struct snd_pcm_substream *substream, dma_filter_fn filter_fn, void *filter_data) diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ffd656012ab8..ac6b33f3779c 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -232,6 +232,49 @@ int snd_pcm_update_state(struct snd_pcm_substream *substream, return 0; } +static void update_audio_tstamp(struct snd_pcm_substream *substream, + struct timespec *curr_tstamp, + struct timespec *audio_tstamp) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + u64 audio_frames, audio_nsecs; + struct timespec driver_tstamp; + + if (runtime->tstamp_mode != SNDRV_PCM_TSTAMP_ENABLE) + return; + + if (!(substream->ops->get_time_info) || + (runtime->audio_tstamp_report.actual_type == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + + /* + * provide audio timestamp derived from pointer position + * add delay only if requested + */ + + audio_frames = runtime->hw_ptr_wrap + runtime->status->hw_ptr; + + if (runtime->audio_tstamp_config.report_delay) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + audio_frames -= runtime->delay; + else + audio_frames += runtime->delay; + } + audio_nsecs = div_u64(audio_frames * 1000000000LL, + runtime->rate); + *audio_tstamp = ns_to_timespec(audio_nsecs); + } + runtime->status->audio_tstamp = *audio_tstamp; + runtime->status->tstamp = *curr_tstamp; + + /* + * re-take a driver timestamp to let apps detect if the reference tstamp + * read by low-level hardware was provided with a delay + */ + snd_pcm_gettime(substream->runtime, (struct timespec *)&driver_tstamp); + runtime->driver_tstamp = driver_tstamp; +} + static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, unsigned int in_interrupt) { @@ -256,11 +299,18 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, pos = substream->ops->pointer(substream); curr_jiffies = jiffies; if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); - - if ((runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) && - (substream->ops->wall_clock)) - substream->ops->wall_clock(substream, &audio_tstamp); + if ((substream->ops->get_time_info) && + (runtime->audio_tstamp_config.type_requested != SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT)) { + substream->ops->get_time_info(substream, &curr_tstamp, + &audio_tstamp, + &runtime->audio_tstamp_config, + &runtime->audio_tstamp_report); + + /* re-test in case tstamp type is not supported in hardware and was demoted to DEFAULT */ + if (runtime->audio_tstamp_report.actual_type == SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT) + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); + } else + snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp); } if (pos == SNDRV_PCM_POS_XRUN) { @@ -403,8 +453,10 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, } no_delta_check: - if (runtime->status->hw_ptr == new_hw_ptr) + if (runtime->status->hw_ptr == new_hw_ptr) { + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return 0; + } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && runtime->silence_size > 0) @@ -426,30 +478,8 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, snd_BUG_ON(crossed_boundary != 1); runtime->hw_ptr_wrap += runtime->boundary; } - if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { - runtime->status->tstamp = curr_tstamp; - if (!(runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK)) { - /* - * no wall clock available, provide audio timestamp - * derived from pointer position+delay - */ - u64 audio_frames, audio_nsecs; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - - runtime->delay; - else - audio_frames = runtime->hw_ptr_wrap - + runtime->status->hw_ptr - + runtime->delay; - audio_nsecs = div_u64(audio_frames * 1000000000LL, - runtime->rate); - audio_tstamp = ns_to_timespec(audio_nsecs); - } - runtime->status->audio_tstamp = audio_tstamp; - } + update_audio_tstamp(substream, &curr_tstamp, &audio_tstamp); return snd_pcm_update_state(substream, runtime); } diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index a69ebc79bc50..d126c03361ae 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -707,6 +707,23 @@ int snd_pcm_status(struct snd_pcm_substream *substream, struct snd_pcm_runtime *runtime = substream->runtime; snd_pcm_stream_lock_irq(substream); + + snd_pcm_unpack_audio_tstamp_config(status->audio_tstamp_data, + &runtime->audio_tstamp_config); + + /* backwards compatible behavior */ + if (runtime->audio_tstamp_config.type_requested == + SNDRV_PCM_AUDIO_TSTAMP_TYPE_COMPAT) { + if (runtime->hw.info & SNDRV_PCM_INFO_HAS_WALL_CLOCK) + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + else + runtime->audio_tstamp_config.type_requested = + SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; + runtime->audio_tstamp_report.valid = 0; + } else + runtime->audio_tstamp_report.valid = 1; + status->state = runtime->status->state; status->suspended_state = runtime->status->suspended_state; if (status->state == SNDRV_PCM_STATE_OPEN) @@ -716,8 +733,15 @@ int snd_pcm_status(struct snd_pcm_substream *substream, snd_pcm_update_hw_ptr(substream); if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) { status->tstamp = runtime->status->tstamp; + status->driver_tstamp = runtime->driver_tstamp; status->audio_tstamp = runtime->status->audio_tstamp; + if (runtime->audio_tstamp_report.valid == 1) + /* backwards compatibility, no report provided in COMPAT mode */ + snd_pcm_pack_audio_tstamp_report(&status->audio_tstamp_data, + &status->audio_tstamp_accuracy, + &runtime->audio_tstamp_report); + goto _tstamp_end; } } else { @@ -753,12 +777,21 @@ int snd_pcm_status(struct snd_pcm_substream *substream, } static int snd_pcm_status_user(struct snd_pcm_substream *substream, - struct snd_pcm_status __user * _status) + struct snd_pcm_status __user * _status, + bool ext) { struct snd_pcm_status status; int res; - + memset(&status, 0, sizeof(status)); + /* + * with extension, parameters are read/write, + * get audio_tstamp_data from user, + * ignore rest of status structure + */ + if (ext && get_user(status.audio_tstamp_data, + (u32 __user *)(&_status->audio_tstamp_data))) + return -EFAULT; res = snd_pcm_status(substream, &status); if (res < 0) return res; @@ -2725,7 +2758,9 @@ static int snd_pcm_common_ioctl1(struct file *file, case SNDRV_PCM_IOCTL_SW_PARAMS: return snd_pcm_sw_params_user(substream, arg); case SNDRV_PCM_IOCTL_STATUS: - return snd_pcm_status_user(substream, arg); + return snd_pcm_status_user(substream, arg, false); + case SNDRV_PCM_IOCTL_STATUS_EXT: + return snd_pcm_status_user(substream, arg, true); case SNDRV_PCM_IOCTL_CHANNEL_INFO: return snd_pcm_channel_info_user(substream, arg); case SNDRV_PCM_IOCTL_PREPARE: @@ -3033,9 +3068,7 @@ static ssize_t snd_pcm_write(struct file *file, const char __user *buf, return result; } -static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) - +static ssize_t snd_pcm_readv(struct kiocb *iocb, struct iov_iter *to) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; @@ -3052,16 +3085,18 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - if (nr_segs > 1024 || nr_segs != runtime->channels) + if (!iter_is_iovec(to)) + return -EINVAL; + if (to->nr_segs > 1024 || to->nr_segs != runtime->channels) return -EINVAL; - if (!frame_aligned(runtime, iov->iov_len)) + if (!frame_aligned(runtime, to->iov->iov_len)) return -EINVAL; - frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); + frames = bytes_to_samples(runtime, to->iov->iov_len); + bufs = kmalloc(sizeof(void *) * to->nr_segs, GFP_KERNEL); if (bufs == NULL) return -ENOMEM; - for (i = 0; i < nr_segs; ++i) - bufs[i] = iov[i].iov_base; + for (i = 0; i < to->nr_segs; ++i) + bufs[i] = to->iov[i].iov_base; result = snd_pcm_lib_readv(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); @@ -3069,8 +3104,7 @@ static ssize_t snd_pcm_aio_read(struct kiocb *iocb, const struct iovec *iov, return result; } -static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, - unsigned long nr_segs, loff_t pos) +static ssize_t snd_pcm_writev(struct kiocb *iocb, struct iov_iter *from) { struct snd_pcm_file *pcm_file; struct snd_pcm_substream *substream; @@ -3087,15 +3121,17 @@ static ssize_t snd_pcm_aio_write(struct kiocb *iocb, const struct iovec *iov, runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_OPEN) return -EBADFD; - if (nr_segs > 128 || nr_segs != runtime->channels || - !frame_aligned(runtime, iov->iov_len)) + if (!iter_is_iovec(from)) + return -EINVAL; + if (from->nr_segs > 128 || from->nr_segs != runtime->channels || + !frame_aligned(runtime, from->iov->iov_len)) return -EINVAL; - frames = bytes_to_samples(runtime, iov->iov_len); - bufs = kmalloc(sizeof(void *) * nr_segs, GFP_KERNEL); + frames = bytes_to_samples(runtime, from->iov->iov_len); + bufs = kmalloc(sizeof(void *) * from->nr_segs, GFP_KERNEL); if (bufs == NULL) return -ENOMEM; - for (i = 0; i < nr_segs; ++i) - bufs[i] = iov[i].iov_base; + for (i = 0; i < from->nr_segs; ++i) + bufs[i] = from->iov[i].iov_base; result = snd_pcm_lib_writev(substream, bufs, frames); if (result > 0) result = frames_to_bytes(runtime, result); @@ -3633,7 +3669,7 @@ const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .write = snd_pcm_write, - .aio_write = snd_pcm_aio_write, + .write_iter = snd_pcm_writev, .open = snd_pcm_playback_open, .release = snd_pcm_release, .llseek = no_llseek, @@ -3647,7 +3683,7 @@ const struct file_operations snd_pcm_f_ops[2] = { { .owner = THIS_MODULE, .read = snd_pcm_read, - .aio_read = snd_pcm_aio_read, + .read_iter = snd_pcm_readv, .open = snd_pcm_capture_open, .release = snd_pcm_release, .llseek = no_llseek, diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index b5a748596fc4..a7759846fbaa 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -1429,10 +1429,8 @@ static int snd_rawmidi_alloc_substreams(struct snd_rawmidi *rmidi, for (idx = 0; idx < count; idx++) { substream = kzalloc(sizeof(*substream), GFP_KERNEL); - if (substream == NULL) { - rmidi_err(rmidi, "rawmidi: cannot allocate substream\n"); + if (!substream) return -ENOMEM; - } substream->stream = direction; substream->number = idx; substream->rmidi = rmidi; @@ -1479,10 +1477,8 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device, if (rrawmidi) *rrawmidi = NULL; rmidi = kzalloc(sizeof(*rmidi), GFP_KERNEL); - if (rmidi == NULL) { - dev_err(card->dev, "rawmidi: cannot allocate\n"); + if (!rmidi) return -ENOMEM; - } rmidi->card = card; rmidi->device = device; mutex_init(&rmidi->open_mutex); diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index 16d42679e43f..72873a46afeb 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -65,15 +65,20 @@ static unsigned int odev_poll(struct file *file, poll_table * wait); * module interface */ +static struct snd_seq_driver seq_oss_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_oss_synth_probe, + .remove = snd_seq_oss_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OSS, + .argsize = sizeof(struct snd_seq_oss_reg), +}; + static int __init alsa_seq_oss_init(void) { int rc; - static struct snd_seq_dev_ops ops = { - snd_seq_oss_synth_register, - snd_seq_oss_synth_unregister, - }; - snd_seq_autoload_lock(); if ((rc = register_device()) < 0) goto error; if ((rc = register_proc()) < 0) { @@ -86,8 +91,8 @@ static int __init alsa_seq_oss_init(void) goto error; } - if ((rc = snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OSS, &ops, - sizeof(struct snd_seq_oss_reg))) < 0) { + rc = snd_seq_driver_register(&seq_oss_synth_driver); + if (rc < 0) { snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); @@ -98,13 +103,12 @@ static int __init alsa_seq_oss_init(void) snd_seq_oss_synth_init(); error: - snd_seq_autoload_unlock(); return rc; } static void __exit alsa_seq_oss_exit(void) { - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OSS); + snd_seq_driver_unregister(&seq_oss_synth_driver); snd_seq_oss_delete_client(); unregister_proc(); unregister_device(); diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c index b0e32e161dd1..2de3feff70d0 100644 --- a/sound/core/seq/oss/seq_oss_init.c +++ b/sound/core/seq/oss/seq_oss_init.c @@ -188,10 +188,8 @@ snd_seq_oss_open(struct file *file, int level) struct seq_oss_devinfo *dp; dp = kzalloc(sizeof(*dp), GFP_KERNEL); - if (!dp) { - pr_err("ALSA: seq_oss: can't malloc device info\n"); + if (!dp) return -ENOMEM; - } dp->cseq = system_client; dp->port = -1; diff --git a/sound/core/seq/oss/seq_oss_midi.c b/sound/core/seq/oss/seq_oss_midi.c index e79cc44b1394..96e8395ae586 100644 --- a/sound/core/seq/oss/seq_oss_midi.c +++ b/sound/core/seq/oss/seq_oss_midi.c @@ -173,10 +173,9 @@ snd_seq_oss_midi_check_new_port(struct snd_seq_port_info *pinfo) /* * allocate midi info record */ - if ((mdev = kzalloc(sizeof(*mdev), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc midi info\n"); + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) return -ENOMEM; - } /* copy the port information */ mdev->client = pinfo->addr.client; diff --git a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c index 654d17a5023c..c080c73cea04 100644 --- a/sound/core/seq/oss/seq_oss_readq.c +++ b/sound/core/seq/oss/seq_oss_readq.c @@ -47,13 +47,12 @@ snd_seq_oss_readq_new(struct seq_oss_devinfo *dp, int maxlen) { struct seq_oss_readq *q; - if ((q = kzalloc(sizeof(*q), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc read queue\n"); + q = kzalloc(sizeof(*q), GFP_KERNEL); + if (!q) return NULL; - } - if ((q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc read queue buffer\n"); + q->q = kcalloc(maxlen, sizeof(union evrec), GFP_KERNEL); + if (!q->q) { kfree(q); return NULL; } diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c index 701feb71b700..48e4fe1b68ab 100644 --- a/sound/core/seq/oss/seq_oss_synth.c +++ b/sound/core/seq/oss/seq_oss_synth.c @@ -98,17 +98,17 @@ snd_seq_oss_synth_init(void) * registration of the synth device */ int -snd_seq_oss_synth_register(struct snd_seq_device *dev) +snd_seq_oss_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int i; struct seq_oss_synth *rec; struct snd_seq_oss_reg *reg = SNDRV_SEQ_DEVICE_ARGPTR(dev); unsigned long flags; - if ((rec = kzalloc(sizeof(*rec), GFP_KERNEL)) == NULL) { - pr_err("ALSA: seq_oss: can't malloc synth info\n"); + rec = kzalloc(sizeof(*rec), GFP_KERNEL); + if (!rec) return -ENOMEM; - } rec->seq_device = -1; rec->synth_type = reg->type; rec->synth_subtype = reg->subtype; @@ -149,8 +149,9 @@ snd_seq_oss_synth_register(struct snd_seq_device *dev) int -snd_seq_oss_synth_unregister(struct snd_seq_device *dev) +snd_seq_oss_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); int index; struct seq_oss_synth *rec = dev->driver_data; unsigned long flags; @@ -247,7 +248,6 @@ snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp) if (info->nr_voices > 0) { info->ch = kcalloc(info->nr_voices, sizeof(struct seq_oss_chinfo), GFP_KERNEL); if (!info->ch) { - pr_err("ALSA: seq_oss: Cannot malloc voices\n"); rec->oper.close(&info->arg); module_put(rec->oper.owner); snd_use_lock_free(&rec->use_lock); diff --git a/sound/core/seq/oss/seq_oss_synth.h b/sound/core/seq/oss/seq_oss_synth.h index dbdfcbb80eaa..74ac55f166b6 100644 --- a/sound/core/seq/oss/seq_oss_synth.h +++ b/sound/core/seq/oss/seq_oss_synth.h @@ -28,8 +28,8 @@ #include <sound/seq_device.h> void snd_seq_oss_synth_init(void); -int snd_seq_oss_synth_register(struct snd_seq_device *dev); -int snd_seq_oss_synth_unregister(struct snd_seq_device *dev); +int snd_seq_oss_synth_probe(struct device *dev); +int snd_seq_oss_synth_remove(struct device *dev); void snd_seq_oss_synth_setup(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_setup_midi(struct seq_oss_devinfo *dp); void snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp); diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 48287651ac77..edbdab85fc02 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -1879,6 +1879,7 @@ static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client, if (cptr == NULL) return -ENOENT; memset(&info, 0, sizeof(info)); + info.client = cptr->number; info.output_pool = cptr->pool->size; info.output_room = cptr->pool->room; info.output_free = info.output_pool; diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 0631bdadd12b..d99f99d61983 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -36,6 +36,7 @@ * */ +#include <linux/device.h> #include <linux/init.h> #include <linux/module.h> #include <sound/core.h> @@ -51,140 +52,78 @@ MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>"); MODULE_DESCRIPTION("ALSA sequencer device management"); MODULE_LICENSE("GPL"); -/* driver state */ -#define DRIVER_EMPTY 0 -#define DRIVER_LOADED (1<<0) -#define DRIVER_REQUESTED (1<<1) -#define DRIVER_LOCKED (1<<2) -#define DRIVER_REQUESTING (1<<3) - -struct ops_list { - char id[ID_LEN]; /* driver id */ - int driver; /* driver state */ - int used; /* reference counter */ - int argsize; /* argument size */ - - /* operators */ - struct snd_seq_dev_ops ops; - - /* registered devices */ - struct list_head dev_list; /* list of devices */ - int num_devices; /* number of associated devices */ - int num_init_devices; /* number of initialized devices */ - struct mutex reg_mutex; - - struct list_head list; /* next driver */ -}; +/* + * bus definition + */ +static int snd_seq_bus_match(struct device *dev, struct device_driver *drv) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_seq_driver *sdrv = to_seq_drv(drv); + return strcmp(sdrv->id, sdev->id) == 0 && + sdrv->argsize == sdev->argsize; +} -static LIST_HEAD(opslist); -static int num_ops; -static DEFINE_MUTEX(ops_mutex); -#ifdef CONFIG_PROC_FS -static struct snd_info_entry *info_entry; -#endif +static struct bus_type snd_seq_bus_type = { + .name = "snd_seq", + .match = snd_seq_bus_match, +}; /* - * prototypes + * proc interface -- just for compatibility */ -static int snd_seq_device_free(struct snd_seq_device *dev); -static int snd_seq_device_dev_free(struct snd_device *device); -static int snd_seq_device_dev_register(struct snd_device *device); -static int snd_seq_device_dev_disconnect(struct snd_device *device); - -static int init_device(struct snd_seq_device *dev, struct ops_list *ops); -static int free_device(struct snd_seq_device *dev, struct ops_list *ops); -static struct ops_list *find_driver(char *id, int create_if_empty); -static struct ops_list *create_driver(char *id); -static void unlock_driver(struct ops_list *ops); -static void remove_drivers(void); +#ifdef CONFIG_PROC_FS +static struct snd_info_entry *info_entry; -/* - * show all drivers and their status - */ +static int print_dev_info(struct device *dev, void *data) +{ + struct snd_seq_device *sdev = to_seq_dev(dev); + struct snd_info_buffer *buffer = data; + + snd_iprintf(buffer, "snd-%s,%s,%d\n", sdev->id, + dev->driver ? "loaded" : "empty", + dev->driver ? 1 : 0); + return 0; +} -#ifdef CONFIG_PROC_FS static void snd_seq_device_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - snd_iprintf(buffer, "snd-%s%s%s%s,%d\n", - ops->id, - ops->driver & DRIVER_LOADED ? ",loaded" : (ops->driver == DRIVER_EMPTY ? ",empty" : ""), - ops->driver & DRIVER_REQUESTED ? ",requested" : "", - ops->driver & DRIVER_LOCKED ? ",locked" : "", - ops->num_devices); - } - mutex_unlock(&ops_mutex); + bus_for_each_dev(&snd_seq_bus_type, NULL, buffer, print_dev_info); } #endif - + /* * load all registered drivers (called from seq_clientmgr.c) */ #ifdef CONFIG_MODULES -/* avoid auto-loading during module_init() */ +/* flag to block auto-loading */ static atomic_t snd_seq_in_init = ATOMIC_INIT(1); /* blocked as default */ -void snd_seq_autoload_lock(void) -{ - atomic_inc(&snd_seq_in_init); -} -void snd_seq_autoload_unlock(void) +static int request_seq_drv(struct device *dev, void *data) { - atomic_dec(&snd_seq_in_init); + struct snd_seq_device *sdev = to_seq_dev(dev); + + if (!dev->driver) + request_module("snd-%s", sdev->id); + return 0; } -static void autoload_drivers(void) +static void autoload_drivers(struct work_struct *work) { /* avoid reentrance */ - if (atomic_inc_return(&snd_seq_in_init) == 1) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if ((ops->driver & DRIVER_REQUESTING) && - !(ops->driver & DRIVER_REQUESTED)) { - ops->used++; - mutex_unlock(&ops_mutex); - ops->driver |= DRIVER_REQUESTED; - request_module("snd-%s", ops->id); - mutex_lock(&ops_mutex); - ops->used--; - } - } - mutex_unlock(&ops_mutex); - } + if (atomic_inc_return(&snd_seq_in_init) == 1) + bus_for_each_dev(&snd_seq_bus_type, NULL, NULL, + request_seq_drv); atomic_dec(&snd_seq_in_init); } -static void call_autoload(struct work_struct *work) -{ - autoload_drivers(); -} - -static DECLARE_WORK(autoload_work, call_autoload); - -static void try_autoload(struct ops_list *ops) -{ - if (!ops->driver) { - ops->driver |= DRIVER_REQUESTING; - schedule_work(&autoload_work); - } -} +static DECLARE_WORK(autoload_work, autoload_drivers); static void queue_autoload_drivers(void) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) - try_autoload(ops); - mutex_unlock(&ops_mutex); + schedule_work(&autoload_work); } void snd_seq_autoload_init(void) @@ -195,384 +134,143 @@ void snd_seq_autoload_init(void) queue_autoload_drivers(); #endif } -#else -#define try_autoload(ops) /* NOP */ -#endif +EXPORT_SYMBOL(snd_seq_autoload_init); -void snd_seq_device_load_drivers(void) +void snd_seq_autoload_exit(void) { -#ifdef CONFIG_MODULES - queue_autoload_drivers(); - flush_work(&autoload_work); -#endif + atomic_inc(&snd_seq_in_init); } +EXPORT_SYMBOL(snd_seq_autoload_exit); -/* - * register a sequencer device - * card = card info - * device = device number (if any) - * id = id of driver - * result = return pointer (NULL allowed if unnecessary) - */ -int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize, - struct snd_seq_device **result) +void snd_seq_device_load_drivers(void) { - struct snd_seq_device *dev; - struct ops_list *ops; - int err; - static struct snd_device_ops dops = { - .dev_free = snd_seq_device_dev_free, - .dev_register = snd_seq_device_dev_register, - .dev_disconnect = snd_seq_device_dev_disconnect, - }; - - if (result) - *result = NULL; - - if (snd_BUG_ON(!id)) - return -EINVAL; - - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - - dev = kzalloc(sizeof(*dev)*2 + argsize, GFP_KERNEL); - if (dev == NULL) { - unlock_driver(ops); - return -ENOMEM; - } - - /* set up device info */ - dev->card = card; - dev->device = device; - strlcpy(dev->id, id, sizeof(dev->id)); - dev->argsize = argsize; - dev->status = SNDRV_SEQ_DEVICE_FREE; - - /* add this device to the list */ - mutex_lock(&ops->reg_mutex); - list_add_tail(&dev->list, &ops->dev_list); - ops->num_devices++; - mutex_unlock(&ops->reg_mutex); - - if ((err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops)) < 0) { - snd_seq_device_free(dev); - return err; - } - - try_autoload(ops); - unlock_driver(ops); - - if (result) - *result = dev; - - return 0; + queue_autoload_drivers(); + flush_work(&autoload_work); } +EXPORT_SYMBOL(snd_seq_device_load_drivers); +#else +#define queue_autoload_drivers() /* NOP */ +#endif /* - * free the existing device + * device management */ -static int snd_seq_device_free(struct snd_seq_device *dev) -{ - struct ops_list *ops; - - if (snd_BUG_ON(!dev)) - return -EINVAL; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENXIO; - - /* remove the device from the list */ - mutex_lock(&ops->reg_mutex); - list_del(&dev->list); - ops->num_devices--; - mutex_unlock(&ops->reg_mutex); - - free_device(dev, ops); - if (dev->private_free) - dev->private_free(dev); - kfree(dev); - - unlock_driver(ops); - - return 0; -} - static int snd_seq_device_dev_free(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - return snd_seq_device_free(dev); + + put_device(&dev->dev); + return 0; } -/* - * register the device - */ static int snd_seq_device_dev_register(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - /* initialize this device if the corresponding driver was - * already loaded - */ - if (ops->driver & DRIVER_LOADED) - init_device(dev, ops); + int err; - unlock_driver(ops); + err = device_add(&dev->dev); + if (err < 0) + return err; + if (!dev->dev.driver) + queue_autoload_drivers(); return 0; } -/* - * disconnect the device - */ static int snd_seq_device_dev_disconnect(struct snd_device *device) { struct snd_seq_device *dev = device->device_data; - struct ops_list *ops; - - ops = find_driver(dev->id, 0); - if (ops == NULL) - return -ENOENT; - - free_device(dev, ops); - unlock_driver(ops); + device_del(&dev->dev); return 0; } -/* - * register device driver - * id = driver id - * entry = driver operators - duplicated to each instance - */ -int snd_seq_device_register_driver(char *id, struct snd_seq_dev_ops *entry, - int argsize) +static void snd_seq_dev_release(struct device *dev) { - struct ops_list *ops; - struct snd_seq_device *dev; + struct snd_seq_device *sdev = to_seq_dev(dev); - if (id == NULL || entry == NULL || - entry->init_device == NULL || entry->free_device == NULL) - return -EINVAL; - - ops = find_driver(id, 1); - if (ops == NULL) - return -ENOMEM; - if (ops->driver & DRIVER_LOADED) { - pr_warn("ALSA: seq: driver_register: driver '%s' already exists\n", id); - unlock_driver(ops); - return -EBUSY; - } - - mutex_lock(&ops->reg_mutex); - /* copy driver operators */ - ops->ops = *entry; - ops->driver |= DRIVER_LOADED; - ops->argsize = argsize; - - /* initialize existing devices if necessary */ - list_for_each_entry(dev, &ops->dev_list, list) { - init_device(dev, ops); - } - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); - - return 0; + if (sdev->private_free) + sdev->private_free(sdev); + kfree(sdev); } - -/* - * create driver record - */ -static struct ops_list * create_driver(char *id) -{ - struct ops_list *ops; - - ops = kzalloc(sizeof(*ops), GFP_KERNEL); - if (ops == NULL) - return ops; - - /* set up driver entry */ - strlcpy(ops->id, id, sizeof(ops->id)); - mutex_init(&ops->reg_mutex); - /* - * The ->reg_mutex locking rules are per-driver, so we create - * separate per-driver lock classes: - */ - lockdep_set_class(&ops->reg_mutex, (struct lock_class_key *)id); - - ops->driver = DRIVER_EMPTY; - INIT_LIST_HEAD(&ops->dev_list); - /* lock this instance */ - ops->used = 1; - - /* register driver entry */ - mutex_lock(&ops_mutex); - list_add_tail(&ops->list, &opslist); - num_ops++; - mutex_unlock(&ops_mutex); - - return ops; -} - - /* - * unregister the specified driver + * register a sequencer device + * card = card info + * device = device number (if any) + * id = id of driver + * result = return pointer (NULL allowed if unnecessary) */ -int snd_seq_device_unregister_driver(char *id) +int snd_seq_device_new(struct snd_card *card, int device, const char *id, + int argsize, struct snd_seq_device **result) { - struct ops_list *ops; struct snd_seq_device *dev; + int err; + static struct snd_device_ops dops = { + .dev_free = snd_seq_device_dev_free, + .dev_register = snd_seq_device_dev_register, + .dev_disconnect = snd_seq_device_dev_disconnect, + }; - ops = find_driver(id, 0); - if (ops == NULL) - return -ENXIO; - if (! (ops->driver & DRIVER_LOADED) || - (ops->driver & DRIVER_LOCKED)) { - pr_err("ALSA: seq: driver_unregister: cannot unload driver '%s': status=%x\n", - id, ops->driver); - unlock_driver(ops); - return -EBUSY; - } - - /* close and release all devices associated with this driver */ - mutex_lock(&ops->reg_mutex); - ops->driver |= DRIVER_LOCKED; /* do not remove this driver recursively */ - list_for_each_entry(dev, &ops->dev_list, list) { - free_device(dev, ops); - } - - ops->driver = 0; - if (ops->num_init_devices > 0) - pr_err("ALSA: seq: free_driver: init_devices > 0!! (%d)\n", - ops->num_init_devices); - mutex_unlock(&ops->reg_mutex); - - unlock_driver(ops); + if (result) + *result = NULL; - /* remove empty driver entries */ - remove_drivers(); + if (snd_BUG_ON(!id)) + return -EINVAL; - return 0; -} + dev = kzalloc(sizeof(*dev) + argsize, GFP_KERNEL); + if (!dev) + return -ENOMEM; + /* set up device info */ + dev->card = card; + dev->device = device; + dev->id = id; + dev->argsize = argsize; -/* - * remove empty driver entries - */ -static void remove_drivers(void) -{ - struct list_head *head; - - mutex_lock(&ops_mutex); - head = opslist.next; - while (head != &opslist) { - struct ops_list *ops = list_entry(head, struct ops_list, list); - if (! (ops->driver & DRIVER_LOADED) && - ops->used == 0 && ops->num_devices == 0) { - head = head->next; - list_del(&ops->list); - kfree(ops); - num_ops--; - } else - head = head->next; - } - mutex_unlock(&ops_mutex); -} + device_initialize(&dev->dev); + dev->dev.parent = &card->card_dev; + dev->dev.bus = &snd_seq_bus_type; + dev->dev.release = snd_seq_dev_release; + dev_set_name(&dev->dev, "%s-%d-%d", dev->id, card->number, device); -/* - * initialize the device - call init_device operator - */ -static int init_device(struct snd_seq_device *dev, struct ops_list *ops) -{ - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_FREE) - return 0; /* already initialized */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); - return -EINVAL; - } - if (ops->ops.init_device(dev) >= 0) { - dev->status = SNDRV_SEQ_DEVICE_REGISTERED; - ops->num_init_devices++; - } else { - pr_err("ALSA: seq: init_device failed: %s: %s\n", - dev->name, dev->id); + /* add this device to the list */ + err = snd_device_new(card, SNDRV_DEV_SEQUENCER, dev, &dops); + if (err < 0) { + put_device(&dev->dev); + return err; } + + if (result) + *result = dev; return 0; } +EXPORT_SYMBOL(snd_seq_device_new); /* - * release the device - call free_device operator + * driver registration */ -static int free_device(struct snd_seq_device *dev, struct ops_list *ops) +int __snd_seq_driver_register(struct snd_seq_driver *drv, struct module *mod) { - int result; - - if (! (ops->driver & DRIVER_LOADED)) - return 0; /* driver is not loaded yet */ - if (dev->status != SNDRV_SEQ_DEVICE_REGISTERED) - return 0; /* not registered */ - if (ops->argsize != dev->argsize) { - pr_err("ALSA: seq: incompatible device '%s' for plug-in '%s' (%d %d)\n", - dev->name, ops->id, ops->argsize, dev->argsize); + if (WARN_ON(!drv->driver.name || !drv->id)) return -EINVAL; - } - if ((result = ops->ops.free_device(dev)) >= 0 || result == -ENXIO) { - dev->status = SNDRV_SEQ_DEVICE_FREE; - dev->driver_data = NULL; - ops->num_init_devices--; - } else { - pr_err("ALSA: seq: free_device failed: %s: %s\n", - dev->name, dev->id); - } - - return 0; + drv->driver.bus = &snd_seq_bus_type; + drv->driver.owner = mod; + return driver_register(&drv->driver); } +EXPORT_SYMBOL_GPL(__snd_seq_driver_register); -/* - * find the matching driver with given id - */ -static struct ops_list * find_driver(char *id, int create_if_empty) +void snd_seq_driver_unregister(struct snd_seq_driver *drv) { - struct ops_list *ops; - - mutex_lock(&ops_mutex); - list_for_each_entry(ops, &opslist, list) { - if (strcmp(ops->id, id) == 0) { - ops->used++; - mutex_unlock(&ops_mutex); - return ops; - } - } - mutex_unlock(&ops_mutex); - if (create_if_empty) - return create_driver(id); - return NULL; + driver_unregister(&drv->driver); } - -static void unlock_driver(struct ops_list *ops) -{ - mutex_lock(&ops_mutex); - ops->used--; - mutex_unlock(&ops_mutex); -} - +EXPORT_SYMBOL_GPL(snd_seq_driver_unregister); /* * module part */ -static int __init alsa_seq_device_init(void) +static int __init seq_dev_proc_init(void) { #ifdef CONFIG_PROC_FS info_entry = snd_info_create_module_entry(THIS_MODULE, "drivers", @@ -589,28 +287,29 @@ static int __init alsa_seq_device_init(void) return 0; } +static int __init alsa_seq_device_init(void) +{ + int err; + + err = bus_register(&snd_seq_bus_type); + if (err < 0) + return err; + err = seq_dev_proc_init(); + if (err < 0) + bus_unregister(&snd_seq_bus_type); + return err; +} + static void __exit alsa_seq_device_exit(void) { #ifdef CONFIG_MODULES cancel_work_sync(&autoload_work); #endif - remove_drivers(); #ifdef CONFIG_PROC_FS snd_info_free_entry(info_entry); #endif - if (num_ops) - pr_err("ALSA: seq: drivers not released (%d)\n", num_ops); + bus_unregister(&snd_seq_bus_type); } -module_init(alsa_seq_device_init) +subsys_initcall(alsa_seq_device_init) module_exit(alsa_seq_device_exit) - -EXPORT_SYMBOL(snd_seq_device_load_drivers); -EXPORT_SYMBOL(snd_seq_device_new); -EXPORT_SYMBOL(snd_seq_device_register_driver); -EXPORT_SYMBOL(snd_seq_device_unregister_driver); -#ifdef CONFIG_MODULES -EXPORT_SYMBOL(snd_seq_autoload_init); -EXPORT_SYMBOL(snd_seq_autoload_lock); -EXPORT_SYMBOL(snd_seq_autoload_unlock); -#endif diff --git a/sound/core/seq/seq_dummy.c b/sound/core/seq/seq_dummy.c index 5d905d90d504..d3a2ec4f0561 100644 --- a/sound/core/seq/seq_dummy.c +++ b/sound/core/seq/seq_dummy.c @@ -214,11 +214,7 @@ delete_client(void) static int __init alsa_seq_dummy_init(void) { - int err; - snd_seq_autoload_lock(); - err = register_client(); - snd_seq_autoload_unlock(); - return err; + return register_client(); } static void __exit alsa_seq_dummy_exit(void) diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c index 53a403e17c5b..1d5acbe0c08b 100644 --- a/sound/core/seq/seq_fifo.c +++ b/sound/core/seq/seq_fifo.c @@ -33,10 +33,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize) struct snd_seq_fifo *f; f = kzalloc(sizeof(*f), GFP_KERNEL); - if (f == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n"); + if (!f) return NULL; - } f->pool = snd_seq_pool_new(poolsize); if (f->pool == NULL) { diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c index ba8e4a64e13e..801076687bb1 100644 --- a/sound/core/seq/seq_memory.c +++ b/sound/core/seq/seq_memory.c @@ -387,10 +387,8 @@ int snd_seq_pool_init(struct snd_seq_pool *pool) return 0; pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size); - if (pool->ptr == NULL) { - pr_debug("ALSA: seq: malloc for sequencer events failed\n"); + if (!pool->ptr) return -ENOMEM; - } /* add new cells to the free cell list */ spin_lock_irqsave(&pool->lock, flags); @@ -463,10 +461,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize) /* create pool block */ pool = kzalloc(sizeof(*pool), GFP_KERNEL); - if (pool == NULL) { - pr_debug("ALSA: seq: malloc failed for pool\n"); + if (!pool) return NULL; - } spin_lock_init(&pool->lock); pool->ptr = NULL; pool->free = NULL; diff --git a/sound/core/seq/seq_midi.c b/sound/core/seq/seq_midi.c index 68fec776da26..5dd0ee258359 100644 --- a/sound/core/seq/seq_midi.c +++ b/sound/core/seq/seq_midi.c @@ -273,8 +273,9 @@ static void snd_seq_midisynth_delete(struct seq_midisynth *msynth) /* register new midi synth port */ static int -snd_seq_midisynth_register_port(struct snd_seq_device *dev) +snd_seq_midisynth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth, *ms; struct snd_seq_port_info *port; @@ -427,8 +428,9 @@ snd_seq_midisynth_register_port(struct snd_seq_device *dev) /* release midi synth port */ static int -snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) +snd_seq_midisynth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct seq_midisynth_client *client; struct seq_midisynth *msynth; struct snd_card *card = dev->card; @@ -457,24 +459,14 @@ snd_seq_midisynth_unregister_port(struct snd_seq_device *dev) return 0; } +static struct snd_seq_driver seq_midisynth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_seq_midisynth_probe, + .remove = snd_seq_midisynth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_MIDISYNTH, + .argsize = 0, +}; -static int __init alsa_seq_midi_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_seq_midisynth_register_port, - snd_seq_midisynth_unregister_port, - }; - memset(&synths, 0, sizeof(synths)); - snd_seq_autoload_lock(); - snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH, &ops, 0); - snd_seq_autoload_unlock(); - return 0; -} - -static void __exit alsa_seq_midi_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_MIDISYNTH); -} - -module_init(alsa_seq_midi_init) -module_exit(alsa_seq_midi_exit) +module_snd_seq_driver(seq_midisynth_driver); diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c index 46ff593f618d..55170a20ae72 100644 --- a/sound/core/seq/seq_ports.c +++ b/sound/core/seq/seq_ports.c @@ -141,10 +141,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client, /* create a new port */ new_port = kzalloc(sizeof(*new_port), GFP_KERNEL); - if (! new_port) { - pr_debug("ALSA: seq: malloc failed for registering client port\n"); + if (!new_port) return NULL; /* failure, out of memory */ - } /* init port data */ new_port->addr.client = client->number; new_port->addr.port = -1; diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c index 021b02bc9330..bc1c8488fc2a 100644 --- a/sound/core/seq/seq_prioq.c +++ b/sound/core/seq/seq_prioq.c @@ -59,10 +59,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void) struct snd_seq_prioq *f; f = kzalloc(sizeof(*f), GFP_KERNEL); - if (f == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n"); + if (!f) return NULL; - } spin_lock_init(&f->lock); f->head = NULL; diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index aad4878cee55..a0cda38205b9 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -111,10 +111,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked) struct snd_seq_queue *q; q = kzalloc(sizeof(*q), GFP_KERNEL); - if (q == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n"); + if (!q) return NULL; - } spin_lock_init(&q->owner_lock); spin_lock_init(&q->check_lock); diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index e73605393eee..186f1611103c 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -56,10 +56,8 @@ struct snd_seq_timer *snd_seq_timer_new(void) struct snd_seq_timer *tmr; tmr = kzalloc(sizeof(*tmr), GFP_KERNEL); - if (tmr == NULL) { - pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n"); + if (!tmr) return NULL; - } spin_lock_init(&tmr->lock); /* reset setup to defaults */ diff --git a/sound/core/sound.c b/sound/core/sound.c index 185cec01ee25..5fc93d00572a 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -186,7 +186,7 @@ static const struct file_operations snd_fops = }; #ifdef CONFIG_SND_DYNAMIC_MINORS -static int snd_find_free_minor(int type) +static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; @@ -209,7 +209,7 @@ static int snd_find_free_minor(int type) return -EBUSY; } #else -static int snd_kernel_minor(int type, struct snd_card *card, int dev) +static int snd_find_free_minor(int type, struct snd_card *card, int dev) { int minor; @@ -237,6 +237,8 @@ static int snd_kernel_minor(int type, struct snd_card *card, int dev) } if (snd_BUG_ON(minor < 0 || minor >= SNDRV_OS_MINORS)) return -EINVAL; + if (snd_minors[minor]) + return -EBUSY; return minor; } #endif @@ -276,13 +278,7 @@ int snd_register_device(int type, struct snd_card *card, int dev, preg->private_data = private_data; preg->card_ptr = card; mutex_lock(&sound_mutex); -#ifdef CONFIG_SND_DYNAMIC_MINORS - minor = snd_find_free_minor(type); -#else - minor = snd_kernel_minor(type, card, dev); - if (minor >= 0 && snd_minors[minor]) - minor = -EBUSY; -#endif + minor = snd_find_free_minor(type, card, dev); if (minor < 0) { err = minor; goto error; diff --git a/sound/core/timer.c b/sound/core/timer.c index 490b489d713d..a9a1a047c521 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -774,10 +774,8 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, if (rtimer) *rtimer = NULL; timer = kzalloc(sizeof(*timer), GFP_KERNEL); - if (timer == NULL) { - pr_err("ALSA: timer: cannot allocate\n"); + if (!timer) return -ENOMEM; - } timer->tmr_class = tid->dev_class; timer->card = card; timer->tmr_device = tid->device; diff --git a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c index a9f618e06a22..fdae5d7f421f 100644 --- a/sound/drivers/opl3/opl3_seq.c +++ b/sound/drivers/opl3/opl3_seq.c @@ -216,8 +216,9 @@ static int snd_opl3_synth_create_port(struct snd_opl3 * opl3) /* ------------------------------ */ -static int snd_opl3_seq_new_device(struct snd_seq_device *dev) +static int snd_opl3_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; int client, err; char name[32]; @@ -257,8 +258,9 @@ static int snd_opl3_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl3_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl3 *opl3; opl3 = *(struct snd_opl3 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -275,22 +277,14 @@ static int snd_opl3_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl3_seq_init(void) -{ - static struct snd_seq_dev_ops ops = - { - snd_opl3_seq_new_device, - snd_opl3_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL3, &ops, - sizeof(struct snd_opl3 *)); -} - -static void __exit alsa_opl3_seq_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL3); -} +static struct snd_seq_driver opl3_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl3_seq_probe, + .remove = snd_opl3_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL3, + .argsize = sizeof(struct snd_opl3 *), +}; -module_init(alsa_opl3_seq_init) -module_exit(alsa_opl3_seq_exit) +module_snd_seq_driver(opl3_seq_driver); diff --git a/sound/drivers/opl4/opl4_seq.c b/sound/drivers/opl4/opl4_seq.c index 99197699c55a..03d6202f4829 100644 --- a/sound/drivers/opl4/opl4_seq.c +++ b/sound/drivers/opl4/opl4_seq.c @@ -124,8 +124,9 @@ static void snd_opl4_seq_free_port(void *private_data) snd_midi_channel_free_set(opl4->chset); } -static int snd_opl4_seq_new_device(struct snd_seq_device *dev) +static int snd_opl4_seq_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; int client; struct snd_seq_port_callback pcallbacks; @@ -180,8 +181,9 @@ static int snd_opl4_seq_new_device(struct snd_seq_device *dev) return 0; } -static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) +static int snd_opl4_seq_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_opl4 *opl4; opl4 = *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(dev); @@ -195,21 +197,14 @@ static int snd_opl4_seq_delete_device(struct snd_seq_device *dev) return 0; } -static int __init alsa_opl4_synth_init(void) -{ - static struct snd_seq_dev_ops ops = { - snd_opl4_seq_new_device, - snd_opl4_seq_delete_device - }; - - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_OPL4, &ops, - sizeof(struct snd_opl4 *)); -} - -static void __exit alsa_opl4_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_OPL4); -} +static struct snd_seq_driver opl4_seq_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_opl4_seq_probe, + .remove = snd_opl4_seq_remove, + }, + .id = SNDRV_SEQ_DEV_ID_OPL4, + .argsize = sizeof(struct snd_opl4 *), +}; -module_init(alsa_opl4_synth_init) -module_exit(alsa_opl4_synth_exit) +module_snd_seq_driver(opl4_seq_driver); diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c index 5cc356db5351..e061355f535f 100644 --- a/sound/firewire/amdtp.c +++ b/sound/firewire/amdtp.c @@ -166,10 +166,10 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, * One AMDTP packet can include some frames. In blocking mode, the * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, * depending on its sampling rate. For accurate period interrupt, it's - * preferrable to aligh period/buffer sizes to current SYT_INTERVAL. + * preferrable to align period/buffer sizes to current SYT_INTERVAL. * - * TODO: These constraints can be improved with propper rules. - * Currently apply LCM of SYT_INTEVALs. + * TODO: These constraints can be improved with proper rules. + * Currently apply LCM of SYT_INTERVALs. */ err = snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); @@ -270,7 +270,7 @@ static void amdtp_read_s32(struct amdtp_stream *s, * @s: the AMDTP stream to configure * @format: the format of the ALSA PCM device * - * The sample format must be set after the other paramters (rate/PCM channels/ + * The sample format must be set after the other parameters (rate/PCM channels/ * MIDI) and before the stream is started, and must not be changed while the * stream is running. */ diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c index 2a85e4209f0b..f550808d1784 100644 --- a/sound/firewire/fireworks/fireworks_transaction.c +++ b/sound/firewire/fireworks/fireworks_transaction.c @@ -13,7 +13,7 @@ * * Transaction substance: * At first, 6 data exist. Following to the data, parameters for each command - * exist. All of the parameters are 32 bit alighed to big endian. + * exist. All of the parameters are 32 bit aligned to big endian. * data[0]: Length of transaction substance * data[1]: Transaction version * data[2]: Sequence number. This is incremented by the device diff --git a/sound/hda/Kconfig b/sound/hda/Kconfig new file mode 100644 index 000000000000..001c6588a5ff --- /dev/null +++ b/sound/hda/Kconfig @@ -0,0 +1,3 @@ +config SND_HDA_CORE + tristate + select REGMAP diff --git a/sound/hda/Makefile b/sound/hda/Makefile new file mode 100644 index 000000000000..7a359f5b7e25 --- /dev/null +++ b/sound/hda/Makefile @@ -0,0 +1,7 @@ +snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \ + hdac_regmap.o array.o + +snd-hda-core-objs += trace.o +CFLAGS_trace.o := -I$(src) + +obj-$(CONFIG_SND_HDA_CORE) += snd-hda-core.o diff --git a/sound/hda/array.c b/sound/hda/array.c new file mode 100644 index 000000000000..516795baa7db --- /dev/null +++ b/sound/hda/array.c @@ -0,0 +1,49 @@ +/* + * generic arrays + */ + +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/hdaudio.h> + +/** + * snd_array_new - get a new element from the given array + * @array: the array object + * + * Get a new element from the given array. If it exceeds the + * pre-allocated array size, re-allocate the array. + * + * Returns NULL if allocation failed. + */ +void *snd_array_new(struct snd_array *array) +{ + if (snd_BUG_ON(!array->elem_size)) + return NULL; + if (array->used >= array->alloced) { + int num = array->alloced + array->alloc_align; + int size = (num + 1) * array->elem_size; + void *nlist; + if (snd_BUG_ON(num >= 4096)) + return NULL; + nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); + if (!nlist) + return NULL; + array->list = nlist; + array->alloced = num; + } + return snd_array_elem(array, array->used++); +} +EXPORT_SYMBOL_GPL(snd_array_new); + +/** + * snd_array_free - free the given array elements + * @array: the array object + */ +void snd_array_free(struct snd_array *array) +{ + kfree(array->list); + array->used = 0; + array->alloced = 0; + array->list = NULL; +} +EXPORT_SYMBOL_GPL(snd_array_free); diff --git a/sound/hda/hda_bus_type.c b/sound/hda/hda_bus_type.c new file mode 100644 index 000000000000..519914a12e8a --- /dev/null +++ b/sound/hda/hda_bus_type.c @@ -0,0 +1,42 @@ +/* + * HD-audio bus + */ +#include <linux/init.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <sound/hdaudio.h> + +MODULE_DESCRIPTION("HD-audio bus"); +MODULE_LICENSE("GPL"); + +static int hda_bus_match(struct device *dev, struct device_driver *drv) +{ + struct hdac_device *hdev = dev_to_hdac_dev(dev); + struct hdac_driver *hdrv = drv_to_hdac_driver(drv); + + if (hdev->type != hdrv->type) + return 0; + if (hdrv->match) + return hdrv->match(hdev, hdrv); + return 1; +} + +struct bus_type snd_hda_bus_type = { + .name = "hdaudio", + .match = hda_bus_match, +}; +EXPORT_SYMBOL_GPL(snd_hda_bus_type); + +static int __init hda_bus_init(void) +{ + return bus_register(&snd_hda_bus_type); +} + +static void __exit hda_bus_exit(void) +{ + bus_unregister(&snd_hda_bus_type); +} + +subsys_initcall(hda_bus_init); +module_exit(hda_bus_exit); diff --git a/sound/hda/hdac_bus.c b/sound/hda/hdac_bus.c new file mode 100644 index 000000000000..8e262da74f6a --- /dev/null +++ b/sound/hda/hdac_bus.c @@ -0,0 +1,186 @@ +/* + * HD-audio core bus driver + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/export.h> +#include <sound/hdaudio.h> +#include "trace.h" + +static void process_unsol_events(struct work_struct *work); + +/** + * snd_hdac_bus_init - initialize a HD-audio bas bus + * @bus: the pointer to bus object + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_init(struct hdac_bus *bus, struct device *dev, + const struct hdac_bus_ops *ops) +{ + memset(bus, 0, sizeof(*bus)); + bus->dev = dev; + bus->ops = ops; + INIT_LIST_HEAD(&bus->codec_list); + INIT_WORK(&bus->unsol_work, process_unsol_events); + mutex_init(&bus->cmd_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_init); + +/** + * snd_hdac_bus_exit - clean up a HD-audio bas bus + * @bus: the pointer to bus object + */ +void snd_hdac_bus_exit(struct hdac_bus *bus) +{ + WARN_ON(!list_empty(&bus->codec_list)); + cancel_work_sync(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exit); + +/** + * snd_hdac_bus_exec_verb - execute a HD-audio verb on the given bus + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + int err; + + mutex_lock(&bus->cmd_mutex); + err = snd_hdac_bus_exec_verb_unlocked(bus, addr, cmd, res); + mutex_unlock(&bus->cmd_mutex); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb); + +/** + * snd_hdac_bus_exec_verb_unlocked - unlocked version + * @bus: bus object + * @cmd: HD-audio encoded verb + * @res: pointer to store the response, NULL if performing asynchronously + * + * Returns 0 if successful, or a negative error code. + */ +int snd_hdac_bus_exec_verb_unlocked(struct hdac_bus *bus, unsigned int addr, + unsigned int cmd, unsigned int *res) +{ + unsigned int tmp; + int err; + + if (cmd == ~0) + return -EINVAL; + + if (res) + *res = -1; + else if (bus->sync_write) + res = &tmp; + for (;;) { + trace_hda_send_cmd(bus, cmd); + err = bus->ops->command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + err = bus->ops->get_response(bus, addr, &tmp); + if (err) + break; + } + if (!err && res) { + err = bus->ops->get_response(bus, addr, res); + trace_hda_get_response(bus, addr, *res); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_exec_verb_unlocked); + +/** + * snd_hdac_bus_queue_event - add an unsolicited event to queue + * @bus: the BUS + * @res: unsolicited event (lower 32bit of RIRB entry) + * @res_ex: codec addr and flags (upper 32bit or RIRB entry) + * + * Adds the given event to the queue. The events are processed in + * the workqueue asynchronously. Call this function in the interrupt + * hanlder when RIRB receives an unsolicited event. + */ +void snd_hdac_bus_queue_event(struct hdac_bus *bus, u32 res, u32 res_ex) +{ + unsigned int wp; + + if (!bus) + return; + + trace_hda_unsol_event(bus, res, res_ex); + wp = (bus->unsol_wp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_wp = wp; + + wp <<= 1; + bus->unsol_queue[wp] = res; + bus->unsol_queue[wp + 1] = res_ex; + + schedule_work(&bus->unsol_work); +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_queue_event); + +/* + * process queued unsolicited events + */ +static void process_unsol_events(struct work_struct *work) +{ + struct hdac_bus *bus = container_of(work, struct hdac_bus, unsol_work); + struct hdac_device *codec; + struct hdac_driver *drv; + unsigned int rp, caddr, res; + + while (bus->unsol_rp != bus->unsol_wp) { + rp = (bus->unsol_rp + 1) % HDA_UNSOL_QUEUE_SIZE; + bus->unsol_rp = rp; + rp <<= 1; + res = bus->unsol_queue[rp]; + caddr = bus->unsol_queue[rp + 1]; + if (!(caddr & (1 << 4))) /* no unsolicited event? */ + continue; + codec = bus->caddr_tbl[caddr & 0x0f]; + if (!codec || !codec->dev.driver) + continue; + drv = drv_to_hdac_driver(codec->dev.driver); + if (drv->unsol_event) + drv->unsol_event(codec, res); + } +} + +int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec) +{ + if (bus->caddr_tbl[codec->addr]) { + dev_err(bus->dev, "address 0x%x is already occupied\n", + codec->addr); + return -EBUSY; + } + + list_add_tail(&codec->list, &bus->codec_list); + bus->caddr_tbl[codec->addr] = codec; + set_bit(codec->addr, &bus->codec_powered); + bus->num_codecs++; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device); + +void snd_hdac_bus_remove_device(struct hdac_bus *bus, + struct hdac_device *codec) +{ + WARN_ON(bus != codec->bus); + if (list_empty(&codec->list)) + return; + list_del_init(&codec->list); + bus->caddr_tbl[codec->addr] = NULL; + clear_bit(codec->addr, &bus->codec_powered); + bus->num_codecs--; +} +EXPORT_SYMBOL_GPL(snd_hdac_bus_remove_device); diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c new file mode 100644 index 000000000000..f75bf5622687 --- /dev/null +++ b/sound/hda/hdac_device.c @@ -0,0 +1,599 @@ +/* + * HD-audio codec core device + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/pm_runtime.h> +#include <sound/hdaudio.h> +#include <sound/hda_regmap.h> +#include "local.h" + +static void setup_fg_nodes(struct hdac_device *codec); +static int get_codec_vendor_name(struct hdac_device *codec); + +static void default_release(struct device *dev) +{ + snd_hdac_device_exit(container_of(dev, struct hdac_device, dev)); +} + +/** + * snd_hdac_device_init - initialize the HD-audio codec base device + * @codec: device to initialize + * @bus: but to attach + * @name: device name string + * @addr: codec address + * + * Returns zero for success or a negative error code. + * + * This function increments the runtime PM counter and marks it active. + * The caller needs to turn it off appropriately later. + * + * The caller needs to set the device's release op properly by itself. + */ +int snd_hdac_device_init(struct hdac_device *codec, struct hdac_bus *bus, + const char *name, unsigned int addr) +{ + struct device *dev; + hda_nid_t fg; + int err; + + dev = &codec->dev; + device_initialize(dev); + dev->parent = bus->dev; + dev->bus = &snd_hda_bus_type; + dev->release = default_release; + dev->groups = hdac_dev_attr_groups; + dev_set_name(dev, "%s", name); + device_enable_async_suspend(dev); + + codec->bus = bus; + codec->addr = addr; + codec->type = HDA_DEV_CORE; + pm_runtime_set_active(&codec->dev); + pm_runtime_get_noresume(&codec->dev); + atomic_set(&codec->in_pm, 0); + + err = snd_hdac_bus_add_device(bus, codec); + if (err < 0) + goto error; + + /* fill parameters */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + if (codec->vendor_id == -1) { + /* read again, hopefully the access method was corrected + * in the last read... + */ + codec->vendor_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_VENDOR_ID); + } + + codec->subsystem_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_SUBSYSTEM_ID); + codec->revision_id = snd_hdac_read_parm(codec, AC_NODE_ROOT, + AC_PAR_REV_ID); + + setup_fg_nodes(codec); + if (!codec->afg && !codec->mfg) { + dev_err(dev, "no AFG or MFG node found\n"); + err = -ENODEV; + goto error; + } + + fg = codec->afg ? codec->afg : codec->mfg; + + err = snd_hdac_refresh_widgets(codec); + if (err < 0) + goto error; + + codec->power_caps = snd_hdac_read_parm(codec, fg, AC_PAR_POWER_STATE); + /* reread ssid if not set by parameter */ + if (codec->subsystem_id == -1 || codec->subsystem_id == 0) + snd_hdac_read(codec, fg, AC_VERB_GET_SUBSYSTEM_ID, 0, + &codec->subsystem_id); + + err = get_codec_vendor_name(codec); + if (err < 0) + goto error; + + codec->chip_name = kasprintf(GFP_KERNEL, "ID %x", + codec->vendor_id & 0xffff); + if (!codec->chip_name) { + err = -ENOMEM; + goto error; + } + + return 0; + + error: + put_device(&codec->dev); + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_init); + +/** + * snd_hdac_device_exit - clean up the HD-audio codec base device + * @codec: device to clean up + */ +void snd_hdac_device_exit(struct hdac_device *codec) +{ + pm_runtime_put_noidle(&codec->dev); + snd_hdac_bus_remove_device(codec->bus, codec); + kfree(codec->vendor_name); + kfree(codec->chip_name); +} +EXPORT_SYMBOL_GPL(snd_hdac_device_exit); + +/** + * snd_hdac_device_register - register the hd-audio codec base device + * codec: the device to register + */ +int snd_hdac_device_register(struct hdac_device *codec) +{ + int err; + + err = device_add(&codec->dev); + if (err < 0) + return err; + err = hda_widget_sysfs_init(codec); + if (err < 0) { + device_del(&codec->dev); + return err; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_device_register); + +/** + * snd_hdac_device_unregister - unregister the hd-audio codec base device + * codec: the device to unregister + */ +void snd_hdac_device_unregister(struct hdac_device *codec) +{ + if (device_is_registered(&codec->dev)) { + hda_widget_sysfs_exit(codec); + device_del(&codec->dev); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_device_unregister); + +/** + * snd_hdac_make_cmd - compose a 32bit command word to be sent to the + * HD-audio controller + * @codec: the codec object + * @nid: NID to encode + * @verb: verb to encode + * @parm: parameter to encode + * + * Return an encoded command verb or -1 for error. + */ +unsigned int snd_hdac_make_cmd(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm) +{ + u32 val, addr; + + addr = codec->addr; + if ((addr & ~0xf) || (nid & ~0x7f) || + (verb & ~0xfff) || (parm & ~0xffff)) { + dev_err(&codec->dev, "out of range cmd %x:%x:%x:%x\n", + addr, nid, verb, parm); + return -1; + } + + val = addr << 28; + val |= (u32)nid << 20; + val |= verb << 8; + val |= parm; + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_make_cmd); + +/** + * snd_hdac_exec_verb - execute an encoded verb + * @codec: the codec object + * @cmd: encoded verb to execute + * @flags: optional flags, pass zero for default + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + * + * This calls the exec_verb op when set in hdac_codec. If not, + * call the default snd_hdac_bus_exec_verb(). + */ +int snd_hdac_exec_verb(struct hdac_device *codec, unsigned int cmd, + unsigned int flags, unsigned int *res) +{ + if (codec->exec_verb) + return codec->exec_verb(codec, cmd, flags, res); + return snd_hdac_bus_exec_verb(codec->bus, codec->addr, cmd, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_exec_verb); + + +/** + * snd_hdac_read - execute a verb + * @codec: the codec object + * @nid: NID to execute a verb + * @verb: verb to execute + * @parm: parameter for a verb + * @res: the pointer to store the result, NULL if running async + * + * Returns zero if successful, or a negative error code. + */ +int snd_hdac_read(struct hdac_device *codec, hda_nid_t nid, + unsigned int verb, unsigned int parm, unsigned int *res) +{ + unsigned int cmd = snd_hdac_make_cmd(codec, nid, verb, parm); + + return snd_hdac_exec_verb(codec, cmd, 0, res); +} +EXPORT_SYMBOL_GPL(snd_hdac_read); + +/** + * _snd_hdac_read_parm - read a parmeter + * + * This function returns zero or an error unlike snd_hdac_read_parm(). + */ +int _snd_hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm, + unsigned int *res) +{ + unsigned int cmd; + + cmd = snd_hdac_regmap_encode_verb(nid, AC_VERB_PARAMETERS) | parm; + return snd_hdac_regmap_read_raw(codec, cmd, res); +} +EXPORT_SYMBOL_GPL(_snd_hdac_read_parm); + +/** + * snd_hdac_read_parm_uncached - read a codec parameter without caching + * @codec: the codec object + * @nid: NID to read a parameter + * @parm: parameter to read + * + * Returns -1 for error. If you need to distinguish the error more + * strictly, use snd_hdac_read() directly. + */ +int snd_hdac_read_parm_uncached(struct hdac_device *codec, hda_nid_t nid, + int parm) +{ + int val; + + if (codec->regmap) + regcache_cache_bypass(codec->regmap, true); + val = snd_hdac_read_parm(codec, nid, parm); + if (codec->regmap) + regcache_cache_bypass(codec->regmap, false); + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_read_parm_uncached); + +/** + * snd_hdac_override_parm - override read-only parameters + * @codec: the codec object + * @nid: NID for the parameter + * @parm: the parameter to change + * @val: the parameter value to overwrite + */ +int snd_hdac_override_parm(struct hdac_device *codec, hda_nid_t nid, + unsigned int parm, unsigned int val) +{ + unsigned int verb = (AC_VERB_PARAMETERS << 8) | (nid << 20) | parm; + int err; + + if (!codec->regmap) + return -EINVAL; + + codec->caps_overwriting = true; + err = snd_hdac_regmap_write_raw(codec, verb, val); + codec->caps_overwriting = false; + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_override_parm); + +/** + * snd_hdac_get_sub_nodes - get start NID and number of subtree nodes + * @codec: the codec object + * @nid: NID to inspect + * @start_id: the pointer to store the starting NID + * + * Returns the number of subtree nodes or zero if not found. + * This function reads parameters always without caching. + */ +int snd_hdac_get_sub_nodes(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *start_id) +{ + unsigned int parm; + + parm = snd_hdac_read_parm_uncached(codec, nid, AC_PAR_NODE_COUNT); + if (parm == -1) { + *start_id = 0; + return 0; + } + *start_id = (parm >> 16) & 0x7fff; + return (int)(parm & 0x7fff); +} +EXPORT_SYMBOL_GPL(snd_hdac_get_sub_nodes); + +/* + * look for an AFG and MFG nodes + */ +static void setup_fg_nodes(struct hdac_device *codec) +{ + int i, total_nodes, function_id; + hda_nid_t nid; + + total_nodes = snd_hdac_get_sub_nodes(codec, AC_NODE_ROOT, &nid); + for (i = 0; i < total_nodes; i++, nid++) { + function_id = snd_hdac_read_parm(codec, nid, + AC_PAR_FUNCTION_TYPE); + switch (function_id & 0xff) { + case AC_GRP_AUDIO_FUNCTION: + codec->afg = nid; + codec->afg_function_id = function_id & 0xff; + codec->afg_unsol = (function_id >> 8) & 1; + break; + case AC_GRP_MODEM_FUNCTION: + codec->mfg = nid; + codec->mfg_function_id = function_id & 0xff; + codec->mfg_unsol = (function_id >> 8) & 1; + break; + default: + break; + } + } +} + +/** + * snd_hdac_refresh_widgets - Reset the widget start/end nodes + * @codec: the codec object + */ +int snd_hdac_refresh_widgets(struct hdac_device *codec) +{ + hda_nid_t start_nid; + int nums; + + nums = snd_hdac_get_sub_nodes(codec, codec->afg, &start_nid); + if (!start_nid || nums <= 0 || nums >= 0xff) { + dev_err(&codec->dev, "cannot read sub nodes for FG 0x%02x\n", + codec->afg); + return -EINVAL; + } + + codec->num_nodes = nums; + codec->start_nid = start_nid; + codec->end_nid = start_nid + nums; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_refresh_widgets); + +/* return CONNLIST_LEN parameter of the given widget */ +static unsigned int get_num_conns(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int wcaps = get_wcaps(codec, nid); + unsigned int parm; + + if (!(wcaps & AC_WCAP_CONN_LIST) && + get_wcaps_type(wcaps) != AC_WID_VOL_KNB) + return 0; + + parm = snd_hdac_read_parm(codec, nid, AC_PAR_CONNLIST_LEN); + if (parm == -1) + parm = 0; + return parm; +} + +/** + * snd_hdac_get_connections - get a widget connection list + * @codec: the codec object + * @nid: NID + * @conn_list: the array to store the results, can be NULL + * @max_conns: the max size of the given array + * + * Returns the number of connected widgets, zero for no connection, or a + * negative error code. When the number of elements don't fit with the + * given array size, it returns -ENOSPC. + * + * When @conn_list is NULL, it just checks the number of connections. + */ +int snd_hdac_get_connections(struct hdac_device *codec, hda_nid_t nid, + hda_nid_t *conn_list, int max_conns) +{ + unsigned int parm; + int i, conn_len, conns, err; + unsigned int shift, num_elems, mask; + hda_nid_t prev_nid; + int null_count = 0; + + parm = get_num_conns(codec, nid); + if (!parm) + return 0; + + if (parm & AC_CLIST_LONG) { + /* long form */ + shift = 16; + num_elems = 2; + } else { + /* short form */ + shift = 8; + num_elems = 4; + } + conn_len = parm & AC_CLIST_LENGTH; + mask = (1 << (shift-1)) - 1; + + if (!conn_len) + return 0; /* no connection */ + + if (conn_len == 1) { + /* single connection */ + err = snd_hdac_read(codec, nid, AC_VERB_GET_CONNECT_LIST, 0, + &parm); + if (err < 0) + return err; + if (conn_list) + conn_list[0] = parm & mask; + return 1; + } + + /* multi connection */ + conns = 0; + prev_nid = 0; + for (i = 0; i < conn_len; i++) { + int range_val; + hda_nid_t val, n; + + if (i % num_elems == 0) { + err = snd_hdac_read(codec, nid, + AC_VERB_GET_CONNECT_LIST, i, + &parm); + if (err < 0) + return -EIO; + } + range_val = !!(parm & (1 << (shift-1))); /* ranges */ + val = parm & mask; + if (val == 0 && null_count++) { /* no second chance */ + dev_dbg(&codec->dev, + "invalid CONNECT_LIST verb %x[%i]:%x\n", + nid, i, parm); + return 0; + } + parm >>= shift; + if (range_val) { + /* ranges between the previous and this one */ + if (!prev_nid || prev_nid >= val) { + dev_warn(&codec->dev, + "invalid dep_range_val %x:%x\n", + prev_nid, val); + continue; + } + for (n = prev_nid + 1; n <= val; n++) { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = n; + } + conns++; + } + } else { + if (conn_list) { + if (conns >= max_conns) + return -ENOSPC; + conn_list[conns] = val; + } + conns++; + } + prev_nid = val; + } + return conns; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_connections); + +#ifdef CONFIG_PM +/** + * snd_hdac_power_up - power up the codec + * @codec: the codec object + * + * This function calls the runtime PM helper to power up the given codec. + * Unlike snd_hdac_power_up_pm(), you should call this only for the code + * path that isn't included in PM path. Otherwise it gets stuck. + */ +void snd_hdac_power_up(struct hdac_device *codec) +{ + pm_runtime_get_sync(&codec->dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_up); + +/** + * snd_hdac_power_down - power down the codec + * @codec: the codec object + */ +void snd_hdac_power_down(struct hdac_device *codec) +{ + struct device *dev = &codec->dev; + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_down); + +/** + * snd_hdac_power_up_pm - power up the codec + * @codec: the codec object + * + * This function can be called in a recursive code path like init code + * which may be called by PM suspend/resume again. OTOH, if a power-up + * call must wake up the sleeper (e.g. in a kctl callback), use + * snd_hdac_power_up() instead. + */ +void snd_hdac_power_up_pm(struct hdac_device *codec) +{ + if (!atomic_inc_not_zero(&codec->in_pm)) + snd_hdac_power_up(codec); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm); + +/** + * snd_hdac_power_down_pm - power down the codec + * @codec: the codec object + * + * Like snd_hdac_power_up_pm(), this function is used in a recursive + * code path like init code which may be called by PM suspend/resume again. + */ +void snd_hdac_power_down_pm(struct hdac_device *codec) +{ + if (atomic_dec_if_positive(&codec->in_pm) < 0) + snd_hdac_power_down(codec); +} +EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); +#endif + +/* codec vendor labels */ +struct hda_vendor_id { + unsigned int id; + const char *name; +}; + +static struct hda_vendor_id hda_vendor_ids[] = { + { 0x1002, "ATI" }, + { 0x1013, "Cirrus Logic" }, + { 0x1057, "Motorola" }, + { 0x1095, "Silicon Image" }, + { 0x10de, "Nvidia" }, + { 0x10ec, "Realtek" }, + { 0x1102, "Creative" }, + { 0x1106, "VIA" }, + { 0x111d, "IDT" }, + { 0x11c1, "LSI" }, + { 0x11d4, "Analog Devices" }, + { 0x13f6, "C-Media" }, + { 0x14f1, "Conexant" }, + { 0x17e8, "Chrontel" }, + { 0x1854, "LG" }, + { 0x1aec, "Wolfson Microelectronics" }, + { 0x1af4, "QEMU" }, + { 0x434d, "C-Media" }, + { 0x8086, "Intel" }, + { 0x8384, "SigmaTel" }, + {} /* terminator */ +}; + +/* store the codec vendor name */ +static int get_codec_vendor_name(struct hdac_device *codec) +{ + const struct hda_vendor_id *c; + u16 vendor_id = codec->vendor_id >> 16; + + for (c = hda_vendor_ids; c->id; c++) { + if (c->id == vendor_id) { + codec->vendor_name = kstrdup(c->name, GFP_KERNEL); + return codec->vendor_name ? 0 : -ENOMEM; + } + } + + codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); + return codec->vendor_name ? 0 : -ENOMEM; +} diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c new file mode 100644 index 000000000000..7371e0c3926f --- /dev/null +++ b/sound/hda/hdac_regmap.c @@ -0,0 +1,472 @@ +/* + * Regmap support for HD-audio verbs + * + * A virtual register is translated to one or more hda verbs for write, + * vice versa for read. + * + * A few limitations: + * - Provided for not all verbs but only subset standard non-volatile verbs. + * - For reading, only AC_VERB_GET_* variants can be used. + * - For writing, mapped to the *corresponding* AC_VERB_SET_* variants, + * so can't handle asymmetric verbs for read and write + */ + +#include <linux/slab.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/export.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include <sound/hda_regmap.h> + +#ifdef CONFIG_PM +#define codec_is_running(codec) \ + (atomic_read(&(codec)->in_pm) || \ + !pm_runtime_suspended(&(codec)->dev)) +#else +#define codec_is_running(codec) true +#endif + +#define get_verb(reg) (((reg) >> 8) & 0xfff) + +static bool hda_volatile_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + + switch (verb) { + case AC_VERB_GET_PROC_COEF: + return !codec->cache_coef; + case AC_VERB_GET_COEF_INDEX: + case AC_VERB_GET_PROC_STATE: + case AC_VERB_GET_POWER_STATE: + case AC_VERB_GET_PIN_SENSE: + case AC_VERB_GET_HDMI_DIP_SIZE: + case AC_VERB_GET_HDMI_ELDD: + case AC_VERB_GET_HDMI_DIP_INDEX: + case AC_VERB_GET_HDMI_DIP_DATA: + case AC_VERB_GET_HDMI_DIP_XMIT: + case AC_VERB_GET_HDMI_CP_CTRL: + case AC_VERB_GET_HDMI_CHAN_SLOT: + case AC_VERB_GET_DEVICE_SEL: + case AC_VERB_GET_DEVICE_LIST: /* read-only volatile */ + return true; + } + + return false; +} + +static bool hda_writeable_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + int i; + + for (i = 0; i < codec->vendor_verbs.used; i++) { + unsigned int *v = snd_array_elem(&codec->vendor_verbs, i); + if (verb == *v) + return true; + } + + if (codec->caps_overwriting) + return true; + + switch (verb & 0xf00) { + case AC_VERB_GET_STREAM_FORMAT: + case AC_VERB_GET_AMP_GAIN_MUTE: + return true; + case AC_VERB_GET_PROC_COEF: + return codec->cache_coef; + case 0xf00: + break; + default: + return false; + } + + switch (verb) { + case AC_VERB_GET_CONNECT_SEL: + case AC_VERB_GET_SDI_SELECT: + case AC_VERB_GET_PIN_WIDGET_CONTROL: + case AC_VERB_GET_UNSOLICITED_RESPONSE: /* only as SET_UNSOLICITED_ENABLE */ + case AC_VERB_GET_BEEP_CONTROL: + case AC_VERB_GET_EAPD_BTLENABLE: + case AC_VERB_GET_DIGI_CONVERT_1: + case AC_VERB_GET_DIGI_CONVERT_2: /* only for beep control */ + case AC_VERB_GET_VOLUME_KNOB_CONTROL: + case AC_VERB_GET_GPIO_MASK: + case AC_VERB_GET_GPIO_DIRECTION: + case AC_VERB_GET_GPIO_DATA: /* not for volatile read */ + case AC_VERB_GET_GPIO_WAKE_MASK: + case AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK: + case AC_VERB_GET_GPIO_STICKY_MASK: + return true; + } + + return false; +} + +static bool hda_readable_reg(struct device *dev, unsigned int reg) +{ + struct hdac_device *codec = dev_to_hdac_dev(dev); + unsigned int verb = get_verb(reg); + + if (codec->caps_overwriting) + return true; + + switch (verb) { + case AC_VERB_PARAMETERS: + case AC_VERB_GET_CONNECT_LIST: + case AC_VERB_GET_SUBSYSTEM_ID: + return true; + /* below are basically writable, but disabled for reducing unnecessary + * writes at sync + */ + case AC_VERB_GET_CONFIG_DEFAULT: /* usually just read */ + case AC_VERB_GET_CONV: /* managed in PCM code */ + case AC_VERB_GET_CVT_CHAN_COUNT: /* managed in HDMI CA code */ + return true; + } + + return hda_writeable_reg(dev, reg); +} + +/* + * Stereo amp pseudo register: + * for making easier to handle the stereo volume control, we provide a + * fake register to deal both left and right channels by a single + * (pseudo) register access. A verb consisting of SET_AMP_GAIN with + * *both* SET_LEFT and SET_RIGHT bits takes a 16bit value, the lower 8bit + * for the left and the upper 8bit for the right channel. + */ +static bool is_stereo_amp_verb(unsigned int reg) +{ + if (((reg >> 8) & 0x700) != AC_VERB_SET_AMP_GAIN_MUTE) + return false; + return (reg & (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT)) == + (AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); +} + +/* read a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_read_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int *val) +{ + unsigned int left, right; + int err; + + reg &= ~(AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT); + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_LEFT, 0, &left); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_GET_RIGHT, 0, &right); + if (err < 0) + return err; + *val = left | (right << 8); + return 0; +} + +/* write a pseudo stereo amp register (16bit left+right) */ +static int hda_reg_write_stereo_amp(struct hdac_device *codec, + unsigned int reg, unsigned int val) +{ + int err; + unsigned int verb, left, right; + + verb = AC_VERB_SET_AMP_GAIN_MUTE << 8; + if (reg & AC_AMP_GET_OUTPUT) + verb |= AC_AMP_SET_OUTPUT; + else + verb |= AC_AMP_SET_INPUT | ((reg & 0xf) << 8); + reg = (reg & ~0xfffff) | verb; + + left = val & 0xff; + right = (val >> 8) & 0xff; + if (left == right) { + reg |= AC_AMP_SET_LEFT | AC_AMP_SET_RIGHT; + return snd_hdac_exec_verb(codec, reg | left, 0, NULL); + } + + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_LEFT | left, 0, NULL); + if (err < 0) + return err; + err = snd_hdac_exec_verb(codec, reg | AC_AMP_SET_RIGHT | right, 0, NULL); + if (err < 0) + return err; + return 0; +} + +/* read a pseudo coef register (16bit) */ +static int hda_reg_read_coef(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8); + return snd_hdac_exec_verb(codec, verb, 0, val); +} + +/* write a pseudo coef register (16bit) */ +static int hda_reg_write_coef(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + unsigned int verb; + int err; + + if (!codec->cache_coef) + return -EINVAL; + /* LSB 8bit = coef index */ + verb = (reg & ~0xfff00) | (AC_VERB_SET_COEF_INDEX << 8); + err = snd_hdac_exec_verb(codec, verb, 0, NULL); + if (err < 0) + return err; + verb = (reg & ~0xfffff) | (AC_VERB_GET_COEF_INDEX << 8) | + (val & 0xffff); + return snd_hdac_exec_verb(codec, verb, 0, NULL); +} + +static int hda_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct hdac_device *codec = context; + int verb = get_verb(reg); + int err; + + if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE) + return -EAGAIN; + reg |= (codec->addr << 28); + if (is_stereo_amp_verb(reg)) + return hda_reg_read_stereo_amp(codec, reg, val); + if (verb == AC_VERB_GET_PROC_COEF) + return hda_reg_read_coef(codec, reg, val); + err = snd_hdac_exec_verb(codec, reg, 0, val); + if (err < 0) + return err; + /* special handling for asymmetric reads */ + if (verb == AC_VERB_GET_POWER_STATE) { + if (*val & AC_PWRST_ERROR) + *val = -1; + else /* take only the actual state */ + *val = (*val >> 4) & 0x0f; + } + return 0; +} + +static int hda_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct hdac_device *codec = context; + unsigned int verb; + int i, bytes, err; + + reg &= ~0x00080000U; /* drop GET bit */ + reg |= (codec->addr << 28); + verb = get_verb(reg); + + if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE) + return codec->lazy_cache ? 0 : -EAGAIN; + + if (is_stereo_amp_verb(reg)) + return hda_reg_write_stereo_amp(codec, reg, val); + + if (verb == AC_VERB_SET_PROC_COEF) + return hda_reg_write_coef(codec, reg, val); + + switch (verb & 0xf00) { + case AC_VERB_SET_AMP_GAIN_MUTE: + verb = AC_VERB_SET_AMP_GAIN_MUTE; + if (reg & AC_AMP_GET_LEFT) + verb |= AC_AMP_SET_LEFT >> 8; + else + verb |= AC_AMP_SET_RIGHT >> 8; + if (reg & AC_AMP_GET_OUTPUT) { + verb |= AC_AMP_SET_OUTPUT >> 8; + } else { + verb |= AC_AMP_SET_INPUT >> 8; + verb |= reg & 0xf; + } + break; + } + + switch (verb) { + case AC_VERB_SET_DIGI_CONVERT_1: + bytes = 2; + break; + case AC_VERB_SET_CONFIG_DEFAULT_BYTES_0: + bytes = 4; + break; + default: + bytes = 1; + break; + } + + for (i = 0; i < bytes; i++) { + reg &= ~0xfffff; + reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff); + err = snd_hdac_exec_verb(codec, reg, 0, NULL); + if (err < 0) + return err; + } + + return 0; +} + +static const struct regmap_config hda_regmap_cfg = { + .name = "hdaudio", + .reg_bits = 32, + .val_bits = 32, + .max_register = 0xfffffff, + .writeable_reg = hda_writeable_reg, + .readable_reg = hda_readable_reg, + .volatile_reg = hda_volatile_reg, + .cache_type = REGCACHE_RBTREE, + .reg_read = hda_reg_read, + .reg_write = hda_reg_write, + .use_single_rw = true, +}; + +int snd_hdac_regmap_init(struct hdac_device *codec) +{ + struct regmap *regmap; + + regmap = regmap_init(&codec->dev, NULL, codec, &hda_regmap_cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + codec->regmap = regmap; + snd_array_init(&codec->vendor_verbs, sizeof(unsigned int), 8); + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_init); + +void snd_hdac_regmap_exit(struct hdac_device *codec) +{ + if (codec->regmap) { + regmap_exit(codec->regmap); + codec->regmap = NULL; + snd_array_free(&codec->vendor_verbs); + } +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_exit); + +/** + * snd_hdac_regmap_add_vendor_verb - add a vendor-specific verb to regmap + * @codec: the codec object + * @verb: verb to allow accessing via regmap + * + * Returns zero for success or a negative error code. + */ +int snd_hdac_regmap_add_vendor_verb(struct hdac_device *codec, + unsigned int verb) +{ + unsigned int *p = snd_array_new(&codec->vendor_verbs); + + if (!p) + return -ENOMEM; + *p = verb | 0x800; /* set GET bit */ + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_add_vendor_verb); + +/* + * helper functions + */ + +/* write a pseudo-register value (w/o power sequence) */ +static int reg_raw_write(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + if (!codec->regmap) + return hda_reg_write(codec, reg, val); + else + return regmap_write(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_write_raw - write a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: value to write + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg, + unsigned int val) +{ + int err; + + err = reg_raw_write(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up_pm(codec); + err = reg_raw_write(codec, reg, val); + snd_hdac_power_down_pm(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_write_raw); + +static int reg_raw_read(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + if (!codec->regmap) + return hda_reg_read(codec, reg, val); + else + return regmap_read(codec->regmap, reg, val); +} + +/** + * snd_hdac_regmap_read_raw - read a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @val: pointer to store the read value + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_read_raw(struct hdac_device *codec, unsigned int reg, + unsigned int *val) +{ + int err; + + err = reg_raw_read(codec, reg, val); + if (err == -EAGAIN) { + snd_hdac_power_up_pm(codec); + err = reg_raw_read(codec, reg, val); + snd_hdac_power_down_pm(codec); + } + return err; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_read_raw); + +/** + * snd_hdac_regmap_update_raw - update a pseudo register with power mgmt + * @codec: the codec object + * @reg: pseudo register + * @mask: bit mask to udpate + * @val: value to update + * + * Returns zero if successful or a negative error code. + */ +int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg, + unsigned int mask, unsigned int val) +{ + unsigned int orig; + int err; + + val &= mask; + err = snd_hdac_regmap_read_raw(codec, reg, &orig); + if (err < 0) + return err; + val |= orig & ~mask; + if (val == orig) + return 0; + err = snd_hdac_regmap_write_raw(codec, reg, val); + if (err < 0) + return err; + return 1; +} +EXPORT_SYMBOL_GPL(snd_hdac_regmap_update_raw); diff --git a/sound/hda/hdac_sysfs.c b/sound/hda/hdac_sysfs.c new file mode 100644 index 000000000000..0a6ce3b84cc4 --- /dev/null +++ b/sound/hda/hdac_sysfs.c @@ -0,0 +1,406 @@ +/* + * sysfs support for HD-audio core device + */ + +#include <linux/slab.h> +#include <linux/sysfs.h> +#include <linux/device.h> +#include <sound/core.h> +#include <sound/hdaudio.h> +#include "local.h" + +struct hdac_widget_tree { + struct kobject *root; + struct kobject *afg; + struct kobject **nodes; +}; + +#define CODEC_ATTR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "0x%x\n", codec->type); \ +} \ +static DEVICE_ATTR_RO(type) + +#define CODEC_ATTR_STR(type) \ +static ssize_t type##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct hdac_device *codec = dev_to_hdac_dev(dev); \ + return sprintf(buf, "%s\n", \ + codec->type ? codec->type : ""); \ +} \ +static DEVICE_ATTR_RO(type) + +CODEC_ATTR(type); +CODEC_ATTR(vendor_id); +CODEC_ATTR(subsystem_id); +CODEC_ATTR(revision_id); +CODEC_ATTR(afg); +CODEC_ATTR(mfg); +CODEC_ATTR_STR(vendor_name); +CODEC_ATTR_STR(chip_name); + +static struct attribute *hdac_dev_attrs[] = { + &dev_attr_type.attr, + &dev_attr_vendor_id.attr, + &dev_attr_subsystem_id.attr, + &dev_attr_revision_id.attr, + &dev_attr_afg.attr, + &dev_attr_mfg.attr, + &dev_attr_vendor_name.attr, + &dev_attr_chip_name.attr, + NULL +}; + +static struct attribute_group hdac_dev_attr_group = { + .attrs = hdac_dev_attrs, +}; + +const struct attribute_group *hdac_dev_attr_groups[] = { + &hdac_dev_attr_group, + NULL +}; + +/* + * Widget tree sysfs + * + * This is a tree showing the attributes of each widget. It appears like + * /sys/bus/hdaudioC0D0/widgets/04/caps + */ + +struct widget_attribute; + +struct widget_attribute { + struct attribute attr; + ssize_t (*show)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf); + ssize_t (*store)(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, + const char *buf, size_t count); +}; + +static int get_codec_nid(struct kobject *kobj, struct hdac_device **codecp) +{ + struct device *dev = kobj_to_dev(kobj->parent->parent); + int nid; + ssize_t ret; + + ret = kstrtoint(kobj->name, 16, &nid); + if (ret < 0) + return ret; + *codecp = dev_to_hdac_dev(dev); + return nid; +} + +static ssize_t widget_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->show) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->show(codec, nid, wid_attr, buf); +} + +static ssize_t widget_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct widget_attribute *wid_attr = + container_of(attr, struct widget_attribute, attr); + struct hdac_device *codec; + int nid; + + if (!wid_attr->store) + return -EIO; + nid = get_codec_nid(kobj, &codec); + if (nid < 0) + return nid; + return wid_attr->store(codec, nid, wid_attr, buf, count); +} + +static const struct sysfs_ops widget_sysfs_ops = { + .show = widget_attr_show, + .store = widget_attr_store, +}; + +static void widget_release(struct kobject *kobj) +{ + kfree(kobj); +} + +static struct kobj_type widget_ktype = { + .release = widget_release, + .sysfs_ops = &widget_sysfs_ops, +}; + +#define WIDGET_ATTR_RO(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RO(_name) +#define WIDGET_ATTR_RW(_name) \ + struct widget_attribute wid_attr_##_name = __ATTR_RW(_name) + +static ssize_t caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", get_wcaps(codec, nid)); +} + +static ssize_t pin_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PIN_CAP)); +} + +static ssize_t pin_cfg_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + unsigned int val; + + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) + return 0; + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return 0; + return sprintf(buf, "0x%08x\n", val); +} + +static bool has_pcm_cap(struct hdac_device *codec, hda_nid_t nid) +{ + if (nid == codec->afg || nid == codec->mfg) + return true; + switch (get_wcaps_type(get_wcaps(codec, nid))) { + case AC_WID_AUD_OUT: + case AC_WID_AUD_IN: + return true; + default: + return false; + } +} + +static ssize_t pcm_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_PCM)); +} + +static ssize_t pcm_formats_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (!has_pcm_cap(codec, nid)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_STREAM)); +} + +static ssize_t amp_in_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_IN_CAP)); +} + +static ssize_t amp_out_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_AMP_OUT_CAP)); +} + +static ssize_t power_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + if (nid != codec->afg && !(get_wcaps(codec, nid) & AC_WCAP_POWER)) + return 0; + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_POWER_STATE)); +} + +static ssize_t gpio_caps_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08x\n", + snd_hdac_read_parm(codec, nid, AC_PAR_GPIO_CAP)); +} + +static ssize_t connections_show(struct hdac_device *codec, hda_nid_t nid, + struct widget_attribute *attr, char *buf) +{ + hda_nid_t list[32]; + int i, nconns; + ssize_t ret = 0; + + nconns = snd_hdac_get_connections(codec, nid, list, ARRAY_SIZE(list)); + if (nconns <= 0) + return nconns; + for (i = 0; i < nconns; i++) + ret += sprintf(buf + ret, "%s0x%02x", i ? " " : "", list[i]); + ret += sprintf(buf + ret, "\n"); + return ret; +} + +static WIDGET_ATTR_RO(caps); +static WIDGET_ATTR_RO(pin_caps); +static WIDGET_ATTR_RO(pin_cfg); +static WIDGET_ATTR_RO(pcm_caps); +static WIDGET_ATTR_RO(pcm_formats); +static WIDGET_ATTR_RO(amp_in_caps); +static WIDGET_ATTR_RO(amp_out_caps); +static WIDGET_ATTR_RO(power_caps); +static WIDGET_ATTR_RO(gpio_caps); +static WIDGET_ATTR_RO(connections); + +static struct attribute *widget_node_attrs[] = { + &wid_attr_caps.attr, + &wid_attr_pin_caps.attr, + &wid_attr_pin_cfg.attr, + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_connections.attr, + NULL, +}; + +static struct attribute *widget_afg_attrs[] = { + &wid_attr_pcm_caps.attr, + &wid_attr_pcm_formats.attr, + &wid_attr_amp_in_caps.attr, + &wid_attr_amp_out_caps.attr, + &wid_attr_power_caps.attr, + &wid_attr_gpio_caps.attr, + NULL, +}; + +static const struct attribute_group widget_node_group = { + .attrs = widget_node_attrs, +}; + +static const struct attribute_group widget_afg_group = { + .attrs = widget_afg_attrs, +}; + +static void free_widget_node(struct kobject *kobj, + const struct attribute_group *group) +{ + if (kobj) { + sysfs_remove_group(kobj, group); + kobject_put(kobj); + } +} + +static void widget_tree_free(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree = codec->widgets; + struct kobject **p; + + if (!tree) + return; + free_widget_node(tree->afg, &widget_afg_group); + if (tree->nodes) { + for (p = tree->nodes; *p; p++) + free_widget_node(*p, &widget_node_group); + kfree(tree->nodes); + } + if (tree->root) + kobject_put(tree->root); + kfree(tree); + codec->widgets = NULL; +} + +static int add_widget_node(struct kobject *parent, hda_nid_t nid, + const struct attribute_group *group, + struct kobject **res) +{ + struct kobject *kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); + int err; + + if (!kobj) + return -ENOMEM; + kobject_init(kobj, &widget_ktype); + err = kobject_add(kobj, parent, "%02x", nid); + if (err < 0) + return err; + err = sysfs_create_group(kobj, group); + if (err < 0) { + kobject_put(kobj); + return err; + } + + *res = kobj; + return 0; +} + +static int widget_tree_create(struct hdac_device *codec) +{ + struct hdac_widget_tree *tree; + int i, err; + hda_nid_t nid; + + tree = codec->widgets = kzalloc(sizeof(*tree), GFP_KERNEL); + if (!tree) + return -ENOMEM; + + tree->root = kobject_create_and_add("widgets", &codec->dev.kobj); + if (!tree->root) + return -ENOMEM; + + tree->nodes = kcalloc(codec->num_nodes + 1, sizeof(*tree->nodes), + GFP_KERNEL); + if (!tree->nodes) + return -ENOMEM; + + for (i = 0, nid = codec->start_nid; i < codec->num_nodes; i++, nid++) { + err = add_widget_node(tree->root, nid, &widget_node_group, + &tree->nodes[i]); + if (err < 0) + return err; + } + + if (codec->afg) { + err = add_widget_node(tree->root, codec->afg, + &widget_afg_group, &tree->afg); + if (err < 0) + return err; + } + + kobject_uevent(tree->root, KOBJ_CHANGE); + return 0; +} + +int hda_widget_sysfs_init(struct hdac_device *codec) +{ + int err; + + err = widget_tree_create(codec); + if (err < 0) { + widget_tree_free(codec); + return err; + } + + return 0; +} + +void hda_widget_sysfs_exit(struct hdac_device *codec) +{ + widget_tree_free(codec); +} diff --git a/sound/hda/local.h b/sound/hda/local.h new file mode 100644 index 000000000000..d692f417ddc0 --- /dev/null +++ b/sound/hda/local.h @@ -0,0 +1,23 @@ +/* + * Local helper macros and functions for HD-audio core drivers + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +#define get_wcaps(codec, nid) \ + snd_hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) + +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +extern const struct attribute_group *hdac_dev_attr_groups[]; +int hda_widget_sysfs_init(struct hdac_device *codec); +void hda_widget_sysfs_exit(struct hdac_device *codec); + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/hda/trace.c b/sound/hda/trace.c new file mode 100644 index 000000000000..ca2d6bd94518 --- /dev/null +++ b/sound/hda/trace.c @@ -0,0 +1,6 @@ +/* + * tracepoint definitions for HD-audio core drivers + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/sound/hda/trace.h b/sound/hda/trace.h new file mode 100644 index 000000000000..33a7eb5573d4 --- /dev/null +++ b/sound/hda/trace.h @@ -0,0 +1,62 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hda + +#if !defined(__HDAC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __HDAC_TRACE_H + +#include <linux/tracepoint.h> +#include <linux/device.h> +#include <sound/hdaudio.h> + +#ifndef HDAC_MSG_MAX +#define HDAC_MSG_MAX 500 +#endif + +struct hdac_bus; +struct hdac_codec; + +TRACE_EVENT(hda_send_cmd, + TP_PROTO(struct hdac_bus *bus, unsigned int cmd), + TP_ARGS(bus, cmd), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] val=0x%08x", + dev_name((bus)->dev), (cmd) >> 28, cmd); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(hda_get_response, + TP_PROTO(struct hdac_bus *bus, unsigned int addr, unsigned int res), + TP_ARGS(bus, addr, res), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] val=0x%08x", + dev_name((bus)->dev), addr, res); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(hda_unsol_event, + TP_PROTO(struct hdac_bus *bus, u32 res, u32 res_ex), + TP_ARGS(bus, res, res_ex), + TP_STRUCT__entry(__dynamic_array(char, msg, HDAC_MSG_MAX)), + TP_fast_assign( + snprintf(__get_str(msg), HDAC_MSG_MAX, + "[%s:%d] res=0x%08x, res_ex=0x%08x", + dev_name((bus)->dev), res_ex & 0x0f, res, res_ex); + ), + TP_printk("%s", __get_str(msg)) +); +#endif /* __HDAC_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c index 88844881cbff..2183e9ebaa6d 100644 --- a/sound/i2c/other/ak4113.c +++ b/sound/i2c/other/ak4113.c @@ -73,7 +73,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, void *private_data, struct ak4113 **r_ak4113) { struct ak4113 *chip; - int err = 0; + int err; unsigned char reg; static struct snd_device_ops ops = { .dev_free = snd_ak4113_dev_free, @@ -109,7 +109,7 @@ int snd_ak4113_create(struct snd_card *card, ak4113_read_t *read, __fail: snd_ak4113_free(chip); - return err < 0 ? err : -EIO; + return err; } EXPORT_SYMBOL_GPL(snd_ak4113_create); diff --git a/sound/isa/sb/emu8000_synth.c b/sound/isa/sb/emu8000_synth.c index 72332dfada9a..4aa719cad331 100644 --- a/sound/isa/sb/emu8000_synth.c +++ b/sound/isa/sb/emu8000_synth.c @@ -34,8 +34,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu8000 */ -static int snd_emu8000_new_device(struct snd_seq_device *dev) +static int snd_emu8000_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; struct snd_emux *emu; @@ -93,8 +94,9 @@ static int snd_emu8000_new_device(struct snd_seq_device *dev) /* * free all resources */ -static int snd_emu8000_delete_device(struct snd_seq_device *dev) +static int snd_emu8000_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emu8000 *hw; if (dev->driver_data == NULL) @@ -114,21 +116,14 @@ static int snd_emu8000_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu8000_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu8000_new_device, - snd_emu8000_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU8000, &ops, - sizeof(struct snd_emu8000*)); -} - -static void __exit alsa_emu8000_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU8000); -} - -module_init(alsa_emu8000_init) -module_exit(alsa_emu8000_exit) +static struct snd_seq_driver emu8000_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu8000_probe, + .remove = snd_emu8000_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU8000, + .argsize = sizeof(struct snd_emu8000 *), +}; + +module_snd_seq_driver(emu8000_driver); diff --git a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c index b5a19708473a..0608a5a4289d 100644 --- a/sound/isa/wavefront/wavefront_fx.c +++ b/sound/isa/wavefront/wavefront_fx.c @@ -79,13 +79,13 @@ wavefront_fx_memset (snd_wavefront_t *dev, if (page < 0 || page > 7) { snd_printk ("FX memset: " "page must be >= 0 and <= 7\n"); - return -(EINVAL); + return -EINVAL; } if (addr < 0 || addr > 0x7f) { snd_printk ("FX memset: " "addr must be >= 0 and <= 7f\n"); - return -(EINVAL); + return -EINVAL; } if (cnt == 1) { @@ -118,7 +118,7 @@ wavefront_fx_memset (snd_wavefront_t *dev, snd_printk ("FX memset " "(0x%x, 0x%x, 0x%lx, %d) incomplete\n", page, addr, (unsigned long) data, cnt); - return -(EIO); + return -EIO; } } diff --git a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c index 33f5ec14fcfa..69f76ff5693d 100644 --- a/sound/isa/wavefront/wavefront_synth.c +++ b/sound/isa/wavefront/wavefront_synth.c @@ -793,7 +793,7 @@ wavefront_send_patch (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PATCH, NULL, buf)) { snd_printk ("download patch failed\n"); - return -(EIO); + return -EIO; } return (0); @@ -831,7 +831,7 @@ wavefront_send_program (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_PROGRAM, NULL, buf)) { snd_printk ("download patch failed\n"); - return -(EIO); + return -EIO; } return (0); @@ -952,7 +952,7 @@ wavefront_send_sample (snd_wavefront_t *dev, if (skip > 0 && header->hdr.s.SampleResolution != LINEAR_16BIT) { snd_printk ("channel selection only " "possible on 16-bit samples"); - return -(EINVAL); + return -EINVAL; } switch (skip) { @@ -1049,7 +1049,7 @@ wavefront_send_sample (snd_wavefront_t *dev, NULL, sample_hdr)) { snd_printk ("sample %sdownload refused.\n", header->size ? "" : "header "); - return -(EIO); + return -EIO; } if (header->size == 0) { @@ -1075,7 +1075,7 @@ wavefront_send_sample (snd_wavefront_t *dev, if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_BLOCK, NULL, NULL)) { snd_printk ("download block " "request refused.\n"); - return -(EIO); + return -EIO; } for (i = 0; i < blocksize; i++) { @@ -1135,12 +1135,12 @@ wavefront_send_sample (snd_wavefront_t *dev, if (dma_ack == -1) { snd_printk ("upload sample " "DMA ack timeout\n"); - return -(EIO); + return -EIO; } else { snd_printk ("upload sample " "DMA ack error 0x%x\n", dma_ack); - return -(EIO); + return -EIO; } } } @@ -1181,7 +1181,7 @@ wavefront_send_alias (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_SAMPLE_ALIAS, NULL, alias_hdr)) { snd_printk ("download alias failed.\n"); - return -(EIO); + return -EIO; } dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_ALIAS); @@ -1232,7 +1232,7 @@ wavefront_send_multisample (snd_wavefront_t *dev, wavefront_patch_info *header) msample_hdr)) { snd_printk ("download of multisample failed.\n"); kfree(msample_hdr); - return -(EIO); + return -EIO; } dev->sample_status[header->number] = (WF_SLOT_FILLED|WF_ST_MULTISAMPLE); @@ -1254,7 +1254,7 @@ wavefront_fetch_multisample (snd_wavefront_t *dev, if (snd_wavefront_cmd (dev, WFC_UPLOAD_MULTISAMPLE, log_ns, number)) { snd_printk ("upload multisample failed.\n"); - return -(EIO); + return -EIO; } DPRINT (WF_DEBUG_DATA, "msample %d has %d samples\n", @@ -1273,14 +1273,14 @@ wavefront_fetch_multisample (snd_wavefront_t *dev, if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); - return -(EIO); + return -EIO; } d[0] = val; if ((val = wavefront_read (dev)) == -1) { snd_printk ("upload multisample failed " "during sample loop.\n"); - return -(EIO); + return -EIO; } d[1] = val; @@ -1315,7 +1315,7 @@ wavefront_send_drum (snd_wavefront_t *dev, wavefront_patch_info *header) if (snd_wavefront_cmd (dev, WFC_DOWNLOAD_EDRUM_PROGRAM, NULL, drumbuf)) { snd_printk ("download drum failed.\n"); - return -(EIO); + return -EIO; } return (0); diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index fbcaa5434fd8..1e30e8475431 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -633,19 +633,25 @@ static int au1000_ac97_probe(struct platform_device *pdev) au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); - if (!au1000->stream[PLAYBACK]) + if (!au1000->stream[PLAYBACK]) { + err = -ENOMEM; goto out; + } au1000->stream[PLAYBACK]->dma = -1; au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL); - if (!au1000->stream[CAPTURE]) + if (!au1000->stream[CAPTURE]) { + err = -ENOMEM; goto out; + } au1000->stream[CAPTURE]->dma = -1; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) + if (!r) { + err = -ENODEV; goto out; + } err = -EBUSY; au1000->ac97_res_port = request_mem_region(r->start, resource_size(r), diff --git a/sound/oss/dev_table.c b/sound/oss/dev_table.c index d8cf3e58dc76..6dad51596b70 100644 --- a/sound/oss/dev_table.c +++ b/sound/oss/dev_table.c @@ -58,13 +58,13 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, if (vers != AUDIO_DRIVER_VERSION || driver_size > sizeof(struct audio_driver)) { printk(KERN_ERR "Sound: Incompatible audio driver for %s\n", name); - return -(EINVAL); + return -EINVAL; } num = sound_alloc_audiodev(); if (num == -1) { printk(KERN_ERR "sound: Too many audio drivers\n"); - return -(EBUSY); + return -EBUSY; } d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc(sizeof(struct audio_driver))); sound_nblocks++; @@ -79,7 +79,7 @@ int sound_install_audiodrv(int vers, char *name, struct audio_driver *driver, if (d == NULL || op == NULL) { printk(KERN_ERR "Sound: Can't allocate driver for (%s)\n", name); sound_unload_audiodev(num); - return -(ENOMEM); + return -ENOMEM; } init_waitqueue_head(&op->in_sleeper); init_waitqueue_head(&op->out_sleeper); diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c index 607cee4d545e..b6d19adf8f41 100644 --- a/sound/oss/opl3.c +++ b/sound/oss/opl3.c @@ -666,7 +666,7 @@ static int opl3_start_note (int dev, int voice, int note, int volume) opl3_command(map->ioaddr, FNUM_LOW + map->voice_num, data); data = 0x20 | ((block & 0x7) << 2) | ((fnum >> 8) & 0x3); - devc->voc[voice].keyon_byte = data; + devc->voc[voice].keyon_byte = data; opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num, data); if (voice_mode == 4) opl3_command(map->ioaddr, KEYON_BLOCK + map->voice_num + 3, data); @@ -717,7 +717,7 @@ static void freq_to_fnum (int freq, int *block, int *fnum) static void opl3_command (int io_addr, unsigned int addr, unsigned int val) { - int i; + int i; /* * The original 2-OP synth requires a quite long delay after writing to a diff --git a/sound/oss/sb_ess.c b/sound/oss/sb_ess.c index b47a69026f1b..57f7d25a2cd3 100644 --- a/sound/oss/sb_ess.c +++ b/sound/oss/sb_ess.c @@ -604,7 +604,7 @@ static void ess_audio_output_block_audio2 ess_chgmixer (devc, 0x78, 0x03, 0x03); /* Go */ devc->irq_mode_16 = IMODE_OUTPUT; - devc->intr_active_16 = 1; + devc->intr_active_16 = 1; } static void ess_audio_output_block @@ -1183,17 +1183,12 @@ FKS_test (devc); chip = "ES1688"; } - printk ( KERN_INFO "ESS chip %s %s%s\n" - , chip - , ( devc->sbmo.esstype == ESSTYPE_DETECT || devc->sbmo.esstype == ESSTYPE_LIKE20 - ? "detected" - : "specified" - ) - , ( devc->sbmo.esstype == ESSTYPE_LIKE20 - ? " (kernel 2.0 compatible)" - : "" - ) - ); + printk(KERN_INFO "ESS chip %s %s%s\n", chip, + (devc->sbmo.esstype == ESSTYPE_DETECT || + devc->sbmo.esstype == ESSTYPE_LIKE20) ? + "detected" : "specified", + devc->sbmo.esstype == ESSTYPE_LIKE20 ? + " (kernel 2.0 compatible)" : ""); sprintf(name,"ESS %s AudioDrive (rev %d)", chip, ess_minor & 0x0f); } else { diff --git a/sound/oss/sb_midi.c b/sound/oss/sb_midi.c index f139028e85c0..551ee7557b4e 100644 --- a/sound/oss/sb_midi.c +++ b/sound/oss/sb_midi.c @@ -179,14 +179,14 @@ void sb_dsp_midi_init(sb_devc * devc, struct module *owner) { printk(KERN_WARNING "Sound Blaster: failed to allocate MIDI memory.\n"); sound_unload_mididev(dev); - return; + return; } memcpy((char *) midi_devs[dev], (char *) &sb_midi_operations, sizeof(struct midi_operations)); if (owner) - midi_devs[dev]->owner = owner; - + midi_devs[dev]->owner = owner; + midi_devs[dev]->devc = devc; diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c index 9f039831114c..2226dda0eff0 100644 --- a/sound/oss/sys_timer.c +++ b/sound/oss/sys_timer.c @@ -50,29 +50,24 @@ tmr2ticks(int tmr_value) static void poll_def_tmr(unsigned long dummy) { + if (!opened) + return; + def_tmr.expires = (1) + jiffies; + add_timer(&def_tmr); - if (opened) - { + if (!tmr_running) + return; - { - def_tmr.expires = (1) + jiffies; - add_timer(&def_tmr); - } + spin_lock(&lock); + tmr_ctr++; + curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - if (tmr_running) - { - spin_lock(&lock); - tmr_ctr++; - curr_ticks = ticks_offs + tmr2ticks(tmr_ctr); - - if (curr_ticks >= next_event_time) - { - next_event_time = (unsigned long) -1; - sequencer_timer(0); - } - spin_unlock(&lock); - } - } + if (curr_ticks >= next_event_time) { + next_event_time = (unsigned long) -1; + sequencer_timer(0); + } + + spin_unlock(&lock); } static void diff --git a/sound/oss/v_midi.c b/sound/oss/v_midi.c index f0b4151d9b17..fc0ba276cc8f 100644 --- a/sound/oss/v_midi.c +++ b/sound/oss/v_midi.c @@ -49,13 +49,13 @@ static int v_midi_open (int dev, int mode, unsigned long flags; if (devc == NULL) - return -(ENXIO); + return -ENXIO; spin_lock_irqsave(&devc->lock,flags); if (devc->opened) { spin_unlock_irqrestore(&devc->lock,flags); - return -(EBUSY); + return -EBUSY; } devc->opened = 1; spin_unlock_irqrestore(&devc->lock,flags); diff --git a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c index 5ee2f17c287c..82259ca61e64 100644 --- a/sound/pci/ac97/ac97_codec.c +++ b/sound/pci/ac97/ac97_codec.c @@ -177,6 +177,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = { { 0x54524123, 0xffffffff, "TR28602", NULL, NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)] { 0x54584e03, 0xffffffff, "TLV320AIC27", NULL, NULL }, { 0x54584e20, 0xffffffff, "TLC320AD9xC", NULL, NULL }, +{ 0x56494120, 0xfffffff0, "VIA1613", patch_vt1613, NULL }, { 0x56494161, 0xffffffff, "VIA1612A", NULL, NULL }, // modified ICE1232 with S/PDIF { 0x56494170, 0xffffffff, "VIA1617A", patch_vt1617a, NULL }, // modified VT1616 with S/PDIF { 0x56494182, 0xffffffff, "VIA1618", patch_vt1618, NULL }, @@ -2901,7 +2902,8 @@ static int apply_quirk_str(struct snd_ac97 *ac97, const char *typestr) * Return: Zero if successful, or a negative error code on failure. */ -int snd_ac97_tune_hardware(struct snd_ac97 *ac97, struct ac97_quirk *quirk, const char *override) +int snd_ac97_tune_hardware(struct snd_ac97 *ac97, + const struct ac97_quirk *quirk, const char *override) { int result; diff --git a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c index ceaac1c41906..f4234edb878c 100644 --- a/sound/pci/ac97/ac97_patch.c +++ b/sound/pci/ac97/ac97_patch.c @@ -3352,6 +3352,33 @@ static int patch_cm9780(struct snd_ac97 *ac97) } /* + * VIA VT1613 codec + */ +static const struct snd_kcontrol_new snd_ac97_controls_vt1613[] = { +AC97_SINGLE("DC Offset removal", 0x5a, 10, 1, 0), +}; + +static int patch_vt1613_specific(struct snd_ac97 *ac97) +{ + return patch_build_controls(ac97, &snd_ac97_controls_vt1613[0], + ARRAY_SIZE(snd_ac97_controls_vt1613)); +}; + +static const struct snd_ac97_build_ops patch_vt1613_ops = { + .build_specific = patch_vt1613_specific +}; + +static int patch_vt1613(struct snd_ac97 *ac97) +{ + ac97->build_ops = &patch_vt1613_ops; + + ac97->flags |= AC97_HAS_NO_VIDEO; + ac97->caps |= AC97_BC_HEADPHONE; + + return 0; +} + +/* * VIA VT1616 codec */ static const struct snd_kcontrol_new snd_ac97_controls_vt1616[] = { diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c index 850a8c984c25..66ddd981d1d5 100644 --- a/sound/pci/ad1889.c +++ b/sound/pci/ad1889.c @@ -747,7 +747,7 @@ snd_ad1889_proc_init(struct snd_ad1889 *chip) snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x11d4, /* AD */ .subdevice = 0x1889, /* AD1889 */ diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index e5cd7be85355..1039eccbb895 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -2376,7 +2376,7 @@ static int snd_asihpi_cmode_add(struct snd_card_asihpi *asihpi, /*------------------------------------------------------------ Sampleclock source controls ------------------------------------------------------------*/ -static const char const *sampleclock_sources[] = { +static const char * const sampleclock_sources[] = { "N/A", "Local PLL", "Digital Sync", "Word External", "Word Header", "SMPTE", "Digital1", "Auto", "Network", "Invalid", "Prev Module", "BLU-Link", diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c index d5f15c9bbeda..42a20c806b39 100644 --- a/sound/pci/atiixp.c +++ b/sound/pci/atiixp.c @@ -1390,7 +1390,7 @@ static irqreturn_t snd_atiixp_interrupt(int irq, void *dev_id) * ac97 mixer section */ -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x103c, .subdevice = 0x006b, diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index a40a2b4c8fd7..33b2a0af1b59 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -1385,8 +1385,8 @@ snd_azf3328_ctrl_codec_activity(struct snd_azf3328 *chip, .running) && (!chip->codecs[peer_codecs[codec_type].other2] .running)); - } - if (call_function) + } + if (call_function) snd_azf3328_ctrl_enable_codecs(chip, enable); /* ...and adjust clock, too @@ -2126,7 +2126,8 @@ static struct snd_pcm_ops snd_azf3328_i2s_out_ops = { static int snd_azf3328_pcm(struct snd_azf3328 *chip) { -enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; /* pcm devices */ + /* pcm devices */ + enum { AZF_PCMDEV_STD, AZF_PCMDEV_I2S_OUT, NUM_AZF_PCMDEVS }; struct snd_pcm *pcm; int err; diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c index 1d0f2cad2f5a..6cf464d9043d 100644 --- a/sound/pci/cmipci.c +++ b/sound/pci/cmipci.c @@ -2062,7 +2062,7 @@ static int snd_cmipci_get_volume(struct snd_kcontrol *kcontrol, val = (snd_cmipci_mixer_read(cm, reg.right_reg) >> reg.right_shift) & reg.mask; if (reg.invert) val = reg.mask - val; - ucontrol->value.integer.value[1] = val; + ucontrol->value.integer.value[1] = val; } spin_unlock_irq(&cm->reg_lock); return 0; diff --git a/sound/pci/cs5535audio/cs5535audio.c b/sound/pci/cs5535audio/cs5535audio.c index 802c33f1cc59..963b912550d4 100644 --- a/sound/pci/cs5535audio/cs5535audio.c +++ b/sound/pci/cs5535audio/cs5535audio.c @@ -43,7 +43,7 @@ static char *ac97_quirk; module_param(ac97_quirk, charp, 0444); MODULE_PARM_DESC(ac97_quirk, "AC'97 board specific workarounds."); -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { #if 0 /* Not yet confirmed if all 5536 boards are HP only */ { .subvendor = PCI_VENDOR_ID_AMD, diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c index a962de03ebb6..862db9a0b041 100644 --- a/sound/pci/echoaudio/echoaudio.c +++ b/sound/pci/echoaudio/echoaudio.c @@ -1283,12 +1283,14 @@ static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol, static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct echoaudio *chip; + struct echoaudio *chip = snd_kcontrol_chip(kcontrol); + unsigned int out = ucontrol->id.index / num_busses_in(chip); + unsigned int in = ucontrol->id.index % num_busses_in(chip); - chip = snd_kcontrol_chip(kcontrol); - ucontrol->value.integer.value[0] = - chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)] - [ucontrol->id.index % num_busses_in(chip)]; + if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) + return -EINVAL; + + ucontrol->value.integer.value[0] = chip->monitor_gain[out][in]; return 0; } @@ -1297,12 +1299,14 @@ static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol, { struct echoaudio *chip; int changed, gain; - short out, in; + unsigned int out, in; changed = 0; chip = snd_kcontrol_chip(kcontrol); out = ucontrol->id.index / num_busses_in(chip); in = ucontrol->id.index % num_busses_in(chip); + if (out >= ECHO_MAXAUDIOOUTPUTS || in >= ECHO_MAXAUDIOINPUTS) + return -EINVAL; gain = ucontrol->value.integer.value[0]; if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT) return -EINVAL; diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index b4458a630a7c..54079f5d5673 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -707,6 +707,7 @@ static int emu1010_firmware_thread(void *data) { struct snd_emu10k1 *emu = data; u32 tmp, tmp2, reg; + u32 last_reg = 0; int err; for (;;) { @@ -782,7 +783,15 @@ static int emu1010_firmware_thread(void *data) msleep(10); /* Unmute all. Default is muted after a firmware load */ snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); + } else if (!reg && last_reg) { + /* Audio Dock removed */ + dev_info(emu->card->dev, + "emu1010: Audio Dock detached\n"); + /* Unmute all */ + snd_emu1010_fpga_write(emu, EMU_HANA_UNMUTE, EMU_UNMUTE); } + + last_reg = reg; } dev_info(emu->card->dev, "emu1010: firmware thread stopping\n"); return 0; @@ -1325,6 +1334,22 @@ static int snd_emu10k1_dev_free(struct snd_device *device) } static struct snd_emu_chip_details emu_chip_details[] = { + /* Audigy 5/Rx SB1550 */ + /* Tested by michael@gernoth.net 28 Mar 2015 */ + /* DSP: CA10300-IAT LF + * DAC: Cirrus Logic CS4382-KQZ + * ADC: Philips 1361T + * AC97: Sigmatel STAC9750 + * CA0151: None + */ + {.vendor = 0x1102, .device = 0x0008, .subsystem = 0x10241102, + .driver = "Audigy2", .name = "SB Audigy 5/Rx [SB1550]", + .id = "Audigy2", + .emu10k2_chip = 1, + .ca0108_chip = 1, + .spk71 = 1, + .adc_1361t = 1, /* 24 bit capture instead of 16bit */ + .ac97_chip = 1}, /* Audigy4 (Not PRO) SB0610 */ /* Tested by James@superbug.co.uk 4th April 2006 */ /* A_IOCFG bits diff --git a/sound/pci/emu10k1/emu10k1_synth.c b/sound/pci/emu10k1/emu10k1_synth.c index 4c41c903a840..5457d5613f6b 100644 --- a/sound/pci/emu10k1/emu10k1_synth.c +++ b/sound/pci/emu10k1/emu10k1_synth.c @@ -29,8 +29,9 @@ MODULE_LICENSE("GPL"); /* * create a new hardware dependent device for Emu10k1 */ -static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_probe(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; struct snd_emu10k1_synth_arg *arg; @@ -79,8 +80,9 @@ static int snd_emu10k1_synth_new_device(struct snd_seq_device *dev) return 0; } -static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) +static int snd_emu10k1_synth_remove(struct device *_dev) { + struct snd_seq_device *dev = to_seq_dev(_dev); struct snd_emux *emux; struct snd_emu10k1 *hw; unsigned long flags; @@ -104,21 +106,14 @@ static int snd_emu10k1_synth_delete_device(struct snd_seq_device *dev) * INIT part */ -static int __init alsa_emu10k1_synth_init(void) -{ - - static struct snd_seq_dev_ops ops = { - snd_emu10k1_synth_new_device, - snd_emu10k1_synth_delete_device, - }; - return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, &ops, - sizeof(struct snd_emu10k1_synth_arg)); -} - -static void __exit alsa_emu10k1_synth_exit(void) -{ - snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH); -} - -module_init(alsa_emu10k1_synth_init) -module_exit(alsa_emu10k1_synth_exit) +static struct snd_seq_driver emu10k1_synth_driver = { + .driver = { + .name = KBUILD_MODNAME, + .probe = snd_emu10k1_synth_probe, + .remove = snd_emu10k1_synth_remove, + }, + .id = SNDRV_SEQ_DEV_ID_EMU10K1_SYNTH, + .argsize = sizeof(struct snd_emu10k1_synth_arg), +}; + +module_snd_seq_driver(emu10k1_synth_driver); diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index 1de33025669a..55e57166256e 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -806,6 +806,108 @@ static struct snd_kcontrol_new snd_emu1010_internal_clock = .put = snd_emu1010_internal_clock_put }; +static int snd_emu1010_optical_out_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { + "SPDIF", "ADAT" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} + +static int snd_emu1010_optical_out_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.optical_out; + return 0; +} + +static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + u32 tmp; + int change = 0; + + val = ucontrol->value.enumerated.item[0]; + /* Limit: uinfo->value.enumerated.items = 2; */ + if (val >= 2) + return -EINVAL; + change = (emu->emu1010.optical_out != val); + if (change) { + emu->emu1010.optical_out = val; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + } + return change; +} + +static struct snd_kcontrol_new snd_emu1010_optical_out = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Optical Output Mode", + .count = 1, + .info = snd_emu1010_optical_out_info, + .get = snd_emu1010_optical_out_get, + .put = snd_emu1010_optical_out_put +}; + +static int snd_emu1010_optical_in_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { + "SPDIF", "ADAT" + }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} + +static int snd_emu1010_optical_in_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = emu->emu1010.optical_in; + return 0; +} + +static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); + unsigned int val; + u32 tmp; + int change = 0; + + val = ucontrol->value.enumerated.item[0]; + /* Limit: uinfo->value.enumerated.items = 2; */ + if (val >= 2) + return -EINVAL; + change = (emu->emu1010.optical_in != val); + if (change) { + emu->emu1010.optical_in = val; + tmp = (emu->emu1010.optical_in ? EMU_HANA_OPTICAL_IN_ADAT : 0) | + (emu->emu1010.optical_out ? EMU_HANA_OPTICAL_OUT_ADAT : 0); + snd_emu1010_fpga_write(emu, EMU_HANA_OPTICAL_TYPE, tmp); + } + return change; +} + +static struct snd_kcontrol_new snd_emu1010_optical_in = { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Optical Input Mode", + .count = 1, + .info = snd_emu1010_optical_in_info, + .get = snd_emu1010_optical_in_get, + .put = snd_emu1010_optical_in_put +}; + static int snd_audigy_i2c_capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2051,6 +2153,14 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_out, emu)); + if (err < 0) + return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_in, emu)); + if (err < 0) + return err; } else if (emu->card_capabilities->emu_model) { /* all other e-mu cards for now */ @@ -2086,6 +2196,14 @@ int snd_emu10k1_mixer(struct snd_emu10k1 *emu, snd_ctl_new1(&snd_emu1010_internal_clock, emu)); if (err < 0) return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_out, emu)); + if (err < 0) + return err; + err = snd_ctl_add(card, + snd_ctl_new1(&snd_emu1010_optical_in, emu)); + if (err < 0) + return err; } if ( emu->card_capabilities->i2c_adc) { diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c index 2ca9f2e93139..53745f4c2bf5 100644 --- a/sound/pci/emu10k1/emuproc.c +++ b/sound/pci/emu10k1/emuproc.c @@ -241,31 +241,22 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry, struct snd_emu10k1 *emu = entry->private_data; u32 value; u32 value2; - unsigned long flags; u32 rate; if (emu->card_capabilities->emu_model) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x38, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); if ((value & 0x1) == 0) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x2a, &value); snd_emu1010_fpga_read(emu, 0x2b, &value2); - spin_unlock_irqrestore(&emu->emu_lock, flags); rate = 0x1770000 / (((value << 5) | value2)+1); snd_iprintf(buffer, "ADAT Locked : %u\n", rate); } else { snd_iprintf(buffer, "ADAT Unlocked\n"); } - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x20, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); if ((value & 0x4) == 0) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, 0x28, &value); snd_emu1010_fpga_read(emu, 0x29, &value2); - spin_unlock_irqrestore(&emu->emu_lock, flags); rate = 0x1770000 / (((value << 5) | value2)+1); snd_iprintf(buffer, "SPDIF Locked : %d\n", rate); } else { @@ -410,14 +401,11 @@ static void snd_emu_proc_emu1010_reg_read(struct snd_info_entry *entry, { struct snd_emu10k1 *emu = entry->private_data; u32 value; - unsigned long flags; int i; snd_iprintf(buffer, "EMU1010 Registers:\n\n"); for(i = 0; i < 0x40; i+=1) { - spin_lock_irqsave(&emu->emu_lock, flags); snd_emu1010_fpga_read(emu, i, &value); - spin_unlock_irqrestore(&emu->emu_lock, flags); snd_iprintf(buffer, "%02X: %08X, %02X\n", i, value, (value >> 8) & 0x7f); } } diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 7f0f2c5a4e97..a5ed1c181784 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -5,6 +5,7 @@ config SND_HDA select SND_PCM select SND_VMASTER select SND_KCTL_JACK + select SND_HDA_CORE config SND_HDA_INTEL tristate "HD Audio PCI" diff --git a/sound/pci/hda/Makefile b/sound/pci/hda/Makefile index 194f30935e77..af78fb33a4fd 100644 --- a/sound/pci/hda/Makefile +++ b/sound/pci/hda/Makefile @@ -4,13 +4,12 @@ snd-hda-tegra-objs := hda_tegra.o # for haswell power well snd-hda-intel-$(CONFIG_SND_HDA_I915) += hda_i915.o -snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o +snd-hda-codec-y := hda_bind.o hda_codec.o hda_jack.o hda_auto_parser.o hda_sysfs.o snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o snd-hda-codec-$(CONFIG_SND_HDA_INPUT_BEEP) += hda_beep.o # for trace-points -CFLAGS_hda_codec.o := -I$(src) CFLAGS_hda_controller.o := -I$(src) snd-hda-codec-generic-objs := hda_generic.o diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 3f8706bb3d16..03b7399bb7f0 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -172,7 +172,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, const hda_nid_t *ignore_nids, unsigned int cond_flags) { - hda_nid_t nid, end_nid; + hda_nid_t nid; short seq, assoc_line_out; struct auto_out_pin line_out[ARRAY_SIZE(cfg->line_out_pins)]; struct auto_out_pin speaker_out[ARRAY_SIZE(cfg->speaker_pins)]; @@ -189,8 +189,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, memset(hp_out, 0, sizeof(hp_out)); assoc_line_out = 0; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); unsigned int def_conf; @@ -410,7 +409,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, * debug prints of the parsed results */ codec_info(codec, "autoconfig for %s: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n", - codec->chip_name, cfg->line_outs, cfg->line_out_pins[0], + codec->core.chip_name, cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1], cfg->line_out_pins[2], cfg->line_out_pins[3], cfg->line_out_pins[4], cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" : @@ -836,33 +835,33 @@ static void apply_fixup(struct hda_codec *codec, int id, int action, int depth) if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pincfg for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_apply_pincfgs(codec, fix->v.pins); break; case HDA_FIXUP_VERBS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs) break; codec_dbg(codec, "%s: Apply fix-verbs for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) break; codec_dbg(codec, "%s: Apply fix-func for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); fix->v.func(codec, fix, action); break; case HDA_FIXUP_PINCTLS: if (action != HDA_FIXUP_ACT_PROBE || !fix->v.pins) break; codec_dbg(codec, "%s: Apply pinctl for %s\n", - codec->chip_name, modelname); + codec->core.chip_name, modelname); set_pin_targets(codec, fix->v.pins); break; default: codec_err(codec, "%s: Invalid fixup type %d\n", - codec->chip_name, fix->type); + codec->core.chip_name, fix->type); break; } if (!fix->chained || fix->chained_before) @@ -912,16 +911,16 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, return; for (pq = pin_quirk; pq->subvendor; pq++) { - if ((codec->subsystem_id & 0xffff0000) != (pq->subvendor << 16)) + if ((codec->core.subsystem_id & 0xffff0000) != (pq->subvendor << 16)) continue; - if (codec->vendor_id != pq->codec) + if (codec->core.vendor_id != pq->codec) continue; if (pin_config_match(codec, pq->pins)) { codec->fixup_id = pq->value; #ifdef CONFIG_SND_DEBUG_VERBOSE codec->fixup_name = pq->name; codec_dbg(codec, "%s: picked fixup %s (pin match)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); #endif codec->fixup_list = fixlist; return; @@ -963,7 +962,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = NULL; codec->fixup_id = HDA_FIXUP_ID_NO_FIXUP; codec_dbg(codec, "%s: picked no fixup (nofixup specified)\n", - codec->chip_name); + codec->core.chip_name); return; } @@ -974,7 +973,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, codec->fixup_name = models->name; codec->fixup_list = fixlist; codec_dbg(codec, "%s: picked fixup %s (model specified)\n", - codec->chip_name, codec->fixup_name); + codec->core.chip_name, codec->fixup_name); return; } models++; @@ -987,7 +986,7 @@ void snd_hda_pick_fixup(struct hda_codec *codec, #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (PCI SSID%s)\n", - codec->chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); + codec->core.chip_name, name, q->subdevice_mask ? "" : " - vendor generic"); #endif } } @@ -996,12 +995,12 @@ void snd_hda_pick_fixup(struct hda_codec *codec, unsigned int vendorid = q->subdevice | (q->subvendor << 16); unsigned int mask = 0xffff0000 | q->subdevice_mask; - if ((codec->subsystem_id & mask) == (vendorid & mask)) { + if ((codec->core.subsystem_id & mask) == (vendorid & mask)) { id = q->value; #ifdef CONFIG_SND_DEBUG_VERBOSE name = q->name; codec_dbg(codec, "%s: picked fixup %s (codec SSID)\n", - codec->chip_name, name); + codec->core.chip_name, name); #endif break; } diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c index 1e7de08e77cb..3364dc0fdeab 100644 --- a/sound/pci/hda/hda_beep.c +++ b/sound/pci/hda/hda_beep.c @@ -33,30 +33,36 @@ enum { DIGBEEP_HZ_MAX = 12000000, /* 12 KHz */ }; -static void snd_hda_generate_beep(struct work_struct *work) +/* generate or stop tone */ +static void generate_tone(struct hda_beep *beep, int tone) { - struct hda_beep *beep = - container_of(work, struct hda_beep, beep_work); struct hda_codec *codec = beep->codec; - int tone; - if (!beep->enabled) - return; - - tone = beep->tone; if (tone && !beep->playing) { snd_hda_power_up(codec); + if (beep->power_hook) + beep->power_hook(beep, true); beep->playing = 1; } - /* generate tone */ snd_hda_codec_write(codec, beep->nid, 0, AC_VERB_SET_BEEP_CONTROL, tone); if (!tone && beep->playing) { beep->playing = 0; + if (beep->power_hook) + beep->power_hook(beep, false); snd_hda_power_down(codec); } } +static void snd_hda_generate_beep(struct work_struct *work) +{ + struct hda_beep *beep = + container_of(work, struct hda_beep, beep_work); + + if (beep->enabled) + generate_tone(beep, beep->tone); +} + /* (non-standard) Linear beep tone calculation for IDT/STAC codecs * * The tone frequency of beep generator on IDT/STAC codecs is @@ -130,10 +136,7 @@ static void turn_off_beep(struct hda_beep *beep) cancel_work_sync(&beep->beep_work); if (beep->playing) { /* turn off beep */ - snd_hda_codec_write(beep->codec, beep->nid, 0, - AC_VERB_SET_BEEP_CONTROL, 0); - beep->playing = 0; - snd_hda_power_down(beep->codec); + generate_tone(beep, 0); } } @@ -160,15 +163,15 @@ static int snd_hda_do_attach(struct hda_beep *beep) input_dev->name = "HDA Digital PCBeep"; input_dev->phys = beep->phys; input_dev->id.bustype = BUS_PCI; + input_dev->dev.parent = &codec->card->card_dev; - input_dev->id.vendor = codec->vendor_id >> 16; - input_dev->id.product = codec->vendor_id & 0xffff; + input_dev->id.vendor = codec->core.vendor_id >> 16; + input_dev->id.product = codec->core.vendor_id & 0xffff; input_dev->id.version = 0x01; input_dev->evbit[0] = BIT_MASK(EV_SND); input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); input_dev->event = snd_hda_beep_event; - input_dev->dev.parent = &codec->dev; input_set_drvdata(input_dev, beep); beep->dev = input_dev; @@ -224,7 +227,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) if (beep == NULL) return -ENOMEM; snprintf(beep->phys, sizeof(beep->phys), - "card%d/codec#%d/beep0", codec->bus->card->number, codec->addr); + "card%d/codec#%d/beep0", codec->card->number, codec->addr); /* enable linear scale */ snd_hda_codec_write_cache(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_2, 0x01); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h index a63b5e077332..46524ff7e79e 100644 --- a/sound/pci/hda/hda_beep.h +++ b/sound/pci/hda/hda_beep.h @@ -40,6 +40,7 @@ struct hda_beep { unsigned int playing:1; struct work_struct beep_work; /* scheduled task for beep event */ struct mutex mutex; + void (*power_hook)(struct hda_beep *beep, bool on); }; #ifdef CONFIG_SND_HDA_INPUT_BEEP diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c new file mode 100644 index 000000000000..00aa31c5f08e --- /dev/null +++ b/sound/pci/hda/hda_bind.c @@ -0,0 +1,273 @@ +/* + * HD-audio codec driver binding + * Copyright (c) Takashi Iwai <tiwai@suse.de> + */ + +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/export.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <sound/core.h> +#include "hda_codec.h" +#include "hda_local.h" + +/* + * find a matching codec preset + */ +static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + struct hda_codec_driver *driver = + container_of(drv, struct hda_codec_driver, core); + const struct hda_codec_preset *preset; + /* check probe_id instead of vendor_id if set */ + u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id; + + for (preset = driver->preset; preset->id; preset++) { + u32 mask = preset->mask; + + if (preset->afg && preset->afg != codec->core.afg) + continue; + if (preset->mfg && preset->mfg != codec->core.mfg) + continue; + if (!mask) + mask = ~0; + if (preset->id == (id & mask) && + (!preset->rev || preset->rev == codec->core.revision_id)) { + codec->preset = preset; + return 1; + } + } + return 0; +} + +/* process an unsolicited event */ +static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev) +{ + struct hda_codec *codec = container_of(dev, struct hda_codec, core); + + if (codec->patch_ops.unsol_event) + codec->patch_ops.unsol_event(codec, ev); +} + +/* reset the codec name from the preset */ +static int codec_refresh_name(struct hda_codec *codec, const char *name) +{ + if (name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); + } + return codec->core.chip_name ? 0 : -ENOMEM; +} + +static int hda_codec_driver_probe(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct module *owner = dev->driver->owner; + int err; + + if (WARN_ON(!codec->preset)) + return -EINVAL; + + err = codec_refresh_name(codec, codec->preset->name); + if (err < 0) + goto error; + err = snd_hdac_regmap_init(&codec->core); + if (err < 0) + goto error; + + if (!try_module_get(owner)) { + err = -EINVAL; + goto error; + } + + err = codec->preset->patch(codec); + if (err < 0) + goto error_module; + + err = snd_hda_codec_build_pcms(codec); + if (err < 0) + goto error_module; + err = snd_hda_codec_build_controls(codec); + if (err < 0) + goto error_module; + if (codec->card->registered) { + err = snd_card_register(codec->card); + if (err < 0) + goto error_module; + snd_hda_codec_register(codec); + } + + codec->core.lazy_cache = true; + return 0; + + error_module: + module_put(owner); + + error: + snd_hda_codec_cleanup_for_unbind(codec); + return err; +} + +static int hda_codec_driver_remove(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (codec->patch_ops.free) + codec->patch_ops.free(codec); + snd_hda_codec_cleanup_for_unbind(codec); + module_put(dev->driver->owner); + return 0; +} + +static void hda_codec_driver_shutdown(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + + if (!pm_runtime_suspended(dev) && codec->patch_ops.reboot_notify) + codec->patch_ops.reboot_notify(codec); +} + +int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, + struct module *owner) +{ + drv->core.driver.name = name; + drv->core.driver.owner = owner; + drv->core.driver.bus = &snd_hda_bus_type; + drv->core.driver.probe = hda_codec_driver_probe; + drv->core.driver.remove = hda_codec_driver_remove; + drv->core.driver.shutdown = hda_codec_driver_shutdown; + drv->core.driver.pm = &hda_codec_driver_pm; + drv->core.type = HDA_DEV_LEGACY; + drv->core.match = hda_codec_match; + drv->core.unsol_event = hda_codec_unsol_event; + return driver_register(&drv->core.driver); +} +EXPORT_SYMBOL_GPL(__hda_codec_driver_register); + +void hda_codec_driver_unregister(struct hda_codec_driver *drv) +{ + driver_unregister(&drv->core.driver); +} +EXPORT_SYMBOL_GPL(hda_codec_driver_unregister); + +static inline bool codec_probed(struct hda_codec *codec) +{ + return device_attach(hda_codec_dev(codec)) > 0 && codec->preset; +} + +/* try to auto-load and bind the codec module */ +static void codec_bind_module(struct hda_codec *codec) +{ +#ifdef MODULE + request_module("snd-hda-codec-id:%08x", codec->core.vendor_id); + if (codec_probed(codec)) + return; + request_module("snd-hda-codec-id:%04x*", + (codec->core.vendor_id >> 16) & 0xffff); + if (codec_probed(codec)) + return; +#endif +} + +#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) +/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ +static bool is_likely_hdmi_codec(struct hda_codec *codec) +{ + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) { + unsigned int wcaps = get_wcaps(codec, nid); + switch (get_wcaps_type(wcaps)) { + case AC_WID_AUD_IN: + return false; /* HDMI parser supports only HDMI out */ + case AC_WID_AUD_OUT: + if (!(wcaps & AC_WCAP_DIGITAL)) + return false; + break; + } + } + return true; +} +#else +/* no HDMI codec parser support */ +#define is_likely_hdmi_codec(codec) false +#endif /* CONFIG_SND_HDA_CODEC_HDMI */ + +static int codec_bind_generic(struct hda_codec *codec) +{ + if (codec->probe_id) + return -ENODEV; + + if (is_likely_hdmi_codec(codec)) { + codec->probe_id = HDA_CODEC_ID_GENERIC_HDMI; +#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) + request_module("snd-hda-codec-hdmi"); +#endif + if (codec_probed(codec)) + return 0; + } + + codec->probe_id = HDA_CODEC_ID_GENERIC; +#if IS_MODULE(CONFIG_SND_HDA_GENERIC) + request_module("snd-hda-codec-generic"); +#endif + if (codec_probed(codec)) + return 0; + return -ENODEV; +} + +#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) +#define is_generic_config(codec) \ + (codec->modelname && !strcmp(codec->modelname, "generic")) +#else +#define is_generic_config(codec) 0 +#endif + +/** + * snd_hda_codec_configure - (Re-)configure the HD-audio codec + * @codec: the HDA codec + * + * Start parsing of the given codec tree and (re-)initialize the whole + * patch instance. + * + * Returns 0 if successful or a negative error code. + */ +int snd_hda_codec_configure(struct hda_codec *codec) +{ + int err; + + if (is_generic_config(codec)) + codec->probe_id = HDA_CODEC_ID_GENERIC; + else + codec->probe_id = 0; + + err = snd_hdac_device_register(&codec->core); + if (err < 0) + return err; + + if (!codec->preset) + codec_bind_module(codec); + if (!codec->preset) { + err = codec_bind_generic(codec); + if (err < 0) { + codec_err(codec, "Unable to bind the codec\n"); + goto error; + } + } + + /* audio codec should override the mixer name */ + if (codec->core.afg || !*codec->card->mixername) + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), "%s %s", + codec->core.vendor_name, codec->core.chip_name); + return 0; + + error: + snd_hdac_device_unregister(&codec->core); + return err; +} +EXPORT_SYMBOL_GPL(snd_hda_codec_configure); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 2fe86d2e1b09..e70a7fb393dd 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -26,6 +26,8 @@ #include <linux/mutex.h> #include <linux/module.h> #include <linux/async.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include "hda_codec.h" #include <sound/asoundef.h> @@ -37,97 +39,20 @@ #include "hda_jack.h" #include <sound/hda_hwdep.h> -#define CREATE_TRACE_POINTS -#include "hda_trace.h" - -/* - * vendor / preset table - */ - -struct hda_vendor_id { - unsigned int id; - const char *name; -}; - -/* codec vendor labels */ -static struct hda_vendor_id hda_vendor_ids[] = { - { 0x1002, "ATI" }, - { 0x1013, "Cirrus Logic" }, - { 0x1057, "Motorola" }, - { 0x1095, "Silicon Image" }, - { 0x10de, "Nvidia" }, - { 0x10ec, "Realtek" }, - { 0x1102, "Creative" }, - { 0x1106, "VIA" }, - { 0x111d, "IDT" }, - { 0x11c1, "LSI" }, - { 0x11d4, "Analog Devices" }, - { 0x13f6, "C-Media" }, - { 0x14f1, "Conexant" }, - { 0x17e8, "Chrontel" }, - { 0x1854, "LG" }, - { 0x1aec, "Wolfson Microelectronics" }, - { 0x1af4, "QEMU" }, - { 0x434d, "C-Media" }, - { 0x8086, "Intel" }, - { 0x8384, "SigmaTel" }, - {} /* terminator */ -}; - -static DEFINE_MUTEX(preset_mutex); -static LIST_HEAD(hda_preset_tables); - -/** - * snd_hda_add_codec_preset - Add a codec preset to the chain - * @preset: codec preset table to add - */ -int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset) -{ - mutex_lock(&preset_mutex); - list_add_tail(&preset->list, &hda_preset_tables); - mutex_unlock(&preset_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_add_codec_preset); - -/** - * snd_hda_delete_codec_preset - Delete a codec preset from the chain - * @preset: codec preset table to delete - */ -int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset) -{ - mutex_lock(&preset_mutex); - list_del(&preset->list); - mutex_unlock(&preset_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_delete_codec_preset); - #ifdef CONFIG_PM -#define codec_in_pm(codec) ((codec)->in_pm) -static void hda_power_work(struct work_struct *work); -static void hda_keep_power_on(struct hda_codec *codec); -#define hda_codec_is_power_on(codec) ((codec)->power_on) - -static void hda_call_pm_notify(struct hda_codec *codec, bool power_up) -{ - struct hda_bus *bus = codec->bus; - - if ((power_up && codec->pm_up_notified) || - (!power_up && !codec->pm_up_notified)) - return; - if (bus->ops.pm_notify) - bus->ops.pm_notify(bus, power_up); - codec->pm_up_notified = power_up; -} - +#define codec_in_pm(codec) atomic_read(&(codec)->core.in_pm) +#define hda_codec_is_power_on(codec) \ + (!pm_runtime_suspended(hda_codec_dev(codec))) #else #define codec_in_pm(codec) 0 -static inline void hda_keep_power_on(struct hda_codec *codec) {} #define hda_codec_is_power_on(codec) 1 -#define hda_call_pm_notify(codec, state) {} #endif +#define codec_has_epss(codec) \ + ((codec)->core.power_caps & AC_PWRST_EPSS) +#define codec_has_clkstop(codec) \ + ((codec)->core.power_caps & AC_PWRST_CLKSTOP) + /** * snd_hda_get_jack_location - Give a location string of the jack * @cfg: pin default config value @@ -199,67 +124,32 @@ const char *snd_hda_get_jack_type(u32 cfg) EXPORT_SYMBOL_GPL(snd_hda_get_jack_type); /* - * Compose a 32bit command word to be sent to the HD-audio controller - */ -static inline unsigned int -make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int flags, - unsigned int verb, unsigned int parm) -{ - u32 val; - - if ((codec->addr & ~0xf) || (nid & ~0x7f) || - (verb & ~0xfff) || (parm & ~0xffff)) { - codec_err(codec, "hda-codec: out of range cmd %x:%x:%x:%x\n", - codec->addr, nid, verb, parm); - return ~0; - } - - val = (u32)codec->addr << 28; - val |= (u32)nid << 20; - val |= verb << 8; - val |= parm; - return val; -} - -/* - * Send and receive a verb + * Send and receive a verb - passed to exec_verb override for hdac_device */ -static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, - int flags, unsigned int *res) +static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd, + unsigned int flags, unsigned int *res) { + struct hda_codec *codec = container_of(dev, struct hda_codec, core); struct hda_bus *bus = codec->bus; int err; if (cmd == ~0) return -1; - if (res) - *res = -1; again: - snd_hda_power_up(codec); - mutex_lock(&bus->cmd_mutex); + snd_hda_power_up_pm(codec); + mutex_lock(&bus->core.cmd_mutex); if (flags & HDA_RW_NO_RESPONSE_FALLBACK) bus->no_response_fallback = 1; - for (;;) { - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); - if (err != -EAGAIN) - break; - /* process pending verbs */ - bus->ops.get_response(bus, codec->addr); - } - if (!err && res) { - *res = bus->ops.get_response(bus, codec->addr); - trace_hda_get_response(codec, *res); - } + err = snd_hdac_bus_exec_verb_unlocked(&bus->core, codec->core.addr, + cmd, res); bus->no_response_fallback = 0; - mutex_unlock(&bus->cmd_mutex); - snd_hda_power_down(codec); - if (!codec_in_pm(codec) && res && *res == -1 && bus->rirb_error) { + mutex_unlock(&bus->core.cmd_mutex); + snd_hda_power_down_pm(codec); + if (!codec_in_pm(codec) && res && err < 0 && bus->rirb_error) { if (bus->response_reset) { codec_dbg(codec, "resetting BUS due to fatal communication error\n"); - trace_hda_bus_reset(bus); bus->ops.bus_reset(bus); } goto again; @@ -286,9 +176,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned cmd = make_codec_cmd(codec, nid, flags, verb, parm); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); unsigned int res; - if (codec_exec_verb(codec, cmd, flags, &res)) + if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res)) return -1; return res; } @@ -309,10 +199,8 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_read); int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm) { - unsigned int cmd = make_codec_cmd(codec, nid, flags, verb, parm); - unsigned int res; - return codec_exec_verb(codec, cmd, flags, - codec->bus->sync_write ? &res : NULL); + unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm); + return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL); } EXPORT_SYMBOL_GPL(snd_hda_codec_write); @@ -331,30 +219,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq) } EXPORT_SYMBOL_GPL(snd_hda_sequence_write); -/** - * snd_hda_get_sub_nodes - get the range of sub nodes - * @codec: the HDA codec - * @nid: NID to parse - * @start_id: the pointer to store the start NID - * - * Parse the NID and store the start NID of its sub-nodes. - * Returns the number of sub-nodes. - */ -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id) -{ - unsigned int parm; - - parm = snd_hda_param_read(codec, nid, AC_PAR_NODE_COUNT); - if (parm == -1) { - *start_id = 0; - return 0; - } - *start_id = (parm >> 16) & 0x7fff; - return (int)(parm & 0x7fff); -} -EXPORT_SYMBOL_GPL(snd_hda_get_sub_nodes); - /* connection list element */ struct hda_conn_list { struct list_head list; @@ -495,128 +359,6 @@ int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_get_connections); -/* return CONNLIST_LEN parameter of the given widget */ -static unsigned int get_num_conns(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int wcaps = get_wcaps(codec, nid); - unsigned int parm; - - if (!(wcaps & AC_WCAP_CONN_LIST) && - get_wcaps_type(wcaps) != AC_WID_VOL_KNB) - return 0; - - parm = snd_hda_param_read(codec, nid, AC_PAR_CONNLIST_LEN); - if (parm == -1) - parm = 0; - return parm; -} - -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid) -{ - return snd_hda_get_raw_connections(codec, nid, NULL, 0); -} - -/** - * snd_hda_get_raw_connections - copy connection list without cache - * @codec: the HDA codec - * @nid: NID to parse - * @conn_list: connection list array - * @max_conns: max. number of connections to store - * - * Like snd_hda_get_connections(), copy the connection list but without - * checking through the connection-list cache. - * Currently called only from hda_proc.c, so not exported. - */ -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns) -{ - unsigned int parm; - int i, conn_len, conns; - unsigned int shift, num_elems, mask; - hda_nid_t prev_nid; - int null_count = 0; - - parm = get_num_conns(codec, nid); - if (!parm) - return 0; - - if (parm & AC_CLIST_LONG) { - /* long form */ - shift = 16; - num_elems = 2; - } else { - /* short form */ - shift = 8; - num_elems = 4; - } - conn_len = parm & AC_CLIST_LENGTH; - mask = (1 << (shift-1)) - 1; - - if (!conn_len) - return 0; /* no connection */ - - if (conn_len == 1) { - /* single connection */ - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, 0); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - if (conn_list) - conn_list[0] = parm & mask; - return 1; - } - - /* multi connection */ - conns = 0; - prev_nid = 0; - for (i = 0; i < conn_len; i++) { - int range_val; - hda_nid_t val, n; - - if (i % num_elems == 0) { - parm = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONNECT_LIST, i); - if (parm == -1 && codec->bus->rirb_error) - return -EIO; - } - range_val = !!(parm & (1 << (shift-1))); /* ranges */ - val = parm & mask; - if (val == 0 && null_count++) { /* no second chance */ - codec_dbg(codec, - "invalid CONNECT_LIST verb %x[%i]:%x\n", - nid, i, parm); - return 0; - } - parm >>= shift; - if (range_val) { - /* ranges between the previous and this one */ - if (!prev_nid || prev_nid >= val) { - codec_warn(codec, - "invalid dep_range_val %x:%x\n", - prev_nid, val); - continue; - } - for (n = prev_nid + 1; n <= val; n++) { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = n; - } - conns++; - } - } else { - if (conn_list) { - if (conns >= max_conns) - return -ENOSPC; - conn_list[conns] = val; - } - conns++; - } - prev_nid = val; - } - return conns; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -741,90 +483,6 @@ int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid, return devices; } -/** - * snd_hda_queue_unsol_event - add an unsolicited event to queue - * @bus: the BUS - * @res: unsolicited event (lower 32bit of RIRB entry) - * @res_ex: codec addr and flags (upper 32bit or RIRB entry) - * - * Adds the given event to the queue. The events are processed in - * the workqueue asynchronously. Call this function in the interrupt - * hanlder when RIRB receives an unsolicited event. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) -{ - struct hda_bus_unsolicited *unsol; - unsigned int wp; - - if (!bus || !bus->workq) - return 0; - - trace_hda_unsol_event(bus, res, res_ex); - unsol = bus->unsol; - if (!unsol) - return 0; - - wp = (unsol->wp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->wp = wp; - - wp <<= 1; - unsol->queue[wp] = res; - unsol->queue[wp + 1] = res_ex; - - queue_work(bus->workq, &unsol->work); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_queue_unsol_event); - -/* - * process queued unsolicited events - */ -static void process_unsol_events(struct work_struct *work) -{ - struct hda_bus_unsolicited *unsol = - container_of(work, struct hda_bus_unsolicited, work); - struct hda_bus *bus = unsol->bus; - struct hda_codec *codec; - unsigned int rp, caddr, res; - - while (unsol->rp != unsol->wp) { - rp = (unsol->rp + 1) % HDA_UNSOL_QUEUE_SIZE; - unsol->rp = rp; - rp <<= 1; - res = unsol->queue[rp]; - caddr = unsol->queue[rp + 1]; - if (!(caddr & (1 << 4))) /* no unsolicited event? */ - continue; - codec = bus->caddr_tbl[caddr & 0x0f]; - if (codec && codec->patch_ops.unsol_event) - codec->patch_ops.unsol_event(codec, res); - } -} - -/* - * initialize unsolicited queue - */ -static int init_unsol_queue(struct hda_bus *bus) -{ - struct hda_bus_unsolicited *unsol; - - if (bus->unsol) /* already initialized */ - return 0; - - unsol = kzalloc(sizeof(*unsol), GFP_KERNEL); - if (!unsol) { - dev_err(bus->card->dev, "can't allocate unsolicited queue\n"); - return -ENOMEM; - } - INIT_WORK(&unsol->work, process_unsol_events); - unsol->bus = bus; - bus->unsol = unsol; - return 0; -} - /* * destructor */ @@ -832,16 +490,9 @@ static void snd_hda_bus_free(struct hda_bus *bus) { if (!bus) return; - - WARN_ON(!list_empty(&bus->codec_list)); - if (bus->workq) - flush_workqueue(bus->workq); - kfree(bus->unsol); if (bus->ops.private_free) bus->ops.private_free(bus); - if (bus->workq) - destroy_workqueue(bus->workq); - + snd_hdac_bus_exit(&bus->core); kfree(bus); } @@ -858,17 +509,35 @@ static int snd_hda_bus_dev_disconnect(struct snd_device *device) return 0; } +/* hdac_bus_ops translations */ +static int _hda_bus_command(struct hdac_bus *_bus, unsigned int cmd) +{ + struct hda_bus *bus = container_of(_bus, struct hda_bus, core); + return bus->ops.command(bus, cmd); +} + +static int _hda_bus_get_response(struct hdac_bus *_bus, unsigned int addr, + unsigned int *res) +{ + struct hda_bus *bus = container_of(_bus, struct hda_bus, core); + *res = bus->ops.get_response(bus, addr); + return bus->rirb_error ? -EIO : 0; +} + +static const struct hdac_bus_ops bus_ops = { + .command = _hda_bus_command, + .get_response = _hda_bus_get_response, +}; + /** * snd_hda_bus_new - create a HDA bus * @card: the card entry - * @temp: the template for hda_bus information * @busp: the pointer to store the created bus instance * * Returns 0 if successful, or a negative error code. */ int snd_hda_bus_new(struct snd_card *card, - const struct hda_bus_template *temp, - struct hda_bus **busp) + struct hda_bus **busp) { struct hda_bus *bus; int err; @@ -877,40 +546,21 @@ int snd_hda_bus_new(struct snd_card *card, .dev_free = snd_hda_bus_dev_free, }; - if (snd_BUG_ON(!temp)) - return -EINVAL; - if (snd_BUG_ON(!temp->ops.command || !temp->ops.get_response)) - return -EINVAL; - if (busp) *busp = NULL; bus = kzalloc(sizeof(*bus), GFP_KERNEL); - if (bus == NULL) { - dev_err(card->dev, "can't allocate struct hda_bus\n"); + if (!bus) return -ENOMEM; + + err = snd_hdac_bus_init(&bus->core, card->dev, &bus_ops); + if (err < 0) { + kfree(bus); + return err; } bus->card = card; - bus->private_data = temp->private_data; - bus->pci = temp->pci; - bus->modelname = temp->modelname; - bus->power_save = temp->power_save; - bus->ops = temp->ops; - - mutex_init(&bus->cmd_mutex); mutex_init(&bus->prepare_mutex); - INIT_LIST_HEAD(&bus->codec_list); - - snprintf(bus->workq_name, sizeof(bus->workq_name), - "hd-audio%d", card->number); - bus->workq = create_singlethread_workqueue(bus->workq_name); - if (!bus->workq) { - dev_err(card->dev, "cannot create workqueue %s\n", - bus->workq_name); - kfree(bus); - return -ENOMEM; - } err = snd_device_new(card, SNDRV_DEV_BUS, bus, &dev_ops); if (err < 0) { @@ -923,140 +573,6 @@ int snd_hda_bus_new(struct snd_card *card, } EXPORT_SYMBOL_GPL(snd_hda_bus_new); -#if IS_ENABLED(CONFIG_SND_HDA_GENERIC) -#define is_generic_config(codec) \ - (codec->modelname && !strcmp(codec->modelname, "generic")) -#else -#define is_generic_config(codec) 0 -#endif - -#ifdef MODULE -#define HDA_MODREQ_MAX_COUNT 2 /* two request_modules()'s */ -#else -#define HDA_MODREQ_MAX_COUNT 0 /* all presets are statically linked */ -#endif - -/* - * find a matching codec preset - */ -static const struct hda_codec_preset * -find_codec_preset(struct hda_codec *codec) -{ - struct hda_codec_preset_list *tbl; - const struct hda_codec_preset *preset; - unsigned int mod_requested = 0; - - again: - mutex_lock(&preset_mutex); - list_for_each_entry(tbl, &hda_preset_tables, list) { - if (!try_module_get(tbl->owner)) { - codec_err(codec, "cannot module_get\n"); - continue; - } - for (preset = tbl->preset; preset->id; preset++) { - u32 mask = preset->mask; - if (preset->afg && preset->afg != codec->afg) - continue; - if (preset->mfg && preset->mfg != codec->mfg) - continue; - if (!mask) - mask = ~0; - if (preset->id == (codec->vendor_id & mask) && - (!preset->rev || - preset->rev == codec->revision_id)) { - mutex_unlock(&preset_mutex); - codec->owner = tbl->owner; - return preset; - } - } - module_put(tbl->owner); - } - mutex_unlock(&preset_mutex); - - if (mod_requested < HDA_MODREQ_MAX_COUNT) { - if (!mod_requested) - request_module("snd-hda-codec-id:%08x", - codec->vendor_id); - else - request_module("snd-hda-codec-id:%04x*", - (codec->vendor_id >> 16) & 0xffff); - mod_requested++; - goto again; - } - return NULL; -} - -/* - * get_codec_name - store the codec name - */ -static int get_codec_name(struct hda_codec *codec) -{ - const struct hda_vendor_id *c; - const char *vendor = NULL; - u16 vendor_id = codec->vendor_id >> 16; - char tmp[16]; - - if (codec->vendor_name) - goto get_chip_name; - - for (c = hda_vendor_ids; c->id; c++) { - if (c->id == vendor_id) { - vendor = c->name; - break; - } - } - if (!vendor) { - sprintf(tmp, "Generic %04x", vendor_id); - vendor = tmp; - } - codec->vendor_name = kstrdup(vendor, GFP_KERNEL); - if (!codec->vendor_name) - return -ENOMEM; - - get_chip_name: - if (codec->chip_name) - return 0; - - if (codec->preset && codec->preset->name) - codec->chip_name = kstrdup(codec->preset->name, GFP_KERNEL); - else { - sprintf(tmp, "ID %x", codec->vendor_id & 0xffff); - codec->chip_name = kstrdup(tmp, GFP_KERNEL); - } - if (!codec->chip_name) - return -ENOMEM; - return 0; -} - -/* - * look for an AFG and MFG nodes - */ -static void setup_fg_nodes(struct hda_codec *codec) -{ - int i, total_nodes, function_id; - hda_nid_t nid; - - total_nodes = snd_hda_get_sub_nodes(codec, AC_NODE_ROOT, &nid); - for (i = 0; i < total_nodes; i++, nid++) { - function_id = snd_hda_param_read(codec, nid, - AC_PAR_FUNCTION_TYPE); - switch (function_id & 0xff) { - case AC_GRP_AUDIO_FUNCTION: - codec->afg = nid; - codec->afg_function_id = function_id & 0xff; - codec->afg_unsol = (function_id >> 8) & 1; - break; - case AC_GRP_MODEM_FUNCTION: - codec->mfg = nid; - codec->mfg_function_id = function_id & 0xff; - codec->mfg_unsol = (function_id >> 8) & 1; - break; - default: - break; - } - } -} - /* * read widget caps for each widget and store in cache */ @@ -1065,25 +581,22 @@ static int read_widget_caps(struct hda_codec *codec, hda_nid_t fg_node) int i; hda_nid_t nid; - codec->num_nodes = snd_hda_get_sub_nodes(codec, fg_node, - &codec->start_nid); - codec->wcaps = kmalloc(codec->num_nodes * 4, GFP_KERNEL); + codec->wcaps = kmalloc(codec->core.num_nodes * 4, GFP_KERNEL); if (!codec->wcaps) return -ENOMEM; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + nid = codec->core.start_nid; + for (i = 0; i < codec->core.num_nodes; i++, nid++) + codec->wcaps[i] = snd_hdac_read_parm_uncached(&codec->core, + nid, AC_PAR_AUDIO_WIDGET_CAP); 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; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { struct hda_pincfg *pin; unsigned int wcaps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wcaps); @@ -1290,14 +803,10 @@ static void hda_jackpoll_work(struct work_struct *work) if (!codec->jackpoll_interval) return; - queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, - codec->jackpoll_interval); + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } -static void init_hda_cache(struct hda_cache_rec *cache, - unsigned int record_size); -static void free_hda_cache(struct hda_cache_rec *cache); - /* release all pincfg lists */ static void free_init_pincfgs(struct hda_codec *codec) { @@ -1339,70 +848,117 @@ get_hda_cvt_setup(struct hda_codec *codec, hda_nid_t nid) } /* - * Dynamic symbol binding for the codec parsers + * PCM device */ +static void release_pcm(struct kref *kref) +{ + struct hda_pcm *pcm = container_of(kref, struct hda_pcm, kref); + + if (pcm->pcm) + snd_device_free(pcm->codec->card, pcm->pcm); + clear_bit(pcm->device, pcm->codec->bus->pcm_dev_bits); + kfree(pcm->name); + kfree(pcm); +} -#define load_parser(codec, sym) \ - ((codec)->parser = (int (*)(struct hda_codec *))symbol_request(sym)) +void snd_hda_codec_pcm_put(struct hda_pcm *pcm) +{ + kref_put(&pcm->kref, release_pcm); +} +EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_put); -static void unload_parser(struct hda_codec *codec) +struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, + const char *fmt, ...) { - if (codec->parser) - symbol_put_addr(codec->parser); - codec->parser = NULL; + struct hda_pcm *pcm; + va_list args; + + va_start(args, fmt); + pcm = kzalloc(sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return NULL; + + pcm->codec = codec; + kref_init(&pcm->kref); + pcm->name = kvasprintf(GFP_KERNEL, fmt, args); + if (!pcm->name) { + kfree(pcm); + return NULL; + } + + list_add_tail(&pcm->list, &codec->pcm_list_head); + return pcm; } +EXPORT_SYMBOL_GPL(snd_hda_codec_pcm_new); /* * codec destructor */ -static void snd_hda_codec_free(struct hda_codec *codec) +static void codec_release_pcms(struct hda_codec *codec) { - if (!codec) - return; + struct hda_pcm *pcm, *n; + + list_for_each_entry_safe(pcm, n, &codec->pcm_list_head, list) { + list_del_init(&pcm->list); + if (pcm->pcm) + snd_device_disconnect(codec->card, pcm->pcm); + snd_hda_codec_pcm_put(pcm); + } +} + +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec) +{ + if (codec->registered) { + /* pm_runtime_put() is called in snd_hdac_device_exit() */ + pm_runtime_get_noresume(hda_codec_dev(codec)); + pm_runtime_disable(hda_codec_dev(codec)); + codec->registered = 0; + } + cancel_delayed_work_sync(&codec->jackpoll_work); + if (!codec->in_freeing) + snd_hda_ctls_clear(codec); + codec_release_pcms(codec); + snd_hda_detach_beep_device(codec); + memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); snd_hda_jack_tbl_clear(codec); - free_init_pincfgs(codec); -#ifdef CONFIG_PM - cancel_delayed_work(&codec->power_work); - flush_workqueue(codec->bus->workq); -#endif - list_del(&codec->list); - snd_array_free(&codec->mixers); - snd_array_free(&codec->nids); + codec->proc_widget_hook = NULL; + codec->spec = NULL; + + /* free only driver_pins so that init_pins + user_pins are restored */ + snd_array_free(&codec->driver_pins); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); + snd_array_free(&codec->verbs); + codec->preset = NULL; + codec->slave_dig_outs = NULL; + codec->spdif_status_reset = 0; + snd_array_free(&codec->mixers); + snd_array_free(&codec->nids); remove_conn_list(codec); - codec->bus->caddr_tbl[codec->addr] = NULL; - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - hda_call_pm_notify(codec, false); /* cancel leftover refcounts */ - snd_hda_sysfs_clear(codec); - unload_parser(codec); - module_put(codec->owner); - free_hda_cache(&codec->amp_cache); - free_hda_cache(&codec->cmd_cache); - kfree(codec->vendor_name); - kfree(codec->chip_name); - kfree(codec->modelname); - kfree(codec->wcaps); - codec->bus->num_codecs--; - put_device(&codec->dev); + snd_hdac_regmap_exit(&codec->core); } -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, - hda_nid_t fg, unsigned int power_state); - static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state); -static int snd_hda_codec_dev_register(struct snd_device *device) +/* also called from hda_bind.c */ +void snd_hda_codec_register(struct hda_codec *codec) { - struct hda_codec *codec = device->device_data; - int err = device_add(&codec->dev); + if (codec->registered) + return; + if (device_is_registered(hda_codec_dev(codec))) { + snd_hda_register_beep_device(codec); + pm_runtime_enable(hda_codec_dev(codec)); + /* it was powered up in snd_hda_codec_new(), now all done */ + snd_hda_power_down(codec); + codec->registered = 1; + } +} - if (err < 0) - return err; - snd_hda_register_beep_device(codec); +static int snd_hda_codec_dev_register(struct snd_device *device) +{ + snd_hda_codec_register(device->device_data); return 0; } @@ -1411,20 +967,29 @@ static int snd_hda_codec_dev_disconnect(struct snd_device *device) struct hda_codec *codec = device->device_data; snd_hda_detach_beep_device(codec); - device_del(&codec->dev); return 0; } static int snd_hda_codec_dev_free(struct snd_device *device) { - snd_hda_codec_free(device->device_data); + struct hda_codec *codec = device->device_data; + + codec->in_freeing = 1; + snd_hdac_device_unregister(&codec->core); + put_device(hda_codec_dev(codec)); return 0; } -/* just free the container */ static void snd_hda_codec_dev_release(struct device *dev) { - kfree(container_of(dev, struct hda_codec, dev)); + struct hda_codec *codec = dev_to_hda_codec(dev); + + free_init_pincfgs(codec); + snd_hdac_device_exit(&codec->core); + snd_hda_sysfs_clear(codec); + kfree(codec->modelname); + kfree(codec->wcaps); + kfree(codec); } /** @@ -1435,9 +1000,8 @@ static void snd_hda_codec_dev_release(struct device *dev) * * Returns 0 if successful, or a negative error code. */ -int snd_hda_codec_new(struct hda_bus *bus, - unsigned int codec_addr, - struct hda_codec **codecp) +int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp) { struct hda_codec *codec; char component[31]; @@ -1454,35 +1018,27 @@ int snd_hda_codec_new(struct hda_bus *bus, if (snd_BUG_ON(codec_addr > HDA_MAX_CODEC_ADDRESS)) return -EINVAL; - if (bus->caddr_tbl[codec_addr]) { - dev_err(bus->card->dev, - "address 0x%x is already occupied\n", - codec_addr); - return -EBUSY; - } - codec = kzalloc(sizeof(*codec), GFP_KERNEL); - if (codec == NULL) { - dev_err(bus->card->dev, "can't allocate struct hda_codec\n"); + if (!codec) return -ENOMEM; + + sprintf(component, "hdaudioC%dD%d", card->number, codec_addr); + err = snd_hdac_device_init(&codec->core, &bus->core, component, + codec_addr); + if (err < 0) { + kfree(codec); + return err; } - device_initialize(&codec->dev); - codec->dev.parent = &bus->card->card_dev; - codec->dev.class = sound_class; - codec->dev.release = snd_hda_codec_dev_release; - codec->dev.groups = snd_hda_dev_attr_groups; - dev_set_name(&codec->dev, "hdaudioC%dD%d", bus->card->number, - codec_addr); - dev_set_drvdata(&codec->dev, codec); /* for sysfs */ + codec->core.dev.release = snd_hda_codec_dev_release; + codec->core.type = HDA_DEV_LEGACY; + codec->core.exec_verb = codec_exec_verb; codec->bus = bus; + codec->card = card; codec->addr = codec_addr; mutex_init(&codec->spdif_mutex); mutex_init(&codec->control_mutex); - mutex_init(&codec->hash_mutex); - 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 hda_nid_item), 32); snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32); snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); @@ -1492,19 +1048,14 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_LIST_HEAD(&codec->conn_list); + INIT_LIST_HEAD(&codec->pcm_list_head); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work); codec->depop_delay = -1; codec->fixup_id = HDA_FIXUP_ID_NOT_SET; #ifdef CONFIG_PM - spin_lock_init(&codec->power_lock); - INIT_DELAYED_WORK(&codec->power_work, hda_power_work); - /* snd_hda_codec_new() marks the codec as power-up, and leave it as is. - * the caller has to power down appropriatley after initialization - * phase. - */ - hda_keep_power_on(codec); + codec->power_jiffies = jiffies; #endif snd_hda_sysfs_init(codec); @@ -1517,59 +1068,14 @@ int snd_hda_codec_new(struct hda_bus *bus, } } - list_add_tail(&codec->list, &bus->codec_list); - bus->num_codecs++; - - bus->caddr_tbl[codec_addr] = codec; - - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - if (codec->vendor_id == -1) - /* read again, hopefully the access method was corrected - * in the last read... - */ - codec->vendor_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_VENDOR_ID); - codec->subsystem_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_SUBSYSTEM_ID); - codec->revision_id = snd_hda_param_read(codec, AC_NODE_ROOT, - AC_PAR_REV_ID); - - setup_fg_nodes(codec); - if (!codec->afg && !codec->mfg) { - dev_err(bus->card->dev, "no AFG or MFG node found\n"); - err = -ENODEV; - goto error; - } - - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - dev_err(bus->card->dev, "cannot malloc\n"); + if (err < 0) goto error; - } err = read_pin_defaults(codec); if (err < 0) goto error; - if (!codec->subsystem_id) { - codec->subsystem_id = - snd_hda_codec_read(codec, fg, 0, - AC_VERB_GET_SUBSYSTEM_ID, 0); - } - -#ifdef CONFIG_PM - codec->d3_stop_clk = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_CLKSTOP); -#endif - codec->epss = snd_hda_codec_get_supported_ps(codec, fg, - AC_PWRST_EPSS); -#ifdef CONFIG_PM - if (!codec->d3_stop_clk || !codec->epss) - bus->power_keep_link_on = 1; -#endif - - /* power-up all before initialization */ hda_set_power_state(codec, AC_PWRST_D0); @@ -1577,11 +1083,11 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_hda_create_hwdep(codec); - sprintf(component, "HDA:%08x,%08x,%08x", codec->vendor_id, - codec->subsystem_id, codec->revision_id); - snd_component_add(codec->bus->card, component); + sprintf(component, "HDA:%08x,%08x,%08x", codec->core.vendor_id, + codec->core.subsystem_id, codec->core.revision_id); + snd_component_add(card, component); - err = snd_device_new(bus->card, SNDRV_DEV_CODEC, codec, &dev_ops); + err = snd_device_new(card, SNDRV_DEV_CODEC, codec, &dev_ops); if (err < 0) goto error; @@ -1590,7 +1096,7 @@ int snd_hda_codec_new(struct hda_bus *bus, return 0; error: - snd_hda_codec_free(codec); + put_device(hda_codec_dev(codec)); return err; } EXPORT_SYMBOL_GPL(snd_hda_codec_new); @@ -1607,16 +1113,18 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) hda_nid_t fg; int err; + err = snd_hdac_refresh_widgets(&codec->core); + if (err < 0) + return err; + /* Assume the function group node does not change, * only the widget nodes may change. */ kfree(codec->wcaps); - fg = codec->afg ? codec->afg : codec->mfg; + fg = codec->core.afg ? codec->core.afg : codec->core.mfg; err = read_widget_caps(codec, fg); - if (err < 0) { - codec_err(codec, "cannot malloc\n"); + if (err < 0) return err; - } snd_array_free(&codec->init_pins); err = read_pin_defaults(codec); @@ -1625,98 +1133,6 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec) } EXPORT_SYMBOL_GPL(snd_hda_codec_update_widgets); - -#if IS_ENABLED(CONFIG_SND_HDA_CODEC_HDMI) -/* if all audio out widgets are digital, let's assume the codec as a HDMI/DP */ -static bool is_likely_hdmi_codec(struct hda_codec *codec) -{ - hda_nid_t nid = codec->start_nid; - int i; - - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int wcaps = get_wcaps(codec, nid); - switch (get_wcaps_type(wcaps)) { - case AC_WID_AUD_IN: - return false; /* HDMI parser supports only HDMI out */ - case AC_WID_AUD_OUT: - if (!(wcaps & AC_WCAP_DIGITAL)) - return false; - break; - } - } - return true; -} -#else -/* no HDMI codec parser support */ -#define is_likely_hdmi_codec(codec) false -#endif /* CONFIG_SND_HDA_CODEC_HDMI */ - -/** - * snd_hda_codec_configure - (Re-)configure the HD-audio codec - * @codec: the HDA codec - * - * Start parsing of the given codec tree and (re-)initialize the whole - * patch instance. - * - * Returns 0 if successful or a negative error code. - */ -int snd_hda_codec_configure(struct hda_codec *codec) -{ - int (*patch)(struct hda_codec *) = NULL; - int err; - - codec->preset = find_codec_preset(codec); - if (!codec->vendor_name || !codec->chip_name) { - err = get_codec_name(codec); - if (err < 0) - return err; - } - - if (!is_generic_config(codec) && codec->preset) - patch = codec->preset->patch; - if (!patch) { - unload_parser(codec); /* to be sure */ - if (is_likely_hdmi_codec(codec)) { -#if IS_MODULE(CONFIG_SND_HDA_CODEC_HDMI) - patch = load_parser(codec, snd_hda_parse_hdmi_codec); -#elif IS_BUILTIN(CONFIG_SND_HDA_CODEC_HDMI) - patch = snd_hda_parse_hdmi_codec; -#endif - } - if (!patch) { -#if IS_MODULE(CONFIG_SND_HDA_GENERIC) - patch = load_parser(codec, snd_hda_parse_generic_codec); -#elif IS_BUILTIN(CONFIG_SND_HDA_GENERIC) - patch = snd_hda_parse_generic_codec; -#endif - } - if (!patch) { - codec_err(codec, "No codec parser is available\n"); - return -ENODEV; - } - } - - err = patch(codec); - if (err < 0) { - unload_parser(codec); - return err; - } - - if (codec->patch_ops.unsol_event) { - err = init_unsol_queue(codec->bus); - if (err < 0) - return err; - } - - /* audio codec should override the mixer name */ - if (codec->afg || !*codec->bus->card->mixername) - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_configure); - /* update the stream-id if changed */ static void update_pcm_stream_id(struct hda_codec *codec, struct hda_cvt_setup *p, hda_nid_t nid, @@ -1782,6 +1198,8 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, if (!p) return; + if (codec->patch_ops.stream_pm) + codec->patch_ops.stream_pm(codec, nid, true); if (codec->pcm_format_first) update_pcm_format(codec, p, nid, format); update_pcm_stream_id(codec, p, nid, stream_tag, channel_id); @@ -1793,7 +1211,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, /* make other inactive cvts with the same stream-tag dirty */ type = get_wcaps_type(get_wcaps(codec, nid)); - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { p = snd_array_elem(&c->cvt_setups, i); if (!p->active && p->stream_tag == stream_tag && @@ -1850,6 +1268,8 @@ static void really_cleanup_stream(struct hda_codec *codec, ); memset(q, 0, sizeof(*q)); q->nid = nid; + if (codec->patch_ops.stream_pm) + codec->patch_ops.stream_pm(codec, nid, false); } /* clean up the all conflicting obsolete streams */ @@ -1858,7 +1278,7 @@ static void purify_inactive_streams(struct hda_codec *codec) struct hda_codec *c; int i; - list_for_each_entry(c, &codec->bus->codec_list, list) { + list_for_each_codec(c, codec->bus) { for (i = 0; i < c->cvt_setups.used; i++) { struct hda_cvt_setup *p; p = snd_array_elem(&c->cvt_setups, i); @@ -1886,127 +1306,6 @@ static void hda_cleanup_all_streams(struct hda_codec *codec) * amp access functions */ -/* 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 HDA_HASH_PARPCM_KEY(nid) (u32)((nid) + (0x03 << 24)) -#define HDA_HASH_PARSTR_KEY(nid) (u32)((nid) + (0x04 << 24)) -#define INFO_AMP_CAPS (1<<0) -#define INFO_AMP_VOL(ch) (1 << (1 + (ch))) - -/* initialize the hash table */ -static void init_hda_cache(struct hda_cache_rec *cache, - unsigned int record_size) -{ - memset(cache, 0, sizeof(*cache)); - memset(cache->hash, 0xff, sizeof(cache->hash)); - snd_array_init(&cache->buf, record_size, 64); -} - -static void free_hda_cache(struct hda_cache_rec *cache) -{ - snd_array_free(&cache->buf); -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_hash(struct hda_cache_rec *cache, u32 key) -{ - u16 idx = key % (u16)ARRAY_SIZE(cache->hash); - u16 cur = cache->hash[idx]; - struct hda_cache_head *info; - - while (cur != 0xffff) { - info = snd_array_elem(&cache->buf, cur); - if (info->key == key) - return info; - cur = info->next; - } - return NULL; -} - -/* query the hash. allocate an entry if not found. */ -static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, - u32 key) -{ - struct hda_cache_head *info = get_hash(cache, key); - if (!info) { - u16 idx, cur; - /* add a new hash entry */ - info = snd_array_new(&cache->buf); - if (!info) - return NULL; - cur = snd_array_index(&cache->buf, info); - info->key = key; - info->val = 0; - info->dirty = 0; - idx = key % (u16)ARRAY_SIZE(cache->hash); - info->next = cache->hash[idx]; - cache->hash[idx] = cur; - } - return info; -} - -/* query and allocate an amp hash entry */ -static inline struct hda_amp_info * -get_alloc_amp_hash(struct hda_codec *codec, u32 key) -{ - return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key); -} - -/* overwrite the value with the key in the caps hash */ -static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val) -{ - struct hda_amp_info *info; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return -EINVAL; - } - info->amp_caps = val; - info->head.val |= INFO_AMP_CAPS; - mutex_unlock(&codec->hash_mutex); - return 0; -} - -/* query the value from the caps hash; if not found, fetch the current - * value from the given function and store in the hash - */ -static unsigned int -query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key, - unsigned int (*func)(struct hda_codec *, hda_nid_t, int)) -{ - struct hda_amp_info *info; - unsigned int val; - - mutex_lock(&codec->hash_mutex); - info = get_alloc_amp_hash(codec, key); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - if (!(info->head.val & INFO_AMP_CAPS)) { - mutex_unlock(&codec->hash_mutex); /* for reentrance */ - val = func(codec, nid, dir); - write_caps_hash(codec, key, val); - } else { - val = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - } - return val; -} - -static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, - int direction) -{ - if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) - nid = codec->afg; - return snd_hda_param_read(codec, nid, - direction == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); -} - /** * query_amp_caps - query AMP capabilities * @codec: the HD-auio codec @@ -2021,9 +1320,11 @@ static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid, */ u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction) { - return query_caps_hash(codec, nid, direction, - HDA_HASH_KEY(nid, direction, 0), - read_amp_cap); + if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD)) + nid = codec->core.afg; + return snd_hda_param_read(codec, nid, + direction == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); } EXPORT_SYMBOL_GPL(query_amp_caps); @@ -2064,183 +1365,14 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_caps); int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int caps) { - return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); - -static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid, - int dir) -{ - return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); -} - -/** - * snd_hda_query_pin_caps - Query PIN capabilities - * @codec: the HD-auio codec - * @nid: the NID to query - * - * Query PIN capabilities for the given widget. - * Returns the obtained capability bits. - * - * When cap bits have been already read, this doesn't read again but - * returns the cached value. - */ -u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid), - read_pin_cap); -} -EXPORT_SYMBOL_GPL(snd_hda_query_pin_caps); - -/** - * snd_hda_override_pin_caps - Override the pin capabilities - * @codec: the CODEC - * @nid: the NID to override - * @caps: the capability bits to set - * - * Override the cached PIN capabilitiy bits value by the given one. - * - * Returns zero if successful or a negative error code. - */ -int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps) -{ - return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps); -} -EXPORT_SYMBOL_GPL(snd_hda_override_pin_caps); - -/* read or sync the hash value with the current value; - * call within hash_mutex - */ -static struct hda_amp_info * -update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index, bool init_only) -{ - struct hda_amp_info *info; - unsigned int parm, val = 0; - bool val_read = false; - - retry: - info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); - if (!info) - return NULL; - if (!(info->head.val & INFO_AMP_VOL(ch))) { - if (!val_read) { - mutex_unlock(&codec->hash_mutex); - parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT; - parm |= direction == HDA_OUTPUT ? - AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; - parm |= index; - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, parm); - val &= 0xff; - val_read = true; - mutex_lock(&codec->hash_mutex); - goto retry; - } - info->vol[ch] = val; - info->head.val |= INFO_AMP_VOL(ch); - } else if (init_only) - return NULL; - return info; -} - -/* - * write the current volume in info to the h/w - */ -static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, - hda_nid_t nid, int ch, int direction, int index, - int val) -{ - u32 parm; - - parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; - parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; - parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && - (amp_caps & AC_AMPCAP_MIN_MUTE)) - ; /* set the zero value as a fake mute */ - else - parm |= val; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm); -} - -/** - * snd_hda_codec_amp_read - Read AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @index: the index value (only for input direction) - * - * Read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. - */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) -{ - struct hda_amp_info *info; - unsigned int val = 0; - - mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index, false); - if (info) - val = info->vol[ch]; - mutex_unlock(&codec->hash_mutex); - return val; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_read); - -static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val, - bool init_only) -{ - struct hda_amp_info *info; - unsigned int caps; - unsigned int cache_only; - - if (snd_BUG_ON(mask & ~0xff)) - mask &= 0xff; - val &= mask; - - mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx, init_only); - if (!info) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - val |= info->vol[ch] & ~mask; - if (info->vol[ch] == val) { - mutex_unlock(&codec->hash_mutex); - return 0; - } - info->vol[ch] = val; - cache_only = info->head.dirty = codec->cached_write; - caps = info->amp_caps; - mutex_unlock(&codec->hash_mutex); - if (!cache_only) - put_vol_mute(codec, caps, nid, ch, direction, idx, val); - return 1; -} + unsigned int parm; -/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) -{ - return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); + snd_hda_override_wcaps(codec, nid, + get_wcaps(codec, nid) | AC_WCAP_AMP_OVRD); + parm = dir == HDA_OUTPUT ? AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP; + return snd_hdac_override_parm(&codec->core, nid, parm, caps); } -EXPORT_SYMBOL_GPL(snd_hda_codec_amp_update); +EXPORT_SYMBOL_GPL(snd_hda_override_amp_caps); /** * snd_hda_codec_amp_stereo - update the AMP stereo values @@ -2285,7 +1417,16 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_amp_stereo); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, int mask, int val) { - return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); + int orig; + + if (!codec->core.regmap) + return -EINVAL; + regcache_cache_only(codec->core.regmap, true); + orig = snd_hda_codec_amp_read(codec, nid, ch, dir, idx); + regcache_cache_only(codec->core.regmap, false); + if (orig >= 0) + return 0; + return snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val); } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init); @@ -2314,49 +1455,6 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_GPL(snd_hda_codec_amp_init_stereo); -/** - * snd_hda_codec_resume_amp - Resume all AMP commands from the cache - * @codec: HD-audio codec - * - * Resume the all amp commands from the cache. - */ -void snd_hda_codec_resume_amp(struct hda_codec *codec) -{ - int i; - - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *buffer; - u32 key; - hda_nid_t nid; - unsigned int idx, dir, ch; - struct hda_amp_info info; - - buffer = snd_array_elem(&codec->amp_cache.buf, i); - if (!buffer->head.dirty) - continue; - buffer->head.dirty = 0; - info = *buffer; - key = info.head.key; - if (!key) - continue; - nid = key & 0xff; - idx = (key >> 16) & 0xff; - dir = (key >> 24) & 0xff; - for (ch = 0; ch < 2; ch++) { - if (!(info.head.val & INFO_AMP_VOL(ch))) - continue; - mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, - info.vol[ch]); - mutex_lock(&codec->hash_mutex); - } - } - mutex_unlock(&codec->hash_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_resume_amp); - static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) { @@ -2478,14 +1576,12 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; int change = 0; - snd_hda_power_up(codec); if (chs & 1) { change = update_amp_value(codec, nid, 0, dir, idx, ofs, *valp); valp++; } if (chs & 2) change |= update_amp_value(codec, nid, 1, dir, idx, ofs, *valp); - snd_hda_power_down(codec); return change; } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_volume_put); @@ -2572,7 +1668,7 @@ find_mixer_ctl(struct hda_codec *codec, const char *name, int dev, int idx) if (snd_BUG_ON(strlen(name) >= sizeof(id.name))) return NULL; strcpy(id.name, name); - return snd_ctl_find_id(codec->bus->card, &id); + return snd_ctl_find_id(codec->card, &id); } /** @@ -2636,7 +1732,7 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid, nid = kctl->id.subdevice & 0xffff; if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG)) kctl->id.subdevice = 0; - err = snd_ctl_add(codec->bus->card, kctl); + err = snd_ctl_add(codec->card, kctl); if (err < 0) return err; item = snd_array_new(&codec->mixers); @@ -2689,7 +1785,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec) int i; struct hda_nid_item *items = codec->mixers.list; for (i = 0; i < codec->mixers.used; i++) - snd_ctl_remove(codec->bus->card, items[i].kctl); + snd_ctl_remove(codec->card, items[i].kctl); snd_array_free(&codec->mixers); snd_array_free(&codec->nids); } @@ -2712,10 +1808,9 @@ int snd_hda_lock_devices(struct hda_bus *bus) if (!list_empty(&card->ctl_files)) goto err_clear; - list_for_each_entry(codec, &bus->codec_list, list) { - int pcm; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + list_for_each_codec(codec, bus) { + struct hda_pcm *cpcm; + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { if (!cpcm->pcm) continue; if (cpcm->pcm->streams[0].substream_opened || @@ -2742,7 +1837,6 @@ void snd_hda_unlock_devices(struct hda_bus *bus) { struct snd_card *card = bus->card; - card = bus->card; spin_lock(&card->files_lock); card->shutdown = 0; spin_unlock(&card->files_lock); @@ -2762,51 +1856,12 @@ EXPORT_SYMBOL_GPL(snd_hda_unlock_devices); int snd_hda_codec_reset(struct hda_codec *codec) { struct hda_bus *bus = codec->bus; - struct snd_card *card = bus->card; - int i; if (snd_hda_lock_devices(bus) < 0) return -EBUSY; /* OK, let it free */ - cancel_delayed_work_sync(&codec->jackpoll_work); -#ifdef CONFIG_PM - cancel_delayed_work_sync(&codec->power_work); - flush_workqueue(bus->workq); -#endif - snd_hda_ctls_clear(codec); - /* release PCMs */ - for (i = 0; i < codec->num_pcms; i++) { - if (codec->pcm_info[i].pcm) { - snd_device_free(card, codec->pcm_info[i].pcm); - clear_bit(codec->pcm_info[i].device, - bus->pcm_dev_bits); - } - } - snd_hda_detach_beep_device(codec); - if (codec->patch_ops.free) - codec->patch_ops.free(codec); - memset(&codec->patch_ops, 0, sizeof(codec->patch_ops)); - snd_hda_jack_tbl_clear(codec); - codec->proc_widget_hook = NULL; - codec->spec = NULL; - free_hda_cache(&codec->amp_cache); - 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); - snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->spdif_out); - snd_array_free(&codec->verbs); - codec->num_pcms = 0; - codec->pcm_info = NULL; - codec->preset = NULL; - codec->slave_dig_outs = NULL; - codec->spdif_status_reset = 0; - unload_parser(codec); - module_put(codec->owner); - codec->owner = NULL; + snd_hdac_device_unregister(&codec->core); /* allow device access again */ snd_hda_unlock_devices(bus); @@ -3153,7 +2208,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *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, HDA_AMP_MUTE, @@ -3165,7 +2219,6 @@ int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, HDA_AMP_MUTE, *valp ? 0 : HDA_AMP_MUTE); hda_call_check_power_status(codec, nid); - snd_hda_power_down(codec); return change; } EXPORT_SYMBOL_GPL(snd_hda_mixer_amp_switch_put); @@ -3466,25 +2519,35 @@ static unsigned int convert_to_spdif_status(unsigned short val) /* set digital convert verbs both for the given NID and its slaves */ static void set_dig_out(struct hda_codec *codec, hda_nid_t nid, - int verb, int val) + int mask, int val) { const hda_nid_t *d; - snd_hda_codec_write_cache(codec, nid, 0, verb, val); + snd_hdac_regmap_update(&codec->core, nid, AC_VERB_SET_DIGI_CONVERT_1, + mask, val); d = codec->slave_dig_outs; if (!d) return; for (; *d; d++) - snd_hda_codec_write_cache(codec, *d, 0, verb, val); + snd_hdac_regmap_update(&codec->core, nid, + AC_VERB_SET_DIGI_CONVERT_1, mask, val); } static inline void set_dig_out_convert(struct hda_codec *codec, hda_nid_t nid, int dig1, int dig2) { - if (dig1 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_1, dig1); - if (dig2 != -1) - set_dig_out(codec, nid, AC_VERB_SET_DIGI_CONVERT_2, dig2); + unsigned int mask = 0; + unsigned int val = 0; + + if (dig1 != -1) { + mask |= 0xff; + val = dig1; + } + if (dig2 != -1) { + mask |= 0xff00; + val |= dig2 << 8; + } + set_dig_out(codec, nid, mask, val); } static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol, @@ -3617,6 +2680,7 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, struct snd_kcontrol *kctl; struct snd_kcontrol_new *dig_mix; int idx = 0; + int val = 0; const int spdif_index = 16; struct hda_spdif_out *spdif; struct hda_bus *bus = codec->bus; @@ -3657,8 +2721,9 @@ int snd_hda_create_dig_out_ctls(struct hda_codec *codec, return err; } spdif->nid = cvt_nid; - spdif->ctls = snd_hda_codec_read(codec, cvt_nid, 0, - AC_VERB_GET_DIGI_CONVERT_1, 0); + snd_hdac_regmap_read(&codec->core, cvt_nid, + AC_VERB_GET_DIGI_CONVERT_1, &val); + spdif->ctls = val; spdif->status = convert_to_spdif_status(spdif->ctls); return 0; } @@ -3802,8 +2867,8 @@ static int snd_hda_spdif_in_switch_put(struct snd_kcontrol *kcontrol, change = codec->spdif_in_enable != val; if (change) { codec->spdif_in_enable = val; - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_DIGI_CONVERT_1, val); + snd_hdac_regmap_write(&codec->core, nid, + AC_VERB_SET_DIGI_CONVERT_1, val); } mutex_unlock(&codec->spdif_mutex); return change; @@ -3814,10 +2879,11 @@ static int snd_hda_spdif_in_status_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); hda_nid_t nid = kcontrol->private_value; - unsigned short val; + unsigned int val; unsigned int sbits; - val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_DIGI_CONVERT_1, 0); + snd_hdac_regmap_read(&codec->core, nid, + AC_VERB_GET_DIGI_CONVERT_1, &val); sbits = convert_to_spdif_status(val); ucontrol->value.iec958.status[0] = sbits; ucontrol->value.iec958.status[1] = sbits >> 8; @@ -3883,153 +2949,6 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_GPL(snd_hda_create_spdif_in_ctls); -/* - * command cache - */ - -/* build a 31bit cache key with the widget id and the command parameter */ -#define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) -#define get_cmd_cache_nid(key) ((key) & 0xff) -#define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) - -/** - * snd_hda_codec_write_cache - send a single command with caching - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * Send a single command without waiting for response. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm) -{ - int err; - struct hda_cache_head *c; - u32 key; - unsigned int cache_only; - - cache_only = codec->cached_write; - if (!cache_only) { - err = snd_hda_codec_write(codec, nid, flags, verb, parm); - if (err < 0) - return err; - } - - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_alloc_hash(&codec->cmd_cache, key); - if (c) { - c->val = parm; - c->dirty = cache_only; - } - mutex_unlock(&codec->bus->cmd_mutex); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_codec_write_cache); - -/** - * snd_hda_codec_update_cache - check cache and write the cmd only when needed - * @codec: the HDA codec - * @nid: NID to send the command - * @flags: optional bit flags - * @verb: the verb to send - * @parm: the parameter for the verb - * - * This function works like snd_hda_codec_write_cache(), but it doesn't send - * command if the parameter is already identical with the cached value. - * If not, it sends the command and refreshes the cache. - * - * Returns 0 if successful, or a negative error code. - */ -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm) -{ - struct hda_cache_head *c; - u32 key; - - /* parm may contain the verb stuff for get/set amp */ - verb = verb | (parm >> 8); - parm &= 0xff; - key = build_cmd_cache_key(nid, verb); - mutex_lock(&codec->bus->cmd_mutex); - c = get_hash(&codec->cmd_cache, key); - if (c && c->val == parm) { - mutex_unlock(&codec->bus->cmd_mutex); - return 0; - } - mutex_unlock(&codec->bus->cmd_mutex); - return snd_hda_codec_write_cache(codec, nid, flags, verb, parm); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_update_cache); - -/** - * snd_hda_codec_resume_cache - Resume the all commands from the cache - * @codec: HD-audio codec - * - * Execute all verbs recorded in the command caches to resume. - */ -void snd_hda_codec_resume_cache(struct hda_codec *codec) -{ - int i; - - mutex_lock(&codec->hash_mutex); - codec->cached_write = 0; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *buffer; - u32 key; - - buffer = snd_array_elem(&codec->cmd_cache.buf, i); - key = buffer->key; - if (!key) - continue; - if (!buffer->dirty) - continue; - buffer->dirty = 0; - mutex_unlock(&codec->hash_mutex); - snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, - get_cmd_cache_cmd(key), buffer->val); - mutex_lock(&codec->hash_mutex); - } - mutex_unlock(&codec->hash_mutex); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_resume_cache); - -/** - * snd_hda_sequence_write_cache - sequence writes with caching - * @codec: the HDA codec - * @seq: VERB array to send - * - * Send the commands sequentially from the given array. - * Thte commands are recorded on cache for power-save and resume. - * The array must be terminated with NID=0. - */ -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq) -{ - for (; seq->nid; seq++) - snd_hda_codec_write_cache(codec, seq->nid, 0, seq->verb, - seq->param); -} -EXPORT_SYMBOL_GPL(snd_hda_sequence_write_cache); - -/** - * snd_hda_codec_flush_cache - Execute all pending (cached) amps / verbs - * @codec: HD-audio codec - */ -void snd_hda_codec_flush_cache(struct hda_codec *codec) -{ - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); - /** * snd_hda_codec_set_power_to_all - Set the power state to all widgets * @codec: the HDA codec @@ -4043,10 +2962,9 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_flush_cache); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int state = power_state; if (!(wcaps & AC_WCAP_POWER)) @@ -4063,22 +2981,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, EXPORT_SYMBOL_GPL(snd_hda_codec_set_power_to_all); /* - * supported power states check - */ -static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg, - unsigned int power_state) -{ - int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE); - - if (sup == -1) - return false; - if (sup & power_state) - return true; - else - return false; -} - -/* * wait until the state is reached, returns the current state */ static unsigned int hda_sync_power_state(struct hda_codec *codec, @@ -4117,7 +3019,7 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg || nid == codec->mfg) + if (nid == codec->core.afg || nid == codec->core.mfg) return power_state; if (power_state == AC_PWRST_D3 && get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN && @@ -4137,7 +3039,7 @@ EXPORT_SYMBOL_GPL(snd_hda_codec_eapd_power_filter); static unsigned int hda_set_power_state(struct hda_codec *codec, unsigned int power_state) { - hda_nid_t fg = codec->afg ? codec->afg : codec->mfg; + hda_nid_t fg = codec->core.afg ? codec->core.afg : codec->core.mfg; int count; unsigned int state; int flags = 0; @@ -4145,7 +3047,7 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, /* this delay seems necessary to avoid click noise at power-down */ if (power_state == AC_PWRST_D3) { if (codec->depop_delay < 0) - msleep(codec->epss ? 10 : 100); + msleep(codec_has_epss(codec) ? 10 : 100); else if (codec->depop_delay > 0) msleep(codec->depop_delay); flags = HDA_RW_NO_RESPONSE_FALLBACK; @@ -4179,14 +3081,13 @@ static unsigned int hda_set_power_state(struct hda_codec *codec, */ static void sync_power_up_states(struct hda_codec *codec) { - hda_nid_t nid = codec->start_nid; - int i; + hda_nid_t nid; /* don't care if no filter is used */ if (!codec->power_filter) return; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wcaps = get_wcaps(codec, nid); unsigned int target; if (!(wcaps & AC_WCAP_POWER)) @@ -4212,63 +3113,54 @@ static inline void hda_exec_init_verbs(struct hda_codec *codec) {} #endif #ifdef CONFIG_PM +/* update the power on/off account with the current jiffies */ +static void update_power_acct(struct hda_codec *codec, bool on) +{ + unsigned long delta = jiffies - codec->power_jiffies; + + if (on) + codec->power_on_acct += delta; + else + codec->power_off_acct += delta; + codec->power_jiffies += delta; +} + +void snd_hda_update_power_acct(struct hda_codec *codec) +{ + update_power_acct(codec, hda_codec_is_power_on(codec)); +} + /* * call suspend and power-down; used both from PM and power-save * this function returns the power state in the end */ -static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) +static unsigned int hda_call_codec_suspend(struct hda_codec *codec) { unsigned int state; - codec->in_pm = 1; + atomic_inc(&codec->core.in_pm); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); - /* Cancel delayed work if we aren't currently running from it. */ - if (!in_wq) - cancel_delayed_work_sync(&codec->power_work); - spin_lock(&codec->power_lock); - snd_hda_update_power_acct(codec); - trace_hda_power_down(codec); - codec->power_on = 0; - codec->power_transition = 0; - codec->power_jiffies = jiffies; - spin_unlock(&codec->power_lock); - codec->in_pm = 0; + update_power_acct(codec, true); + atomic_dec(&codec->core.in_pm); return state; } -/* mark all entries of cmd and amp caches dirty */ -static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) -{ - int i; - for (i = 0; i < codec->cmd_cache.buf.used; i++) { - struct hda_cache_head *cmd; - cmd = snd_array_elem(&codec->cmd_cache.buf, i); - cmd->dirty = 1; - } - for (i = 0; i < codec->amp_cache.buf.used; i++) { - struct hda_amp_info *amp; - amp = snd_array_elem(&codec->amp_cache.buf, i); - amp->head.dirty = 1; - } -} - /* * kick up codec; used both from PM and power-save */ static void hda_call_codec_resume(struct hda_codec *codec) { - codec->in_pm = 1; + atomic_inc(&codec->core.in_pm); - hda_mark_cmd_cache_dirty(codec); + if (codec->core.regmap) + regcache_mark_dirty(codec->core.regmap); + + codec->power_jiffies = jiffies; - /* set as if powered on for avoiding re-entering the resume - * in the resume / power-save sequence - */ - hda_keep_power_on(codec); hda_set_power_state(codec, AC_PWRST_D0); restore_shutup_pins(codec); hda_exec_init_verbs(codec); @@ -4278,72 +3170,71 @@ static void hda_call_codec_resume(struct hda_codec *codec) else { if (codec->patch_ops.init) codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + if (codec->core.regmap) + regcache_sync(codec->core.regmap); } if (codec->jackpoll_interval) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); - - codec->in_pm = 0; - snd_hda_power_down(codec); /* flag down before returning */ + atomic_dec(&codec->core.in_pm); } -#endif /* CONFIG_PM */ +static int hda_codec_runtime_suspend(struct device *dev) +{ + struct hda_codec *codec = dev_to_hda_codec(dev); + struct hda_pcm *pcm; + unsigned int state; -/** - * snd_hda_build_controls - build mixer controls - * @bus: the BUS - * - * Creates mixer controls for each codec included in the bus. - * - * Returns 0 if successful, otherwise a negative error code. - */ -int snd_hda_build_controls(struct hda_bus *bus) + cancel_delayed_work_sync(&codec->jackpoll_work); + list_for_each_entry(pcm, &codec->pcm_list_head, list) + snd_pcm_suspend_all(pcm->pcm); + state = hda_call_codec_suspend(codec); + if (codec_has_clkstop(codec) && codec_has_epss(codec) && + (state & AC_PWRST_CLK_STOP_OK)) + snd_hdac_codec_link_down(&codec->core); + return 0; +} + +static int hda_codec_runtime_resume(struct device *dev) { - struct hda_codec *codec; + struct hda_codec *codec = dev_to_hda_codec(dev); - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_controls(codec); - if (err < 0) { - codec_err(codec, - "cannot build controls for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return err; - } - } - } + snd_hdac_codec_link_up(&codec->core); + hda_call_codec_resume(codec); + pm_runtime_mark_last_busy(dev); return 0; } -EXPORT_SYMBOL_GPL(snd_hda_build_controls); +#endif /* CONFIG_PM */ + +/* referred in hda_bind.c */ +const struct dev_pm_ops hda_codec_driver_pm = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, + NULL) +}; /* * add standard channel maps if not specified */ static int add_std_chmaps(struct hda_codec *codec) { - int i, str, err; + struct hda_pcm *pcm; + int str, err; - for (i = 0; i < codec->num_pcms; i++) { + list_for_each_entry(pcm, &codec->pcm_list_head, list) { for (str = 0; str < 2; str++) { - struct snd_pcm *pcm = codec->pcm_info[i].pcm; - struct hda_pcm_stream *hinfo = - &codec->pcm_info[i].stream[str]; + struct hda_pcm_stream *hinfo = &pcm->stream[str]; struct snd_pcm_chmap *chmap; const struct snd_pcm_chmap_elem *elem; - if (codec->pcm_info[i].own_chmap) - continue; - if (!pcm || !hinfo->substreams) + if (!pcm || pcm->own_chmap || + !hinfo->substreams) continue; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; - err = snd_pcm_add_chmap_ctls(pcm, str, elem, + err = snd_pcm_add_chmap_ctls(pcm->pcm, str, elem, hinfo->channels_max, 0, &chmap); if (err < 0) @@ -4499,43 +3390,29 @@ unsigned int snd_hda_calc_stream_format(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_calc_stream_format); -static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int val = 0; - if (nid != codec->afg && + if (nid != codec->core.afg && (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) val = snd_hda_param_read(codec, nid, AC_PAR_PCM); if (!val || val == -1) - val = snd_hda_param_read(codec, codec->afg, AC_PAR_PCM); + val = snd_hda_param_read(codec, codec->core.afg, AC_PAR_PCM); if (!val || val == -1) return 0; return val; } -static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid), - get_pcm_param); -} - -static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid, - int dir) +static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) { unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM); if (!streams || streams == -1) - streams = snd_hda_param_read(codec, codec->afg, AC_PAR_STREAM); + streams = snd_hda_param_read(codec, codec->core.afg, AC_PAR_STREAM); if (!streams || streams == -1) return 0; return streams; } -static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid) -{ - return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid), - get_stream_param); -} - /** * snd_hda_query_supported_pcm - query the supported PCM rates and formats * @codec: the HDA codec @@ -4792,7 +3669,11 @@ int snd_hda_codec_prepare(struct hda_codec *codec, { int ret; mutex_lock(&codec->bus->prepare_mutex); - ret = hinfo->ops.prepare(hinfo, codec, stream, format, substream); + if (hinfo->ops.prepare) + ret = hinfo->ops.prepare(hinfo, codec, stream, format, + substream); + else + ret = -ENODEV; if (ret >= 0) purify_inactive_streams(codec); mutex_unlock(&codec->bus->prepare_mutex); @@ -4813,7 +3694,8 @@ void snd_hda_codec_cleanup(struct hda_codec *codec, struct snd_pcm_substream *substream) { mutex_lock(&codec->bus->prepare_mutex); - hinfo->ops.cleanup(hinfo, codec, substream); + if (hinfo->ops.cleanup) + hinfo->ops.cleanup(hinfo, codec, substream); mutex_unlock(&codec->bus->prepare_mutex); } EXPORT_SYMBOL_GPL(snd_hda_codec_cleanup); @@ -4871,112 +3753,84 @@ static int get_empty_pcm_device(struct hda_bus *bus, unsigned int type) return -EAGAIN; } -/* - * attach a new PCM stream - */ -static int snd_hda_attach_pcm(struct hda_codec *codec, struct hda_pcm *pcm) +/* call build_pcms ops of the given codec and set up the default parameters */ +int snd_hda_codec_parse_pcms(struct hda_codec *codec) { - struct hda_bus *bus = codec->bus; - struct hda_pcm_stream *info; - int stream, err; + struct hda_pcm *cpcm; + int err; - if (snd_BUG_ON(!pcm->name)) - return -EINVAL; - for (stream = 0; stream < 2; stream++) { - info = &pcm->stream[stream]; - if (info->substreams) { + if (!list_empty(&codec->pcm_list_head)) + return 0; /* already parsed */ + + if (!codec->patch_ops.build_pcms) + return 0; + + err = codec->patch_ops.build_pcms(codec); + if (err < 0) { + codec_err(codec, "cannot build PCMs for #%d (error %d)\n", + codec->core.addr, err); + return err; + } + + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { + int stream; + + for (stream = 0; stream < 2; stream++) { + struct hda_pcm_stream *info = &cpcm->stream[stream]; + + if (!info->substreams) + continue; err = set_pcm_default_values(codec, info); - if (err < 0) + if (err < 0) { + codec_warn(codec, + "fail to setup default for PCM %s\n", + cpcm->name); return err; + } } } - return bus->ops.attach_pcm(bus, codec, pcm); + + return 0; } /* assign all PCMs of the given codec */ int snd_hda_codec_build_pcms(struct hda_codec *codec) { - unsigned int pcm; - int err; + struct hda_bus *bus = codec->bus; + struct hda_pcm *cpcm; + int dev, err; - if (!codec->num_pcms) { - if (!codec->patch_ops.build_pcms) - return 0; - err = codec->patch_ops.build_pcms(codec); - if (err < 0) { - codec_err(codec, - "cannot build PCMs for #%d (error %d)\n", - codec->addr, err); - err = snd_hda_codec_reset(codec); - if (err < 0) { - codec_err(codec, - "cannot revert codec\n"); - return err; - } - } + if (snd_BUG_ON(!bus->ops.attach_pcm)) + return -EINVAL; + + err = snd_hda_codec_parse_pcms(codec); + if (err < 0) { + snd_hda_codec_reset(codec); + return err; } - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - struct hda_pcm *cpcm = &codec->pcm_info[pcm]; - int dev; + /* attach a new PCM streams */ + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { + if (cpcm->pcm) + continue; /* already attached */ if (!cpcm->stream[0].substreams && !cpcm->stream[1].substreams) continue; /* no substreams assigned */ - if (!cpcm->pcm) { - dev = get_empty_pcm_device(codec->bus, cpcm->pcm_type); - if (dev < 0) - continue; /* no fatal error */ - cpcm->device = dev; - err = snd_hda_attach_pcm(codec, cpcm); - if (err < 0) { - codec_err(codec, - "cannot attach PCM stream %d for codec #%d\n", - dev, codec->addr); - continue; /* no fatal error */ - } + dev = get_empty_pcm_device(bus, cpcm->pcm_type); + if (dev < 0) + continue; /* no fatal error */ + cpcm->device = dev; + err = bus->ops.attach_pcm(bus, codec, cpcm); + if (err < 0) { + codec_err(codec, + "cannot attach PCM stream %d for codec #%d\n", + dev, codec->core.addr); + continue; /* no fatal error */ } } - return 0; -} -/** - * snd_hda_build_pcms - build PCM information - * @bus: the BUS - * - * Create PCM information for each codec included in the bus. - * - * The build_pcms codec patch is requested to set up codec->num_pcms and - * codec->pcm_info properly. The array is referred by the top-level driver - * to create its PCM instances. - * The allocated codec->pcm_info should be released in codec->patch_ops.free - * callback. - * - * At least, substreams, channels_min and channels_max must be filled for - * each stream. substreams = 0 indicates that the stream doesn't exist. - * When rates and/or formats are zero, the supported values are queried - * from the given nid. The nid is used also by the default ops.prepare - * and ops.cleanup callbacks. - * - * The driver needs to call ops.open in its open callback. Similarly, - * ops.close is supposed to be called in the close callback. - * ops.prepare should be called in the prepare or hw_params callback - * with the proper parameters for set up. - * ops.cleanup should be called in hw_free for clean up of streams. - * - * This function returns 0 if successful, or a negative error code. - */ -int snd_hda_build_pcms(struct hda_bus *bus) -{ - struct hda_codec *codec; - - list_for_each_entry(codec, &bus->codec_list, list) { - int err = snd_hda_codec_build_pcms(codec); - if (err < 0) - return err; - } return 0; } -EXPORT_SYMBOL_GPL(snd_hda_build_pcms); /** * snd_hda_add_new_ctls - create controls from the array @@ -5013,8 +3867,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, * the codec addr; if it still fails (or it's the * primary codec), then try another control index */ - if (!addr && codec->addr) - addr = codec->addr; + if (!addr && codec->core.addr) + addr = codec->core.addr; else if (!idx && !knew->index) { idx = find_empty_mixer_ctl_idx(codec, knew->name, 0); @@ -5029,127 +3883,37 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, EXPORT_SYMBOL_GPL(snd_hda_add_new_ctls); #ifdef CONFIG_PM -static void hda_power_work(struct work_struct *work) -{ - struct hda_codec *codec = - container_of(work, struct hda_codec, power_work.work); - struct hda_bus *bus = codec->bus; - unsigned int state; - - spin_lock(&codec->power_lock); - if (codec->power_transition > 0) { /* during power-up sequence? */ - spin_unlock(&codec->power_lock); - return; - } - if (!codec->power_on || codec->power_count) { - codec->power_transition = 0; - spin_unlock(&codec->power_lock); - return; - } - spin_unlock(&codec->power_lock); - - state = hda_call_codec_suspend(codec, true); - if (!bus->power_keep_link_on && (state & AC_PWRST_CLK_STOP_OK)) - hda_call_pm_notify(codec, false); -} - -static void hda_keep_power_on(struct hda_codec *codec) -{ - spin_lock(&codec->power_lock); - codec->power_count++; - codec->power_on = 1; - codec->power_jiffies = jiffies; - spin_unlock(&codec->power_lock); - hda_call_pm_notify(codec, true); -} - -/* update the power on/off account with the current jiffies */ -void snd_hda_update_power_acct(struct hda_codec *codec) -{ - unsigned long delta = jiffies - codec->power_jiffies; - if (codec->power_on) - codec->power_on_acct += delta; - else - codec->power_off_acct += delta; - codec->power_jiffies += delta; -} - -/* Transition to powered up, if wait_power_down then wait for a pending - * transition to D3 to complete. A pending D3 transition is indicated - * with power_transition == -1. */ -/* call this with codec->power_lock held! */ -static void __snd_hda_power_up(struct hda_codec *codec, bool wait_power_down) -{ - /* Return if power_on or transitioning to power_on, unless currently - * powering down. */ - if ((codec->power_on || codec->power_transition > 0) && - !(wait_power_down && codec->power_transition < 0)) - return; - spin_unlock(&codec->power_lock); - - cancel_delayed_work_sync(&codec->power_work); - - spin_lock(&codec->power_lock); - /* If the power down delayed work was cancelled above before starting, - * then there is no need to go through power up here. - */ - if (codec->power_on) { - if (codec->power_transition < 0) - codec->power_transition = 0; - return; - } - - trace_hda_power_up(codec); - snd_hda_update_power_acct(codec); - codec->power_on = 1; - codec->power_jiffies = jiffies; - codec->power_transition = 1; /* avoid reentrance */ - spin_unlock(&codec->power_lock); - - hda_call_codec_resume(codec); - - spin_lock(&codec->power_lock); - codec->power_transition = 0; -} - -#define power_save(codec) \ - ((codec)->bus->power_save ? *(codec)->bus->power_save : 0) - -/* Transition to powered down */ -static void __snd_hda_power_down(struct hda_codec *codec) +static void codec_set_power_save(struct hda_codec *codec, int delay) { - if (!codec->power_on || codec->power_count || codec->power_transition) - return; + struct device *dev = hda_codec_dev(codec); - if (power_save(codec)) { - codec->power_transition = -1; /* avoid reentrance */ - queue_delayed_work(codec->bus->workq, &codec->power_work, - msecs_to_jiffies(power_save(codec) * 1000)); + if (delay > 0) { + pm_runtime_set_autosuspend_delay(dev, delay); + pm_runtime_use_autosuspend(dev); + pm_runtime_allow(dev); + if (!pm_runtime_suspended(dev)) + pm_runtime_mark_last_busy(dev); + } else { + pm_runtime_dont_use_autosuspend(dev); + pm_runtime_forbid(dev); } } /** - * snd_hda_power_save - Power-up/down/sync the codec - * @codec: HD-audio codec - * @delta: the counter delta to change - * @d3wait: sync for D3 transition complete + * snd_hda_set_power_save - reprogram autosuspend for the given delay + * @bus: HD-audio bus + * @delay: autosuspend delay in msec, 0 = off * - * Change the power-up counter via @delta, and power up or down the hardware - * appropriately. For the power-down, queue to the delayed action. - * Passing zero to @delta means to synchronize the power state. + * Synchronize the runtime PM autosuspend state from the power_save option. */ -void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait) +void snd_hda_set_power_save(struct hda_bus *bus, int delay) { - spin_lock(&codec->power_lock); - codec->power_count += delta; - trace_hda_power_count(codec); - if (delta > 0) - __snd_hda_power_up(codec, d3wait); - else - __snd_hda_power_down(codec); - spin_unlock(&codec->power_lock); + struct hda_codec *c; + + list_for_each_codec(c, bus) + codec_set_power_save(c, delay); } -EXPORT_SYMBOL_GPL(snd_hda_power_save); +EXPORT_SYMBOL_GPL(snd_hda_set_power_save); /** * snd_hda_check_amp_list_power - Check the amp list and update the power @@ -5187,7 +3951,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, if (!(v & HDA_AMP_MUTE) && v > 0) { if (!check->power_on) { check->power_on = 1; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); } return 1; } @@ -5195,7 +3959,7 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, } if (check->power_on) { check->power_on = 0; - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); } return 0; } @@ -5203,88 +3967,6 @@ EXPORT_SYMBOL_GPL(snd_hda_check_amp_list_power); #endif /* - * Channel mode helper - */ - -/** - * snd_hda_ch_mode_info - Info callback helper for the channel mode enum - * @codec: the HDA codec - * @uinfo: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - */ -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = num_chmodes; - if (uinfo->value.enumerated.item >= num_chmodes) - uinfo->value.enumerated.item = num_chmodes - 1; - sprintf(uinfo->value.enumerated.name, "%dch", - chmode[uinfo->value.enumerated.item].channels); - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_info); - -/** - * snd_hda_ch_mode_get - Get callback helper for the channel mode enum - * @codec: the HDA codec - * @ucontrol: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - * @max_channels: max number of channels - */ -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels) -{ - int i; - - for (i = 0; i < num_chmodes; i++) { - if (max_channels == chmode[i].channels) { - ucontrol->value.enumerated.item[0] = i; - break; - } - } - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_get); - -/** - * snd_hda_ch_mode_put - Put callback helper for the channel mode enum - * @codec: the HDA codec - * @ucontrol: pointer to get/store the data - * @chmode: channel mode array - * @num_chmodes: channel mode array size - * @max_channelsp: pointer to store the max channels - */ -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp) -{ - unsigned int mode; - - mode = ucontrol->value.enumerated.item[0]; - if (mode >= num_chmodes) - return -EINVAL; - if (*max_channelsp == chmode[mode].channels) - return 0; - /* change the current channel setting */ - *max_channelsp = chmode[mode].channels; - if (chmode[mode].sequence) - snd_hda_sequence_write_cache(codec, chmode[mode].sequence); - return 1; -} -EXPORT_SYMBOL_GPL(snd_hda_ch_mode_put); - -/* * input MUX helper */ @@ -5418,24 +4100,6 @@ static void cleanup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid) } /** - * snd_hda_bus_reboot_notify - call the reboot notifier of each codec - * @bus: HD-audio bus - */ -void snd_hda_bus_reboot_notify(struct hda_bus *bus) -{ - struct hda_codec *codec; - - if (!bus) - return; - list_for_each_entry(codec, &bus->codec_list, list) { - if (hda_codec_is_power_on(codec) && - codec->patch_ops.reboot_notify) - codec->patch_ops.reboot_notify(codec); - } -} -EXPORT_SYMBOL_GPL(snd_hda_bus_reboot_notify); - -/** * snd_hda_multi_out_dig_open - open the digital out in the exclusive mode * @codec: the HDA codec * @mout: hda_multi_out object @@ -5825,123 +4489,26 @@ int snd_hda_add_imux_item(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_add_imux_item); - -#ifdef CONFIG_PM -/* - * power management - */ - - -static void hda_async_suspend(void *data, async_cookie_t cookie) -{ - hda_call_codec_suspend(data, false); -} - -static void hda_async_resume(void *data, async_cookie_t cookie) -{ - hda_call_codec_resume(data); -} - /** - * snd_hda_suspend - suspend the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. + * snd_hda_bus_reset - Reset the bus + * @bus: HD-audio bus */ -int snd_hda_suspend(struct hda_bus *bus) +void snd_hda_bus_reset(struct hda_bus *bus) { struct hda_codec *codec; - ASYNC_DOMAIN_EXCLUSIVE(domain); - list_for_each_entry(codec, &bus->codec_list, list) { + list_for_each_codec(codec, bus) { + /* FIXME: maybe a better way needed for forced reset */ cancel_delayed_work_sync(&codec->jackpoll_work); +#ifdef CONFIG_PM if (hda_codec_is_power_on(codec)) { - if (bus->num_codecs > 1) - async_schedule_domain(hda_async_suspend, codec, - &domain); - else - hda_call_codec_suspend(codec, false); - } - } - - if (bus->num_codecs > 1) - async_synchronize_full_domain(&domain); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_suspend); - -/** - * snd_hda_resume - resume the codecs - * @bus: the HDA bus - * - * Returns 0 if successful. - */ -int snd_hda_resume(struct hda_bus *bus) -{ - struct hda_codec *codec; - ASYNC_DOMAIN_EXCLUSIVE(domain); - - list_for_each_entry(codec, &bus->codec_list, list) { - if (bus->num_codecs > 1) - async_schedule_domain(hda_async_resume, codec, &domain); - else + hda_call_codec_suspend(codec); hda_call_codec_resume(codec); + } +#endif } - - if (bus->num_codecs > 1) - async_synchronize_full_domain(&domain); - - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_resume); -#endif /* CONFIG_PM */ - -/* - * generic arrays - */ - -/** - * snd_array_new - get a new element from the given array - * @array: the array object - * - * Get a new element from the given array. If it exceeds the - * pre-allocated array size, re-allocate the array. - * - * Returns NULL if allocation failed. - */ -void *snd_array_new(struct snd_array *array) -{ - if (snd_BUG_ON(!array->elem_size)) - return NULL; - if (array->used >= array->alloced) { - int num = array->alloced + array->alloc_align; - int size = (num + 1) * array->elem_size; - void *nlist; - if (snd_BUG_ON(num >= 4096)) - return NULL; - nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO); - if (!nlist) - return NULL; - array->list = nlist; - array->alloced = num; - } - return snd_array_elem(array, array->used++); -} -EXPORT_SYMBOL_GPL(snd_array_new); - -/** - * snd_array_free - free the given array elements - * @array: the array object - */ -void snd_array_free(struct snd_array *array) -{ - kfree(array->list); - array->used = 0; - array->alloced = 0; - array->list = NULL; } -EXPORT_SYMBOL_GPL(snd_array_free); +EXPORT_SYMBOL_GPL(snd_hda_bus_reset); /** * snd_print_pcm_bits - Print the supported PCM fmt bits to the string buffer diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9c8820f21f94..9075ac28dc4b 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -21,41 +21,14 @@ #ifndef __SOUND_HDA_CODEC_H #define __SOUND_HDA_CODEC_H +#include <linux/kref.h> #include <sound/info.h> #include <sound/control.h> #include <sound/pcm.h> #include <sound/hwdep.h> +#include <sound/hdaudio.h> #include <sound/hda_verbs.h> - -/* - * generic arrays - */ -struct snd_array { - unsigned int used; - unsigned int alloced; - unsigned int elem_size; - unsigned int alloc_align; - void *list; -}; - -void *snd_array_new(struct snd_array *array); -void snd_array_free(struct snd_array *array); -static inline void snd_array_init(struct snd_array *array, unsigned int size, - unsigned int align) -{ - array->elem_size = size; - array->alloc_align = align; -} - -static inline void *snd_array_elem(struct snd_array *array, unsigned int idx) -{ - return array->list + idx * array->elem_size; -} - -static inline unsigned int snd_array_index(struct snd_array *array, void *ptr) -{ - return (unsigned long)(ptr - array->list) / array->elem_size; -} +#include <sound/hda_regmap.h> /* * Structures @@ -66,10 +39,6 @@ struct hda_beep; struct hda_codec; struct hda_pcm; struct hda_pcm_stream; -struct hda_bus_unsolicited; - -/* NID type */ -typedef u16 hda_nid_t; /* bus operators */ struct hda_bus_ops { @@ -84,10 +53,6 @@ struct hda_bus_ops { struct hda_pcm *pcm); /* reset bus for retry verb */ void (*bus_reset)(struct hda_bus *bus); -#ifdef CONFIG_PM - /* notify power-up/down from codec to controller */ - void (*pm_notify)(struct hda_bus *bus, bool power_up); -#endif #ifdef CONFIG_SND_HDA_DSP_LOADER /* prepare DSP transfer */ int (*load_dsp_prepare)(struct hda_bus *bus, unsigned int format, @@ -101,15 +66,6 @@ struct hda_bus_ops { #endif }; -/* template to pass to the bus constructor */ -struct hda_bus_template { - void *private_data; - struct pci_dev *pci; - const char *modelname; - int *power_save; - struct hda_bus_ops ops; -}; - /* * codec bus * @@ -117,42 +73,28 @@ struct hda_bus_template { * A hda_bus contains several codecs in the list codec_list. */ struct hda_bus { + struct hdac_bus core; + struct snd_card *card; - /* copied from template */ void *private_data; struct pci_dev *pci; const char *modelname; - int *power_save; struct hda_bus_ops ops; - /* codec linked list */ - struct list_head codec_list; - unsigned int num_codecs; - /* link caddr -> codec */ - struct hda_codec *caddr_tbl[HDA_MAX_CODEC_ADDRESS + 1]; - - struct mutex cmd_mutex; struct mutex prepare_mutex; - /* unsolicited event queue */ - struct hda_bus_unsolicited *unsol; - char workq_name[16]; - struct workqueue_struct *workq; /* common workqueue for codecs */ - /* assigned PCMs */ DECLARE_BITMAP(pcm_dev_bits, SNDRV_PCM_DEVICES); /* misc op flags */ unsigned int needs_damn_long_delay :1; unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */ - unsigned int sync_write:1; /* sync after verb write */ /* status for codec/controller */ unsigned int shutdown :1; /* being unloaded */ unsigned int rirb_error:1; /* error in codec communication */ unsigned int response_reset:1; /* controller was reset */ unsigned int in_reset:1; /* during reset operation */ - unsigned int power_keep_link_on:1; /* don't power off HDA link */ unsigned int no_response_fallback:1; /* don't fallback at RIRB error */ int primary_dig_out_type; /* primary digital out PCM type */ @@ -175,15 +117,22 @@ struct hda_codec_preset { int (*patch)(struct hda_codec *codec); }; -struct hda_codec_preset_list { +#define HDA_CODEC_ID_GENERIC_HDMI 0x00000101 +#define HDA_CODEC_ID_GENERIC 0x00000201 + +struct hda_codec_driver { + struct hdac_driver core; const struct hda_codec_preset *preset; - struct module *owner; - struct list_head list; }; -/* initial hook */ -int snd_hda_add_codec_preset(struct hda_codec_preset_list *preset); -int snd_hda_delete_codec_preset(struct hda_codec_preset_list *preset); +int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name, + struct module *owner); +#define hda_codec_driver_register(drv) \ + __hda_codec_driver_register(drv, KBUILD_MODNAME, THIS_MODULE) +void hda_codec_driver_unregister(struct hda_codec_driver *drv); +#define module_hda_codec_driver(drv) \ + module_driver(drv, hda_codec_driver_register, \ + hda_codec_driver_unregister) /* ops set by the preset patch */ struct hda_codec_ops { @@ -200,25 +149,7 @@ struct hda_codec_ops { int (*check_power_status)(struct hda_codec *codec, hda_nid_t nid); #endif void (*reboot_notify)(struct hda_codec *codec); -}; - -/* record for amp information cache */ -struct hda_cache_head { - u32 key:31; /* hash key */ - u32 dirty:1; - u16 val; /* assigned value */ - u16 next; -}; - -struct hda_amp_info { - struct hda_cache_head head; - u32 amp_caps; /* amp capabilities */ - u16 vol[2]; /* current volume & mute */ -}; - -struct hda_cache_rec { - u16 hash[64]; /* hash table for index */ - struct snd_array buf; /* record entries */ + void (*stream_pm)(struct hda_codec *codec, hda_nid_t nid, bool on); }; /* PCM callbacks */ @@ -267,41 +198,29 @@ struct hda_pcm { int device; /* device number to assign */ struct snd_pcm *pcm; /* assigned PCM instance */ bool own_chmap; /* codec driver provides own channel maps */ + /* private: */ + struct hda_codec *codec; + struct kref kref; + struct list_head list; }; /* codec information */ struct hda_codec { - struct device dev; + struct hdac_device core; struct hda_bus *bus; + struct snd_card *card; unsigned int addr; /* codec addr*/ - struct list_head list; /* list point */ - - hda_nid_t afg; /* AFG node id */ - hda_nid_t mfg; /* MFG node id */ - - /* ids */ - u8 afg_function_id; - u8 mfg_function_id; - u8 afg_unsol; - u8 mfg_unsol; - u32 vendor_id; - u32 subsystem_id; - u32 revision_id; + u32 probe_id; /* overridden id for probing */ /* detected preset */ const struct hda_codec_preset *preset; - struct module *owner; - int (*parser)(struct hda_codec *codec); - const char *vendor_name; /* codec vendor name */ - const char *chip_name; /* codec chip name */ const char *modelname; /* model name for preset */ /* set by patch */ struct hda_codec_ops patch_ops; /* PCM to create, set by patch_ops.build_pcms callback */ - unsigned int num_pcms; - struct hda_pcm *pcm_info; + struct list_head pcm_list_head; /* codec specific info */ void *spec; @@ -311,21 +230,15 @@ struct hda_codec { unsigned int beep_mode; /* widget capabilities cache */ - unsigned int num_nodes; - hda_nid_t start_nid; u32 *wcaps; struct snd_array mixers; /* list of assigned mixer elements */ struct snd_array nids; /* list of mapped mixer elements */ - struct hda_cache_rec amp_cache; /* cache for amp access */ - struct hda_cache_rec cmd_cache; /* cache for other commands */ - struct list_head conn_list; /* linked-list of connection-list */ struct mutex spdif_mutex; struct mutex control_mutex; - struct mutex hash_mutex; struct snd_array spdif_out; unsigned int spdif_in_enable; /* SPDIF input enable? */ const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */ @@ -345,6 +258,8 @@ struct hda_codec { #endif /* misc flags */ + unsigned int in_freeing:1; /* being released */ + unsigned int registered:1; /* codec was registered */ unsigned int spdif_status_reset :1; /* needs to toggle SPDIF for each * status change * (e.g. Realtek codecs) @@ -362,22 +277,14 @@ struct hda_codec { unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ - unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */ unsigned int dump_coef:1; /* dump processing coefs in codec proc file */ + unsigned int power_save_node:1; /* advanced PM for each widget */ #ifdef CONFIG_PM - unsigned int power_on :1; /* current (global) power-state */ - unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ - unsigned int pm_up_notified:1; /* PM notified to controller */ - unsigned int in_pm:1; /* suspend/resume being performed */ - int power_transition; /* power-state in transition */ - int power_count; /* current (global) power refcount */ - struct delayed_work power_work; /* delayed task for powerdown */ unsigned long power_on_acct; unsigned long power_off_acct; unsigned long power_jiffies; - spinlock_t power_lock; #endif /* filter the requested power state per nid */ @@ -409,10 +316,11 @@ struct hda_codec { struct snd_array verbs; }; -/* direction */ -enum { - HDA_INPUT, HDA_OUTPUT -}; +#define dev_to_hda_codec(_dev) container_of(_dev, struct hda_codec, core.dev) +#define hda_codec_dev(_dev) (&(_dev)->core.dev) + +#define list_for_each_codec(c, bus) \ + list_for_each_entry(c, &(bus)->core.codec_list, core.list) /* snd_hda_codec_read/write optional flags */ #define HDA_RW_NO_RESPONSE_FALLBACK (1 << 0) @@ -420,10 +328,9 @@ enum { /* * constructors */ -int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp, - struct hda_bus **busp); -int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr, - struct hda_codec **codecp); +int snd_hda_bus_new(struct snd_card *card, struct hda_bus **busp); +int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card, + unsigned int codec_addr, struct hda_codec **codecp); int snd_hda_codec_configure(struct hda_codec *codec); int snd_hda_codec_update_widgets(struct hda_codec *codec); @@ -436,9 +343,9 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid, int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags, unsigned int verb, unsigned int parm); #define snd_hda_param_read(codec, nid, param) \ - snd_hda_codec_read(codec, nid, 0, AC_VERB_PARAMETERS, param) -int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *start_id); + snd_hdac_read_parm(&(codec)->core, nid, param) +#define snd_hda_get_sub_nodes(codec, nid, start_nid) \ + snd_hdac_get_sub_nodes(&(codec)->core, nid, start_nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); static inline int @@ -446,9 +353,12 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) { return snd_hda_get_connections(codec, nid, NULL, 0); } -int snd_hda_get_num_raw_conns(struct hda_codec *codec, hda_nid_t nid); -int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t *conn_list, int max_conns); + +#define snd_hda_get_raw_connections(codec, nid, list, max_conns) \ + snd_hdac_get_connections(&(codec)->core, nid, list, max_conns) +#define snd_hda_get_num_raw_conns(codec, nid) \ + snd_hdac_get_connections(&(codec)->core, nid, NULL, 0); + int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, @@ -470,18 +380,22 @@ void snd_hda_sequence_write(struct hda_codec *codec, const struct hda_verb *seq); /* unsolicited event */ -int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex); +static inline void +snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex) +{ + snd_hdac_bus_queue_event(&bus->core, res, res_ex); +} /* cached write */ -int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm); -void snd_hda_sequence_write_cache(struct hda_codec *codec, - const struct hda_verb *seq); -int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, - int flags, unsigned int verb, unsigned int parm); -void snd_hda_codec_resume_cache(struct hda_codec *codec); -/* both for cmd & amp caches */ -void snd_hda_codec_flush_cache(struct hda_codec *codec); +static inline int +snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, + int flags, unsigned int verb, unsigned int parm) +{ + return snd_hdac_regmap_write(&codec->core, nid, verb, parm); +} + +#define snd_hda_codec_update_cache(codec, nid, flags, verb, parm) \ + snd_hda_codec_write_cache(codec, nid, flags, verb, parm) /* the struct for codec->pin_configs */ struct hda_pincfg { @@ -512,15 +426,24 @@ void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid); /* * Mixer */ -int snd_hda_build_controls(struct hda_bus *bus); int snd_hda_codec_build_controls(struct hda_codec *codec); /* * PCM */ -int snd_hda_build_pcms(struct hda_bus *bus); +int snd_hda_codec_parse_pcms(struct hda_codec *codec); int snd_hda_codec_build_pcms(struct hda_codec *codec); +__printf(2, 3) +struct hda_pcm *snd_hda_codec_pcm_new(struct hda_codec *codec, + const char *fmt, ...); + +static inline void snd_hda_codec_pcm_get(struct hda_pcm *pcm) +{ + kref_get(&pcm->kref); +} +void snd_hda_codec_pcm_put(struct hda_pcm *pcm); + int snd_hda_codec_prepare(struct hda_codec *codec, struct hda_pcm_stream *hinfo, unsigned int stream, @@ -552,20 +475,17 @@ extern const struct snd_pcm_chmap_elem snd_pcm_2_1_chmaps[]; * Misc */ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen); -void snd_hda_bus_reboot_notify(struct hda_bus *bus); void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state); int snd_hda_lock_devices(struct hda_bus *bus); void snd_hda_unlock_devices(struct hda_bus *bus); +void snd_hda_bus_reset(struct hda_bus *bus); /* * power management */ -#ifdef CONFIG_PM -int snd_hda_suspend(struct hda_bus *bus); -int snd_hda_resume(struct hda_bus *bus); -#endif +extern const struct dev_pm_ops hda_codec_driver_pm; static inline int hda_call_check_power_status(struct hda_codec *codec, hda_nid_t nid) @@ -587,65 +507,17 @@ const char *snd_hda_get_jack_location(u32 cfg); /* * power saving */ +#define snd_hda_power_up(codec) snd_hdac_power_up(&(codec)->core) +#define snd_hda_power_up_pm(codec) snd_hdac_power_up_pm(&(codec)->core) +#define snd_hda_power_down(codec) snd_hdac_power_down(&(codec)->core) +#define snd_hda_power_down_pm(codec) snd_hdac_power_down_pm(&(codec)->core) #ifdef CONFIG_PM -void snd_hda_power_save(struct hda_codec *codec, int delta, bool d3wait); +void snd_hda_set_power_save(struct hda_bus *bus, int delay); void snd_hda_update_power_acct(struct hda_codec *codec); #else -static inline void snd_hda_power_save(struct hda_codec *codec, int delta, - bool d3wait) {} +static inline void snd_hda_set_power_save(struct hda_bus *bus, int delay) {} #endif -/** - * snd_hda_power_up - Power-up the codec - * @codec: HD-audio codec - * - * Increment the power-up counter and power up the hardware really when - * not turned on yet. - */ -static inline void snd_hda_power_up(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 1, false); -} - -/** - * snd_hda_power_up_d3wait - Power-up the codec after waiting for any pending - * D3 transition to complete. This differs from snd_hda_power_up() when - * power_transition == -1. snd_hda_power_up sees this case as a nop, - * snd_hda_power_up_d3wait waits for the D3 transition to complete then powers - * back up. - * @codec: HD-audio codec - * - * Cancel any power down operation hapenning on the work queue, then power up. - */ -static inline void snd_hda_power_up_d3wait(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 1, true); -} - -/** - * snd_hda_power_down - Power-down the codec - * @codec: HD-audio codec - * - * Decrement the power-up counter and schedules the power-off work if - * the counter rearches to zero. - */ -static inline void snd_hda_power_down(struct hda_codec *codec) -{ - snd_hda_power_save(codec, -1, false); -} - -/** - * snd_hda_power_sync - Synchronize the power-save status - * @codec: HD-audio codec - * - * Synchronize the actual power state with the power account; - * called when power_save parameter is changed - */ -static inline void snd_hda_power_sync(struct hda_codec *codec) -{ - snd_hda_power_save(codec, 0, false); -} - #ifdef CONFIG_SND_HDA_PATCH_LOADER /* * patch firmware diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c index 17c2637d842c..26ce990592a0 100644 --- a/sound/pci/hda/hda_controller.c +++ b/sound/pci/hda/hda_controller.c @@ -27,10 +27,8 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/slab.h> -#include <linux/reboot.h> #include <sound/core.h> #include <sound/initval.h> -#include "hda_priv.h" #include "hda_controller.h" #define CREATE_TRACE_POINTS @@ -259,11 +257,18 @@ static void azx_timecounter_init(struct snd_pcm_substream *substream, tc->cycle_last = last; } +static inline struct hda_pcm_stream * +to_hda_pcm_stream(struct snd_pcm_substream *substream) +{ + struct azx_pcm *apcm = snd_pcm_substream_chip(substream); + return &apcm->info->stream[substream->stream]; +} + static u64 azx_adjust_codec_delay(struct snd_pcm_substream *substream, u64 nsec) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); u64 codec_frames, codec_nsecs; if (!hinfo->ops.get_delay) @@ -399,7 +404,7 @@ static int azx_setup_periods(struct azx *chip, static int azx_pcm_close(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); unsigned long flags; @@ -410,9 +415,11 @@ static int azx_pcm_close(struct snd_pcm_substream *substream) azx_dev->running = 0; spin_unlock_irqrestore(&chip->reg_lock, flags); azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); snd_hda_power_down(apcm->codec); mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); return 0; } @@ -441,7 +448,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); struct azx *chip = apcm->chip; - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); int err; /* reset BDL address */ @@ -468,7 +475,7 @@ static int azx_pcm_prepare(struct snd_pcm_substream *substream) struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev = get_azx_dev(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct snd_pcm_runtime *runtime = substream->runtime; unsigned int bufsize, period_bytes, format_val, stream_tag; int err; @@ -708,7 +715,7 @@ unsigned int azx_get_position(struct azx *chip, if (substream->runtime) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); if (chip->get_delay[stream]) delay += chip->get_delay[stream](chip, azx_dev, pos); @@ -732,17 +739,32 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream) azx_get_position(chip, azx_dev)); } -static int azx_get_wallclock_tstamp(struct snd_pcm_substream *substream, - struct timespec *ts) +static int azx_get_time_info(struct snd_pcm_substream *substream, + struct timespec *system_ts, struct timespec *audio_ts, + struct snd_pcm_audio_tstamp_config *audio_tstamp_config, + struct snd_pcm_audio_tstamp_report *audio_tstamp_report) { struct azx_dev *azx_dev = get_azx_dev(substream); u64 nsec; - nsec = timecounter_read(&azx_dev->azx_tc); - nsec = div_u64(nsec, 3); /* can be optimized */ - nsec = azx_adjust_codec_delay(substream, nsec); + if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) && + (audio_tstamp_config->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK)) { + + snd_pcm_gettime(substream->runtime, system_ts); - *ts = ns_to_timespec(nsec); + nsec = timecounter_read(&azx_dev->azx_tc); + nsec = div_u64(nsec, 3); /* can be optimized */ + if (audio_tstamp_config->report_delay) + nsec = azx_adjust_codec_delay(substream, nsec); + + *audio_ts = ns_to_timespec(nsec); + + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK; + audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */ + audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */ + + } else + audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT; return 0; } @@ -756,7 +778,8 @@ static struct snd_pcm_hardware azx_pcm_hw = { /* SNDRV_PCM_INFO_RESUME |*/ SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START | - SNDRV_PCM_INFO_HAS_WALL_CLOCK | + SNDRV_PCM_INFO_HAS_WALL_CLOCK | /* legacy */ + SNDRV_PCM_INFO_HAS_LINK_ATIME | SNDRV_PCM_INFO_NO_PERIOD_WAKEUP), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, @@ -775,7 +798,7 @@ static struct snd_pcm_hardware azx_pcm_hw = { static int azx_pcm_open(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); - struct hda_pcm_stream *hinfo = apcm->hinfo[substream->stream]; + struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); struct azx *chip = apcm->chip; struct azx_dev *azx_dev; struct snd_pcm_runtime *runtime = substream->runtime; @@ -783,11 +806,12 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) int err; int buff_step; + snd_hda_codec_pcm_get(apcm->info); mutex_lock(&chip->open_mutex); azx_dev = azx_assign_device(chip, substream); if (azx_dev == NULL) { - mutex_unlock(&chip->open_mutex); - return -EBUSY; + err = -EBUSY; + goto unlock; } runtime->hw = azx_pcm_hw; runtime->hw.channels_min = hinfo->channels_min; @@ -821,13 +845,14 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) buff_step); snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, buff_step); - snd_hda_power_up_d3wait(apcm->codec); - err = hinfo->ops.open(hinfo, apcm->codec, substream); + snd_hda_power_up(apcm->codec); + if (hinfo->ops.open) + err = hinfo->ops.open(hinfo, apcm->codec, substream); + else + err = -ENODEV; if (err < 0) { azx_release_device(azx_dev); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return err; + goto powerdown; } snd_pcm_limit_hw_rates(runtime); /* sanity check */ @@ -836,16 +861,18 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_BUG_ON(!runtime->hw.formats) || snd_BUG_ON(!runtime->hw.rates)) { azx_release_device(azx_dev); - hinfo->ops.close(hinfo, apcm->codec, substream); - snd_hda_power_down(apcm->codec); - mutex_unlock(&chip->open_mutex); - return -EINVAL; + if (hinfo->ops.close) + hinfo->ops.close(hinfo, apcm->codec, substream); + err = -EINVAL; + goto powerdown; } - /* disable WALLCLOCK timestamps for capture streams + /* disable LINK_ATIME timestamps for capture streams until we figure out how to handle digital inputs */ - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_WALL_CLOCK; /* legacy */ + runtime->hw.info &= ~SNDRV_PCM_INFO_HAS_LINK_ATIME; + } spin_lock_irqsave(&chip->reg_lock, flags); azx_dev->substream = substream; @@ -856,6 +883,13 @@ static int azx_pcm_open(struct snd_pcm_substream *substream) snd_pcm_set_sync(substream); mutex_unlock(&chip->open_mutex); return 0; + + powerdown: + snd_hda_power_down(apcm->codec); + unlock: + mutex_unlock(&chip->open_mutex); + snd_hda_codec_pcm_put(apcm->info); + return err; } static int azx_pcm_mmap(struct snd_pcm_substream *substream, @@ -877,7 +911,7 @@ static struct snd_pcm_ops azx_pcm_ops = { .prepare = azx_pcm_prepare, .trigger = azx_pcm_trigger, .pointer = azx_pcm_pointer, - .wall_clock = azx_get_wallclock_tstamp, + .get_time_info = azx_get_time_info, .mmap = azx_pcm_mmap, .page = snd_pcm_sgbuf_ops_page, }; @@ -887,6 +921,7 @@ static void azx_pcm_free(struct snd_pcm *pcm) struct azx_pcm *apcm = pcm->private_data; if (apcm) { list_del(&apcm->list); + apcm->info->pcm = NULL; kfree(apcm); } } @@ -923,6 +958,7 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, apcm->chip = chip; apcm->pcm = pcm; apcm->codec = codec; + apcm->info = cpcm; pcm->private_data = apcm; pcm->private_free = azx_pcm_free; if (cpcm->pcm_type == HDA_PCM_TYPE_MODEM) @@ -930,7 +966,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, list_add_tail(&apcm->list, &chip->pcm_list); cpcm->pcm = pcm; for (s = 0; s < 2; s++) { - apcm->hinfo[s] = &cpcm->stream[s]; if (cpcm->stream[s].substreams) snd_pcm_set_ops(pcm, s, &azx_pcm_ops); } @@ -941,9 +976,6 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, chip->card->dev, size, MAX_PREALLOC_SIZE); - /* link to codec */ - for (s = 0; s < 2; s++) - pcm->streams[s].dev.parent = &codec->dev; return 0; } @@ -952,14 +984,9 @@ static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec, */ static int azx_alloc_cmd_io(struct azx *chip) { - int err; - /* single page (at least 4096 bytes) must suffice for both ringbuffes */ - err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, - PAGE_SIZE, &chip->rb); - if (err < 0) - dev_err(chip->card->dev, "cannot allocate CORB/RIRB\n"); - return err; + return chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, + PAGE_SIZE, &chip->rb); } static void azx_init_cmd_io(struct azx *chip) @@ -1445,7 +1472,6 @@ static void azx_load_dsp_cleanup(struct hda_bus *bus, int azx_alloc_stream_pages(struct azx *chip) { int i, err; - struct snd_card *card = chip->card; for (i = 0; i < chip->num_streams; i++) { dsp_lock_init(&chip->azx_dev[i]); @@ -1453,18 +1479,14 @@ int azx_alloc_stream_pages(struct azx *chip) err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, BDL_SIZE, &chip->azx_dev[i].bdl); - if (err < 0) { - dev_err(card->dev, "cannot allocate BDL\n"); + if (err < 0) return -ENOMEM; - } } /* allocate memory for the position buffer */ err = chip->ops->dma_alloc_pages(chip, SNDRV_DMA_TYPE_DEV, chip->num_streams * 8, &chip->posbuf); - if (err < 0) { - dev_err(card->dev, "cannot allocate posbuf\n"); + if (err < 0) return -ENOMEM; - } /* allocate CORB/RIRB */ err = azx_alloc_cmd_io(chip); @@ -1676,7 +1698,7 @@ irqreturn_t azx_interrupt(int irq, void *dev_id) int i; #ifdef CONFIG_PM - if (chip->driver_caps & AZX_DCAPS_PM_RUNTIME) + if (azx_has_pm_runtime(chip)) if (!pm_runtime_active(chip->card->dev)) return IRQ_NONE; #endif @@ -1742,12 +1764,12 @@ static int probe_codec(struct azx *chip, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res; - mutex_lock(&chip->bus->cmd_mutex); + mutex_lock(&chip->bus->core.cmd_mutex); chip->probing = 1; azx_send_cmd(chip->bus, cmd); res = azx_get_response(chip->bus, addr); chip->probing = 0; - mutex_unlock(&chip->bus->cmd_mutex); + mutex_unlock(&chip->bus->core.cmd_mutex); if (res == -1) return -EIO; dev_dbg(chip->card->dev, "codec #%d probed OK\n", addr); @@ -1761,34 +1783,11 @@ static void azx_bus_reset(struct hda_bus *bus) bus->in_reset = 1; azx_stop_chip(chip); azx_init_chip(chip, true); -#ifdef CONFIG_PM - if (chip->initialized) { - struct azx_pcm *p; - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - snd_hda_suspend(chip->bus); - snd_hda_resume(chip->bus); - } -#endif + if (chip->initialized) + snd_hda_bus_reset(chip->bus); bus->in_reset = 0; } -#ifdef CONFIG_PM -/* power-up/down the controller */ -static void azx_power_notify(struct hda_bus *bus, bool power_up) -{ - struct azx *chip = bus->private_data; - - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) - return; - - if (power_up) - pm_runtime_get_sync(chip->card->dev); - else - pm_runtime_put_sync(chip->card->dev); -} -#endif - static int get_jackpoll_interval(struct azx *chip) { int i; @@ -1810,41 +1809,59 @@ static int get_jackpoll_interval(struct azx *chip) return j; } -/* Codec initialization */ -int azx_codec_create(struct azx *chip, const char *model, - unsigned int max_slots, - int *power_save_to) -{ - struct hda_bus_template bus_temp; - int c, codecs, err; - - memset(&bus_temp, 0, sizeof(bus_temp)); - bus_temp.private_data = chip; - bus_temp.modelname = model; - bus_temp.pci = chip->pci; - bus_temp.ops.command = azx_send_cmd; - bus_temp.ops.get_response = azx_get_response; - bus_temp.ops.attach_pcm = azx_attach_pcm_stream; - bus_temp.ops.bus_reset = azx_bus_reset; -#ifdef CONFIG_PM - bus_temp.power_save = power_save_to; - bus_temp.ops.pm_notify = azx_power_notify; -#endif +static struct hda_bus_ops bus_ops = { + .command = azx_send_cmd, + .get_response = azx_get_response, + .attach_pcm = azx_attach_pcm_stream, + .bus_reset = azx_bus_reset, #ifdef CONFIG_SND_HDA_DSP_LOADER - bus_temp.ops.load_dsp_prepare = azx_load_dsp_prepare; - bus_temp.ops.load_dsp_trigger = azx_load_dsp_trigger; - bus_temp.ops.load_dsp_cleanup = azx_load_dsp_cleanup; + .load_dsp_prepare = azx_load_dsp_prepare, + .load_dsp_trigger = azx_load_dsp_trigger, + .load_dsp_cleanup = azx_load_dsp_cleanup, #endif +}; + +/* HD-audio bus initialization */ +int azx_bus_create(struct azx *chip, const char *model) +{ + struct hda_bus *bus; + int err; - err = snd_hda_bus_new(chip->card, &bus_temp, &chip->bus); + err = snd_hda_bus_new(chip->card, &bus); if (err < 0) return err; + chip->bus = bus; + bus->private_data = chip; + bus->pci = chip->pci; + bus->modelname = model; + bus->ops = bus_ops; + if (chip->driver_caps & AZX_DCAPS_RIRB_DELAY) { dev_dbg(chip->card->dev, "Enable delay in RIRB handling\n"); - chip->bus->needs_damn_long_delay = 1; + bus->needs_damn_long_delay = 1; + } + + /* AMD chipsets often cause the communication stalls upon certain + * sequence like the pin-detection. It seems that forcing the synced + * access works around the stall. Grrr... + */ + if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { + dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n"); + bus->core.sync_write = 1; + bus->allow_bus_reset = 1; } + return 0; +} +EXPORT_SYMBOL_GPL(azx_bus_create); + +/* Probe codecs */ +int azx_probe_codecs(struct azx *chip, unsigned int max_slots) +{ + struct hda_bus *bus = chip->bus; + int c, codecs, err; + codecs = 0; if (!max_slots) max_slots = AZX_DEFAULT_CODECS; @@ -1872,21 +1889,11 @@ int azx_codec_create(struct azx *chip, const char *model, } } - /* AMD chipsets often cause the communication stalls upon certain - * sequence like the pin-detection. It seems that forcing the synced - * access works around the stall. Grrr... - */ - if (chip->driver_caps & AZX_DCAPS_SYNC_WRITE) { - dev_dbg(chip->card->dev, "Enable sync_write for stable communication\n"); - chip->bus->sync_write = 1; - chip->bus->allow_bus_reset = 1; - } - /* Then create codec instances */ for (c = 0; c < max_slots; c++) { if ((chip->codec_mask & (1 << c)) & chip->codec_probe_mask) { struct hda_codec *codec; - err = snd_hda_codec_new(chip->bus, c, &codec); + err = snd_hda_codec_new(bus, bus->card, c, &codec); if (err < 0) continue; codec->jackpoll_interval = get_jackpoll_interval(chip); @@ -1900,26 +1907,19 @@ int azx_codec_create(struct azx *chip, const char *model, } return 0; } -EXPORT_SYMBOL_GPL(azx_codec_create); +EXPORT_SYMBOL_GPL(azx_probe_codecs); /* configure each codec instance */ int azx_codec_configure(struct azx *chip) { struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { + list_for_each_codec(codec, chip->bus) { snd_hda_codec_configure(codec); } return 0; } EXPORT_SYMBOL_GPL(azx_codec_configure); -/* mixer creation - all stuff is implemented in hda module */ -int azx_mixer_create(struct azx *chip) -{ - return snd_hda_build_controls(chip->bus); -} -EXPORT_SYMBOL_GPL(azx_mixer_create); - static bool is_input_stream(struct azx *chip, unsigned char index) { @@ -1966,30 +1966,5 @@ int azx_init_stream(struct azx *chip) } EXPORT_SYMBOL_GPL(azx_init_stream); -/* - * reboot notifier for hang-up problem at power-down - */ -static int azx_halt(struct notifier_block *nb, unsigned long event, void *buf) -{ - struct azx *chip = container_of(nb, struct azx, reboot_notifier); - snd_hda_bus_reboot_notify(chip->bus); - azx_stop_chip(chip); - return NOTIFY_OK; -} - -void azx_notifier_register(struct azx *chip) -{ - chip->reboot_notifier.notifier_call = azx_halt; - register_reboot_notifier(&chip->reboot_notifier); -} -EXPORT_SYMBOL_GPL(azx_notifier_register); - -void azx_notifier_unregister(struct azx *chip) -{ - if (chip->reboot_notifier.notifier_call) - unregister_reboot_notifier(&chip->reboot_notifier); -} -EXPORT_SYMBOL_GPL(azx_notifier_unregister); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Common HDA driver functions"); diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h index c90d10fd4d8f..be1b7ded8d82 100644 --- a/sound/pci/hda/hda_controller.h +++ b/sound/pci/hda/hda_controller.h @@ -15,10 +15,396 @@ #ifndef __SOUND_HDA_CONTROLLER_H #define __SOUND_HDA_CONTROLLER_H +#include <linux/timecounter.h> +#include <linux/interrupt.h> #include <sound/core.h> +#include <sound/pcm.h> #include <sound/initval.h> #include "hda_codec.h" -#include "hda_priv.h" + +/* + * registers + */ +#define AZX_REG_GCAP 0x00 +#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */ +#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */ +#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */ +#define AZX_GCAP_ISS (15 << 8) /* # of input streams */ +#define AZX_GCAP_OSS (15 << 12) /* # of output streams */ +#define AZX_REG_VMIN 0x02 +#define AZX_REG_VMAJ 0x03 +#define AZX_REG_OUTPAY 0x04 +#define AZX_REG_INPAY 0x06 +#define AZX_REG_GCTL 0x08 +#define AZX_GCTL_RESET (1 << 0) /* controller reset */ +#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */ +#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ +#define AZX_REG_WAKEEN 0x0c +#define AZX_REG_STATESTS 0x0e +#define AZX_REG_GSTS 0x10 +#define AZX_GSTS_FSTS (1 << 1) /* flush status */ +#define AZX_REG_INTCTL 0x20 +#define AZX_REG_INTSTS 0x24 +#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */ +#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ +#define AZX_REG_SSYNC 0x38 +#define AZX_REG_CORBLBASE 0x40 +#define AZX_REG_CORBUBASE 0x44 +#define AZX_REG_CORBWP 0x48 +#define AZX_REG_CORBRP 0x4a +#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */ +#define AZX_REG_CORBCTL 0x4c +#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */ +#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ +#define AZX_REG_CORBSTS 0x4d +#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */ +#define AZX_REG_CORBSIZE 0x4e + +#define AZX_REG_RIRBLBASE 0x50 +#define AZX_REG_RIRBUBASE 0x54 +#define AZX_REG_RIRBWP 0x58 +#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */ +#define AZX_REG_RINTCNT 0x5a +#define AZX_REG_RIRBCTL 0x5c +#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ +#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */ +#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ +#define AZX_REG_RIRBSTS 0x5d +#define AZX_RBSTS_IRQ (1 << 0) /* response irq */ +#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */ +#define AZX_REG_RIRBSIZE 0x5e + +#define AZX_REG_IC 0x60 +#define AZX_REG_IR 0x64 +#define AZX_REG_IRS 0x68 +#define AZX_IRS_VALID (1<<1) +#define AZX_IRS_BUSY (1<<0) + +#define AZX_REG_DPLBASE 0x70 +#define AZX_REG_DPUBASE 0x74 +#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */ + +/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ +enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; + +/* stream register offsets from stream base */ +#define AZX_REG_SD_CTL 0x00 +#define AZX_REG_SD_STS 0x03 +#define AZX_REG_SD_LPIB 0x04 +#define AZX_REG_SD_CBL 0x08 +#define AZX_REG_SD_LVI 0x0c +#define AZX_REG_SD_FIFOW 0x0e +#define AZX_REG_SD_FIFOSIZE 0x10 +#define AZX_REG_SD_FORMAT 0x12 +#define AZX_REG_SD_BDLPL 0x18 +#define AZX_REG_SD_BDLPU 0x1c + +/* PCI space */ +#define AZX_PCIREG_TCSEL 0x44 + +/* + * other constants + */ + +/* max number of fragments - we may use more if allocating more pages for BDL */ +#define BDL_SIZE 4096 +#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) +#define AZX_MAX_FRAG 32 +/* max buffer size - no h/w limit, you can increase as you like */ +#define AZX_MAX_BUF_SIZE (1024*1024*1024) + +/* RIRB int mask: overrun[2], response[0] */ +#define RIRB_INT_RESPONSE 0x01 +#define RIRB_INT_OVERRUN 0x04 +#define RIRB_INT_MASK 0x05 + +/* STATESTS int mask: S3,SD2,SD1,SD0 */ +#define AZX_MAX_CODECS 8 +#define AZX_DEFAULT_CODECS 4 +#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) + +/* SD_CTL bits */ +#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ +#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ +#define SD_CTL_STRIPE (3 << 16) /* stripe control */ +#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ +#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ +#define SD_CTL_STREAM_TAG_MASK (0xf << 20) +#define SD_CTL_STREAM_TAG_SHIFT 20 + +/* SD_CTL and SD_STS */ +#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ +#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ +#define SD_INT_COMPLETE 0x04 /* completion interrupt */ +#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ + SD_INT_COMPLETE) + +/* SD_STS */ +#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ + +/* INTCTL and INTSTS */ +#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ +#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ +#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ + +/* below are so far hardcoded - should read registers in future */ +#define AZX_MAX_CORB_ENTRIES 256 +#define AZX_MAX_RIRB_ENTRIES 256 + +/* driver quirks (capabilities) */ +/* bits 0-7 are used for indicating driver type */ +#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ +#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ +#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ +#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ +#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ +#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ +#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ +#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ +#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ +#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ +#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ +#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ +#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ +/* 22 unused */ +#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ +#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ +#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ +#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ +#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ +#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ +#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ +#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ + +enum { + AZX_SNOOP_TYPE_NONE, + AZX_SNOOP_TYPE_SCH, + AZX_SNOOP_TYPE_ATI, + AZX_SNOOP_TYPE_NVIDIA, +}; + +/* HD Audio class code */ +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 + +struct azx_dev { + struct snd_dma_buffer bdl; /* BDL buffer */ + u32 *posbuf; /* position buffer pointer */ + + unsigned int bufsize; /* size of the play buffer in bytes */ + unsigned int period_bytes; /* size of the period in bytes */ + unsigned int frags; /* number for period in the play buffer */ + unsigned int fifo_size; /* FIFO size */ + unsigned long start_wallclk; /* start + minimum wallclk */ + unsigned long period_wallclk; /* wallclk for period */ + + void __iomem *sd_addr; /* stream descriptor pointer */ + + u32 sd_int_sta_mask; /* stream int status mask */ + + /* pcm support */ + struct snd_pcm_substream *substream; /* assigned substream, + * set in PCM open + */ + unsigned int format_val; /* format value to be set in the + * controller and the codec + */ + unsigned char stream_tag; /* assigned stream */ + unsigned char index; /* stream index */ + int assigned_key; /* last device# key assigned to */ + + unsigned int opened:1; + unsigned int running:1; + unsigned int irq_pending:1; + unsigned int prepared:1; + unsigned int locked:1; + /* + * For VIA: + * A flag to ensure DMA position is 0 + * when link position is not greater than FIFO size + */ + unsigned int insufficient:1; + unsigned int wc_marked:1; + unsigned int no_period_wakeup:1; + + struct timecounter azx_tc; + struct cyclecounter azx_cc; + + int delay_negative_threshold; + +#ifdef CONFIG_SND_HDA_DSP_LOADER + /* Allows dsp load to have sole access to the playback stream. */ + struct mutex dsp_mutex; +#endif +}; + +/* CORB/RIRB */ +struct azx_rb { + u32 *buf; /* CORB/RIRB buffer + * Each CORB entry is 4byte, RIRB is 8byte + */ + dma_addr_t addr; /* physical address of CORB/RIRB buffer */ + /* for RIRB */ + unsigned short rp, wp; /* read/write pointers */ + int cmds[AZX_MAX_CODECS]; /* number of pending requests */ + u32 res[AZX_MAX_CODECS]; /* last read value */ +}; + +struct azx; + +/* Functions to read/write to hda registers. */ +struct hda_controller_ops { + /* Register Access */ + void (*reg_writel)(u32 value, u32 __iomem *addr); + u32 (*reg_readl)(u32 __iomem *addr); + void (*reg_writew)(u16 value, u16 __iomem *addr); + u16 (*reg_readw)(u16 __iomem *addr); + void (*reg_writeb)(u8 value, u8 __iomem *addr); + u8 (*reg_readb)(u8 __iomem *addr); + /* Disable msi if supported, PCI only */ + int (*disable_msi_reset_irq)(struct azx *); + /* Allocation ops */ + int (*dma_alloc_pages)(struct azx *chip, + int type, + size_t size, + struct snd_dma_buffer *buf); + void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf); + int (*substream_alloc_pages)(struct azx *chip, + struct snd_pcm_substream *substream, + size_t size); + int (*substream_free_pages)(struct azx *chip, + struct snd_pcm_substream *substream); + void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, + struct vm_area_struct *area); + /* Check if current position is acceptable */ + int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); +}; + +struct azx_pcm { + struct azx *chip; + struct snd_pcm *pcm; + struct hda_codec *codec; + struct hda_pcm *info; + struct list_head list; +}; + +typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); +typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos); + +struct azx { + struct snd_card *card; + struct pci_dev *pci; + int dev_index; + + /* chip type specific */ + int driver_type; + unsigned int driver_caps; + int playback_streams; + int playback_index_offset; + int capture_streams; + int capture_index_offset; + int num_streams; + const int *jackpoll_ms; /* per-card jack poll interval */ + + /* Register interaction. */ + const struct hda_controller_ops *ops; + + /* position adjustment callbacks */ + azx_get_pos_callback_t get_position[2]; + azx_get_delay_callback_t get_delay[2]; + + /* pci resources */ + unsigned long addr; + void __iomem *remap_addr; + int irq; + + /* locks */ + spinlock_t reg_lock; + struct mutex open_mutex; /* Prevents concurrent open/close operations */ + + /* streams (x num_streams) */ + struct azx_dev *azx_dev; + + /* PCM */ + struct list_head pcm_list; /* azx_pcm list */ + + /* HD codec */ + unsigned short codec_mask; + int codec_probe_mask; /* copied from probe_mask option */ + struct hda_bus *bus; + unsigned int beep_mode; + + /* CORB/RIRB */ + struct azx_rb corb; + struct azx_rb rirb; + + /* CORB/RIRB and position buffers */ + struct snd_dma_buffer rb; + struct snd_dma_buffer posbuf; + +#ifdef CONFIG_SND_HDA_PATCH_LOADER + const struct firmware *fw; +#endif + + /* flags */ + const int *bdl_pos_adj; + int poll_count; + unsigned int running:1; + unsigned int initialized:1; + unsigned int single_cmd:1; + unsigned int polling_mode:1; + unsigned int msi:1; + unsigned int probing:1; /* codec probing phase */ + unsigned int snoop:1; + unsigned int align_buffer_size:1; + unsigned int region_requested:1; + unsigned int disabled:1; /* disabled by VGA-switcher */ + + /* for debugging */ + unsigned int last_cmd[AZX_MAX_CODECS]; + +#ifdef CONFIG_SND_HDA_DSP_LOADER + struct azx_dev saved_azx_dev; +#endif +}; + +#ifdef CONFIG_X86 +#define azx_snoop(chip) ((chip)->snoop) +#else +#define azx_snoop(chip) true +#endif + +/* + * macros for easy use + */ + +#define azx_writel(chip, reg, value) \ + ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) +#define azx_readl(chip, reg) \ + ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) +#define azx_writew(chip, reg, value) \ + ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) +#define azx_readw(chip, reg) \ + ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) +#define azx_writeb(chip, reg, value) \ + ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) +#define azx_readb(chip, reg) \ + ((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg)) + +#define azx_sd_writel(chip, dev, reg, value) \ + ((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) +#define azx_sd_readl(chip, dev, reg) \ + ((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) +#define azx_sd_writew(chip, dev, reg, value) \ + ((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) +#define azx_sd_readw(chip, dev, reg) \ + ((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) +#define azx_sd_writeb(chip, dev, reg, value) \ + ((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) +#define azx_sd_readb(chip, dev, reg) \ + ((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg)) + +#define azx_has_pm_runtime(chip) \ + (!AZX_DCAPS_PM_RUNTIME || ((chip)->driver_caps & AZX_DCAPS_PM_RUNTIME)) /* PCM setup */ static inline struct azx_dev *get_azx_dev(struct snd_pcm_substream *substream) @@ -43,14 +429,9 @@ void azx_enter_link_reset(struct azx *chip); irqreturn_t azx_interrupt(int irq, void *dev_id); /* Codec interface */ -int azx_codec_create(struct azx *chip, const char *model, - unsigned int max_slots, - int *power_save_to); +int azx_bus_create(struct azx *chip, const char *model); +int azx_probe_codecs(struct azx *chip, unsigned int max_slots); int azx_codec_configure(struct azx *chip); -int azx_mixer_create(struct azx *chip); int azx_init_stream(struct azx *chip); -void azx_notifier_register(struct azx *chip); -void azx_notifier_unregister(struct azx *chip); - #endif /* __SOUND_HDA_CONTROLLER_H */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8ec5289f8e05..3d2597b7037b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -140,6 +140,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "single_adc_amp"); if (val >= 0) codec->single_adc_amp = !!val; + val = snd_hda_get_bool_hint(codec, "power_save_node"); + if (val >= 0) + codec->power_save_node = !!val; val = snd_hda_get_bool_hint(codec, "auto_mute"); if (val >= 0) @@ -648,12 +651,24 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, unsigned int dir, unsigned int idx) { struct hda_gen_spec *spec = codec->spec; + int type = get_wcaps_type(get_wcaps(codec, nid)); int i, n; + if (nid == codec->core.afg) + return true; + for (n = 0; n < spec->paths.used; n++) { struct nid_path *path = snd_array_elem(&spec->paths, n); if (!path->active) continue; + if (codec->power_save_node) { + if (!path->stream_enabled) + continue; + /* ignore unplugged paths except for DAC/ADC */ + if (!(path->pin_enabled || path->pin_fixed) && + type != AC_WID_AUD_OUT && type != AC_WID_AUD_IN) + continue; + } for (i = 0; i < path->depth; i++) { if (path->path[i] == nid) { if (dir == HDA_OUTPUT || path->idx[i] == idx) @@ -807,6 +822,44 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, } } +/* sync power of each widget in the the given path */ +static hda_nid_t path_power_update(struct hda_codec *codec, + struct nid_path *path, + bool allow_powerdown) +{ + hda_nid_t nid, changed = 0; + int i, state; + + for (i = 0; i < path->depth; i++) { + nid = path->path[i]; + if (!(get_wcaps(codec, nid) & AC_WCAP_POWER)) + continue; + if (nid == codec->core.afg) + continue; + if (!allow_powerdown || is_active_nid_for_any(codec, nid)) + state = AC_PWRST_D0; + else + state = AC_PWRST_D3; + if (!snd_hda_check_power_state(codec, nid, state)) { + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_POWER_STATE, state); + changed = nid; + if (state == AC_PWRST_D0) + snd_hdac_regmap_sync_node(&codec->core, nid); + } + } + return changed; +} + +/* do sync with the last power state change */ +static void sync_power_state_change(struct hda_codec *codec, hda_nid_t nid) +{ + if (nid) { + msleep(10); + snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); + } +} + /** * snd_hda_activate_path - activate or deactivate the given path * @codec: the HDA codec @@ -825,15 +878,13 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, if (!enable) path->active = false; + /* make sure the widget is powered up */ + if (enable && (spec->power_down_unused || codec->power_save_node)) + path_power_update(codec, path, codec->power_save_node); + for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; - if (enable && spec->power_down_unused) { - /* make sure the widget is powered up */ - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D0)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D0); - } + if (enable && path->multi[i]) snd_hda_codec_update_cache(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, @@ -853,28 +904,10 @@ EXPORT_SYMBOL_GPL(snd_hda_activate_path); static void path_power_down_sync(struct hda_codec *codec, struct nid_path *path) { struct hda_gen_spec *spec = codec->spec; - bool changed = false; - int i; - if (!spec->power_down_unused || path->active) + if (!(spec->power_down_unused || codec->power_save_node) || path->active) return; - - for (i = 0; i < path->depth; i++) { - hda_nid_t nid = path->path[i]; - if (!snd_hda_check_power_state(codec, nid, AC_PWRST_D3) && - !is_active_nid_for_any(codec, nid)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_POWER_STATE, - AC_PWRST_D3); - changed = true; - } - } - - if (changed) { - msleep(10); - snd_hda_codec_read(codec, path->path[0], 0, - AC_VERB_GET_POWER_STATE, 0); - } + sync_power_state_change(codec, path_power_update(codec, path, true)); } /* turn on/off EAPD on the given pin */ @@ -1574,6 +1607,7 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) return 0; /* print_nid_path(codec, "output-aamix", path); */ path->active = false; /* unused as default */ + path->pin_fixed = true; /* static route */ return snd_hda_get_path_idx(codec, path); } @@ -1863,12 +1897,11 @@ static void debug_show_configs(struct hda_codec *codec, static void fill_all_dac_nids(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; + hda_nid_t nid; spec->num_all_dacs = 0; memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) continue; if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { @@ -2998,6 +3031,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, } path->active = true; + path->stream_enabled = true; /* no DAC/ADC involved */ err = add_loopback_list(spec, mix_nid, idx); if (err < 0) return err; @@ -3009,6 +3043,8 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, if (path) { print_nid_path(codec, "loopback-merge", path); path->active = true; + path->pin_fixed = true; /* static route */ + path->stream_enabled = true; /* no DAC/ADC involved */ spec->loopback_merge_path = snd_hda_get_path_idx(codec, path); } @@ -3030,10 +3066,9 @@ static int fill_adc_nids(struct hda_codec *codec) hda_nid_t nid; hda_nid_t *adc_nids = spec->adc_nids; int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; + int nums = 0; - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps); @@ -3346,11 +3381,6 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, imux = &spec->input_mux; adc_idx = kcontrol->id.index; mutex_lock(&codec->control_mutex); - /* we use the cache-only update at first since multiple input paths - * may shared the same amp; by updating only caches, the redundant - * writes to hardware can be reduced. - */ - codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { path = get_input_path(codec, adc_idx, i); if (!path || !path->ctls[type]) @@ -3358,12 +3388,9 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); if (err < 0) - goto error; + break; } - error: - codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec, kcontrol, ucontrol); return err; @@ -3810,6 +3837,7 @@ static void parse_digital(struct hda_codec *codec) continue; print_nid_path(codec, "digout", path); path->active = true; + path->pin_fixed = true; /* no jack detection */ spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_OUT, false); if (!nums) { @@ -3826,8 +3854,7 @@ static void parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { pin = spec->autocfg.dig_in_pin; - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + for_each_hda_codec_node(dig_nid, codec) { unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; @@ -3837,6 +3864,7 @@ static void parse_digital(struct hda_codec *codec) if (path) { print_nid_path(codec, "digin", path); path->active = true; + path->pin_fixed = true; /* no jack */ spec->dig_in_nid = dig_nid; spec->digin_path = snd_hda_get_path_idx(codec, path); set_pin_target(codec, pin, PIN_IN, false); @@ -3896,6 +3924,238 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, return 1; } +/* power up/down widgets in the all paths that match with the given NID + * as terminals (either start- or endpoint) + * + * returns the last changed NID, or zero if unchanged. + */ +static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid, + int pin_state, int stream_state) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t last, changed = 0; + struct nid_path *path; + int n; + + for (n = 0; n < spec->paths.used; n++) { + path = snd_array_elem(&spec->paths, n); + if (path->path[0] == nid || + path->path[path->depth - 1] == nid) { + bool pin_old = path->pin_enabled; + bool stream_old = path->stream_enabled; + + if (pin_state >= 0) + path->pin_enabled = pin_state; + if (stream_state >= 0) + path->stream_enabled = stream_state; + if ((!path->pin_fixed && path->pin_enabled != pin_old) + || path->stream_enabled != stream_old) { + last = path_power_update(codec, path, true); + if (last) + changed = last; + } + } + } + return changed; +} + +/* check the jack status for power control */ +static bool detect_pin_state(struct hda_codec *codec, hda_nid_t pin) +{ + if (!is_jack_detectable(codec, pin)) + return true; + return snd_hda_jack_detect_state(codec, pin) != HDA_JACK_NOT_PRESENT; +} + +/* power up/down the paths of the given pin according to the jack state; + * power = 0/1 : only power up/down if it matches with the jack state, + * < 0 : force power up/down to follow the jack sate + * + * returns the last changed NID, or zero if unchanged. + */ +static hda_nid_t set_pin_power_jack(struct hda_codec *codec, hda_nid_t pin, + int power) +{ + bool on; + + if (!codec->power_save_node) + return 0; + + on = detect_pin_state(codec, pin); + + if (power >= 0 && on != power) + return 0; + return set_path_power(codec, pin, on, -1); +} + +static void pin_power_callback(struct hda_codec *codec, + struct hda_jack_callback *jack, + bool on) +{ + if (jack && jack->tbl->nid) + sync_power_state_change(codec, + set_pin_power_jack(codec, jack->tbl->nid, on)); +} + +/* callback only doing power up -- called at first */ +static void pin_power_up_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + pin_power_callback(codec, jack, true); +} + +/* callback only doing power down -- called at last */ +static void pin_power_down_callback(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + pin_power_callback(codec, jack, false); +} + +/* set up the power up/down callbacks */ +static void add_pin_power_ctls(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, bool on) +{ + int i; + hda_jack_callback_fn cb = + on ? pin_power_up_callback : pin_power_down_callback; + + for (i = 0; i < num_pins && pins[i]; i++) { + if (is_jack_detectable(codec, pins[i])) + snd_hda_jack_detect_enable_callback(codec, pins[i], cb); + else + set_path_power(codec, pins[i], true, -1); + } +} + +/* enabled power callback to each available I/O pin with jack detections; + * the digital I/O pins are excluded because of the unreliable detectsion + */ +static void add_all_pin_power_ctls(struct hda_codec *codec, bool on) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + if (!codec->power_save_node) + return; + add_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins, on); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + add_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins, on); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + add_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins, on); + for (i = 0; i < cfg->num_inputs; i++) + add_pin_power_ctls(codec, 1, &cfg->inputs[i].pin, on); +} + +/* sync path power up/down with the jack states of given pins */ +static void sync_pin_power_ctls(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins) +{ + int i; + + for (i = 0; i < num_pins && pins[i]; i++) + if (is_jack_detectable(codec, pins[i])) + set_pin_power_jack(codec, pins[i], -1); +} + +/* sync path power up/down with pins; called at init and resume */ +static void sync_all_pin_power_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + if (!codec->power_save_node) + return; + sync_pin_power_ctls(codec, cfg->line_outs, cfg->line_out_pins); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + sync_pin_power_ctls(codec, cfg->hp_outs, cfg->hp_pins); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + sync_pin_power_ctls(codec, cfg->speaker_outs, cfg->speaker_pins); + for (i = 0; i < cfg->num_inputs; i++) + sync_pin_power_ctls(codec, 1, &cfg->inputs[i].pin); +} + +/* add fake paths if not present yet */ +static int add_fake_paths(struct hda_codec *codec, hda_nid_t nid, + int num_pins, const hda_nid_t *pins) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + int i; + + for (i = 0; i < num_pins; i++) { + if (!pins[i]) + break; + if (get_nid_path(codec, nid, pins[i], 0)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + path->depth = 2; + path->path[0] = nid; + path->path[1] = pins[i]; + path->active = true; + } + return 0; +} + +/* create fake paths to all outputs from beep */ +static int add_fake_beep_paths(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t nid = spec->beep_nid; + int err; + + if (!codec->power_save_node || !nid) + return 0; + err = add_fake_paths(codec, nid, cfg->line_outs, cfg->line_out_pins); + if (err < 0) + return err; + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = add_fake_paths(codec, nid, cfg->hp_outs, cfg->hp_pins); + if (err < 0) + return err; + } + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = add_fake_paths(codec, nid, cfg->speaker_outs, + cfg->speaker_pins); + if (err < 0) + return err; + } + return 0; +} + +/* power up/down beep widget and its output paths */ +static void beep_power_hook(struct hda_beep *beep, bool on) +{ + set_path_power(beep->codec, beep->nid, -1, on); +} + +/** + * snd_hda_gen_fix_pin_power - Fix the power of the given pin widget to D0 + * @codec: the HDA codec + * @pin: NID of pin to fix + */ +int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + path->depth = 1; + path->path[0] = pin; + path->active = true; + path->pin_fixed = true; + path->stream_enabled = true; + return 0; +} +EXPORT_SYMBOL_GPL(snd_hda_gen_fix_pin_power); /* * Jack detections for HP auto-mute and mic-switch @@ -3933,6 +4193,10 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, if (!nid) break; + oldval = snd_hda_codec_get_pin_target(codec, nid); + if (oldval & PIN_IN) + continue; /* no mute for inputs */ + if (spec->auto_mute_via_amp) { struct nid_path *path; hda_nid_t mute_nid; @@ -3947,29 +4211,32 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, spec->mute_bits |= (1ULL << mute_nid); else spec->mute_bits &= ~(1ULL << mute_nid); - set_pin_eapd(codec, nid, !mute); continue; + } else { + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) + val = oldval & ~PIN_HP; + else + val = 0; + if (!mute) + val |= oldval; + /* here we call update_pin_ctl() so that the pinctl is + * changed without changing the pinctl target value; + * the original target value will be still referred at + * the init / resume again + */ + update_pin_ctl(codec, nid, val); } - oldval = snd_hda_codec_get_pin_target(codec, nid); - if (oldval & PIN_IN) - continue; /* no mute for inputs */ - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) - val = oldval & ~PIN_HP; - else - val = 0; - if (!mute) - val |= oldval; - /* here we call update_pin_ctl() so that the pinctl is changed - * without changing the pinctl target value; - * the original target value will be still referred at the - * init / resume again - */ - update_pin_ctl(codec, nid, val); set_pin_eapd(codec, nid, !mute); + if (codec->power_save_node) { + bool on = !mute; + if (on) + on = detect_pin_state(codec, nid); + set_path_power(codec, nid, on, -1); + } } } @@ -4436,7 +4703,11 @@ unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (power_state != AC_PWRST_D0 || nid == codec->afg) + struct hda_gen_spec *spec = codec->spec; + + if (!spec->power_down_unused && !codec->power_save_node) + return power_state; + if (power_state != AC_PWRST_D0 || nid == codec->core.afg) return power_state; if (get_wcaps_type(get_wcaps(codec, nid)) >= AC_WID_POWER) return power_state; @@ -4466,6 +4737,21 @@ static void mute_all_mixer_nid(struct hda_codec *codec, hda_nid_t mix) } /** + * snd_hda_gen_stream_pm - Stream power management callback + * @codec: the HDA codec + * @nid: audio widget + * @on: power on/off flag + * + * Set this in patch_ops.stream_pm. Only valid with power_save_node flag. + */ +void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on) +{ + if (codec->power_save_node) + set_path_power(codec, nid, -1, on); +} +EXPORT_SYMBOL_GPL(snd_hda_gen_stream_pm); + +/** * snd_hda_gen_parse_auto_config - Parse the given BIOS configuration and * set up the hda_gen_spec * @codec: the HDA codec @@ -4549,6 +4835,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err; + /* add power-down pin callbacks at first */ + add_all_pin_power_ctls(codec, false); + spec->const_channel_count = spec->ext_channel_count; /* check the multiple speaker and headphone pins */ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) @@ -4618,6 +4907,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, } } + /* add power-up pin callbacks at last */ + add_all_pin_power_ctls(codec, true); + /* mute all aamix input initially */ if (spec->mixer_nid) mute_all_mixer_nid(codec, spec->mixer_nid); @@ -4625,13 +4917,20 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, dig_only: parse_digital(codec); - if (spec->power_down_unused) - codec->power_filter = snd_hda_gen_path_power_filter; + if (spec->power_down_unused || codec->power_save_node) + if (!codec->power_filter) + codec->power_filter = snd_hda_gen_path_power_filter; if (!spec->no_analog && spec->beep_nid) { err = snd_hda_attach_beep_device(codec, spec->beep_nid); if (err < 0) return err; + if (codec->beep && codec->power_save_node) { + err = add_fake_beep_paths(codec); + if (err < 0) + return err; + codec->beep->power_hook = beep_power_hook; + } } return 1; @@ -4675,7 +4974,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); + spec->pcm_rec[1]->pcm_type); if (err < 0) return err; if (!spec->no_analog) { @@ -5137,6 +5436,33 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, strlcat(str, sfx, len); } +/* copy PCM stream info from @default_str, and override non-NULL entries + * from @spec_str and @nid + */ +static void setup_pcm_stream(struct hda_pcm_stream *str, + const struct hda_pcm_stream *default_str, + const struct hda_pcm_stream *spec_str, + hda_nid_t nid) +{ + *str = *default_str; + if (nid) + str->nid = nid; + if (spec_str) { + if (spec_str->substreams) + str->substreams = spec_str->substreams; + if (spec_str->channels_min) + str->channels_min = spec_str->channels_min; + if (spec_str->channels_max) + str->channels_max = spec_str->channels_max; + if (spec_str->rates) + str->rates = spec_str->rates; + if (spec_str->formats) + str->formats = spec_str->formats; + if (spec_str->maxbps) + str->maxbps = spec_str->maxbps; + } +} + /** * snd_hda_gen_build_pcms - build PCM streams based on the parsed results * @codec: the HDA codec @@ -5146,27 +5472,25 @@ static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, int snd_hda_gen_build_pcms(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; + struct hda_pcm *info; bool have_multi_adcs; - codec->num_pcms = 1; - codec->pcm_info = info; - if (spec->no_analog) goto skip_analog; fill_pcm_stream_name(spec->stream_name_analog, sizeof(spec->stream_name_analog), - " Analog", codec->chip_name); - info->name = spec->stream_name_analog; + " Analog", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", spec->stream_name_analog); + if (!info) + return -ENOMEM; + spec->pcm_rec[0] = info; if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_analog_playback, + spec->stream_analog_playback, + spec->multiout.dac_nids[0]); info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels; if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && @@ -5175,15 +5499,11 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) snd_pcm_2_1_chmaps; } if (spec->num_adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + (spec->dyn_adc_switch ? + &dyn_adc_pcm_analog_capture : &pcm_analog_capture), + spec->stream_analog_capture, + spec->adc_nids[0]); } skip_analog: @@ -5191,29 +5511,27 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->multiout.dig_out_nid || spec->dig_in_nid) { fill_pcm_stream_name(spec->stream_name_digital, sizeof(spec->stream_name_digital), - " Digital", codec->chip_name); - codec->num_pcms = 2; + " Digital", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", + spec->stream_name_digital); + if (!info) + return -ENOMEM; codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; + spec->pcm_rec[1] = info; 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) { - p = spec->stream_digital_playback; - if (!p) - p = &pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } + if (spec->multiout.dig_out_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_digital_playback, + spec->stream_digital_playback, + spec->multiout.dig_out_nid); + if (spec->dig_in_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_digital_capture, + spec->stream_digital_capture, + spec->dig_in_nid); } if (spec->no_analog) @@ -5228,35 +5546,30 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->alt_dac_nid || have_multi_adcs) { fill_pcm_stream_name(spec->stream_name_alt_analog, sizeof(spec->stream_name_alt_analog), - " Alt Analog", codec->chip_name); - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_alt_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } + " Alt Analog", codec->core.chip_name); + info = snd_hda_codec_pcm_new(codec, "%s", + spec->stream_name_alt_analog); + if (!info) + return -ENOMEM; + spec->pcm_rec[2] = info; + if (spec->alt_dac_nid) + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_analog_alt_playback, + spec->stream_analog_alt_playback, + spec->alt_dac_nid); + else + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_PLAYBACK], + &pcm_null_stream, NULL, 0); if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_analog_alt_capture, + spec->stream_analog_alt_capture, + spec->adc_nids[1]); info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids - 1; } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; + setup_pcm_stream(&info->stream[SNDRV_PCM_STREAM_CAPTURE], + &pcm_null_stream, NULL, 0); } } @@ -5452,8 +5765,6 @@ int snd_hda_gen_init(struct hda_codec *codec) snd_hda_apply_verbs(codec); - codec->cached_write = 1; - init_multi_out(codec); init_extra_out(codec); init_multi_io(codec); @@ -5464,10 +5775,12 @@ int snd_hda_gen_init(struct hda_codec *codec) clear_unsol_on_unused_pins(codec); + sync_all_pin_power_ctls(codec); + /* call init functions of standard auto-mute helpers */ update_automute_all(codec); - snd_hda_codec_flush_cache(codec); + regcache_sync(codec->core.regmap); if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); @@ -5524,13 +5837,11 @@ static const struct hda_codec_ops generic_patch_ops = { #endif }; -/** +/* * snd_hda_parse_generic_codec - Generic codec parser * @codec: the HDA codec - * - * This should be called from the HDA codec core. */ -int snd_hda_parse_generic_codec(struct hda_codec *codec) +static int snd_hda_parse_generic_codec(struct hda_codec *codec) { struct hda_gen_spec *spec; int err; @@ -5556,7 +5867,17 @@ error: snd_hda_gen_free(codec); return err; } -EXPORT_SYMBOL_GPL(snd_hda_parse_generic_codec); + +static const struct hda_codec_preset snd_hda_preset_generic[] = { + { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec }, + {} /* terminator */ +}; + +static struct hda_codec_driver generic_driver = { + .preset = snd_hda_preset_generic, +}; + +module_hda_codec_driver(generic_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Generic HD-audio codec parser"); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 3d852660443a..56e4139b9032 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -46,7 +46,10 @@ struct nid_path { unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active; + bool active:1; /* activated by driver */ + bool pin_enabled:1; /* pins are enabled */ + bool pin_fixed:1; /* path with fixed pin */ + bool stream_enabled:1; /* stream is active */ }; /* mic/line-in auto switching entry */ @@ -144,7 +147,7 @@ struct hda_gen_spec { int const_channel_count; /* channel count for all */ /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ + struct hda_pcm *pcm_rec[3]; /* used in build_pcms() */ /* dynamic controls, init_verbs and input_mux */ struct auto_pin_cfg autocfg; @@ -340,5 +343,7 @@ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); unsigned int snd_hda_gen_path_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state); +void snd_hda_gen_stream_pm(struct hda_codec *codec, hda_nid_t nid, bool on); +int snd_hda_gen_fix_pin_power(struct hda_codec *codec, hda_nid_t pin); #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 11b5a42b4ec8..57df06e76968 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -101,7 +101,7 @@ int snd_hda_create_hwdep(struct hda_codec *codec) int err; sprintf(hwname, "HDA Codec %d", codec->addr); - err = snd_hwdep_new(codec->bus->card, hwname, codec->addr, &hwdep); + err = snd_hwdep_new(codec->card, hwname, codec->addr, &hwdep); if (err < 0) return err; codec->hwdep = hwdep; @@ -116,9 +116,6 @@ int snd_hda_create_hwdep(struct hda_codec *codec) hwdep->ops.ioctl_compat = hda_hwdep_ioctl_compat; #endif - /* link to codec */ - hwdep->dev.parent = &codec->dev; - /* for sysfs */ hwdep->dev.groups = snd_hda_dev_attr_groups; dev_set_drvdata(&hwdep->dev, codec); diff --git a/sound/pci/hda/hda_i915.c b/sound/pci/hda/hda_i915.c index 714894527e06..52a85d87c23c 100644 --- a/sound/pci/hda/hda_i915.c +++ b/sound/pci/hda/hda_i915.c @@ -22,7 +22,7 @@ #include <linux/component.h> #include <drm/i915_component.h> #include <sound/core.h> -#include "hda_priv.h" +#include "hda_controller.h" #include "hda_intel.h" /* Intel HSW/BDW display HDA controller Extended Mode registers. diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index a8a1e14272a1..e1c210515581 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -62,7 +62,6 @@ #include <linux/firmware.h> #include "hda_codec.h" #include "hda_controller.h" -#include "hda_priv.h" #include "hda_intel.h" /* position fix mode */ @@ -174,7 +173,6 @@ static struct kernel_param_ops param_ops_xint = { #define param_check_xint param_check_int static int power_save = CONFIG_SND_HDA_POWER_SAVE_DEFAULT; -static int *power_save_addr = &power_save; module_param(power_save, xint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout " "(in second, 0 = disable)."); @@ -187,7 +185,7 @@ static bool power_save_controller = 1; module_param(power_save_controller, bool, 0644); MODULE_PARM_DESC(power_save_controller, "Reset controller in power save mode."); #else -static int *power_save_addr; +#define power_save 0 #endif /* CONFIG_PM */ static int align_buffer_size = -1; @@ -299,8 +297,12 @@ enum { AZX_DCAPS_PM_RUNTIME | AZX_DCAPS_I915_POWERWELL |\ AZX_DCAPS_SNOOP_TYPE(SCH)) +#define AZX_DCAPS_INTEL_BRASWELL \ + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_I915_POWERWELL) + #define AZX_DCAPS_INTEL_SKYLAKE \ - (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG) + (AZX_DCAPS_INTEL_PCH | AZX_DCAPS_SEPARATE_STREAM_TAG |\ + AZX_DCAPS_I915_POWERWELL) /* quirks for ATI SB / AMD Hudson */ #define AZX_DCAPS_PRESET_ATI_SB \ @@ -530,10 +532,10 @@ static int azx_position_check(struct azx *chip, struct azx_dev *azx_dev) if (ok == 1) { azx_dev->irq_pending = 0; return ok; - } else if (ok == 0 && chip->bus && chip->bus->workq) { + } else if (ok == 0) { /* bogus IRQ, process it later */ azx_dev->irq_pending = 1; - queue_work(chip->bus->workq, &hda->irq_pending_work); + schedule_work(&hda->irq_pending_work); } return 0; } @@ -741,7 +743,6 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) { struct hda_intel *hda; struct azx *chip; - struct hda_codec *c; int prev = power_save; int ret = param_set_int(val, kp); @@ -753,8 +754,7 @@ static int param_set_xint(const char *val, const struct kernel_param *kp) chip = &hda->chip; if (!chip->bus || chip->disabled) continue; - list_for_each_entry(c, &chip->bus->codec_list, list) - snd_hda_power_sync(c); + snd_hda_set_power_save(chip->bus, power_save * 1000); } mutex_unlock(&card_list_lock); return 0; @@ -773,7 +773,6 @@ static int azx_suspend(struct device *dev) struct snd_card *card = dev_get_drvdata(dev); struct azx *chip; struct hda_intel *hda; - struct azx_pcm *p; if (!card) return 0; @@ -785,10 +784,6 @@ static int azx_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); azx_clear_irq_pending(chip); - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - if (chip->initialized) - snd_hda_suspend(chip->bus); azx_stop_chip(chip); azx_enter_link_reset(chip); if (chip->irq >= 0) { @@ -831,7 +826,6 @@ static int azx_resume(struct device *dev) azx_init_chip(chip, true); - snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } @@ -852,7 +846,7 @@ static int azx_runtime_suspend(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!azx_has_pm_runtime(chip)) return 0; /* enable controller wake up event */ @@ -885,7 +879,7 @@ static int azx_runtime_resume(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!azx_has_pm_runtime(chip)) return 0; if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { @@ -901,10 +895,10 @@ static int azx_runtime_resume(struct device *dev) bus = chip->bus; if (status && bus) { - list_for_each_entry(codec, &bus->codec_list, list) + list_for_each_codec(codec, bus) if (status & (1 << codec->addr)) - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, codec->jackpoll_interval); + schedule_delayed_work(&codec->jackpoll_work, + codec->jackpoll_interval); } /* disable controller Wake Up event*/ @@ -928,8 +922,8 @@ static int azx_runtime_idle(struct device *dev) if (chip->disabled || hda->init_failed) return 0; - if (!power_save_controller || - !(chip->driver_caps & AZX_DCAPS_PM_RUNTIME)) + if (!power_save_controller || !azx_has_pm_runtime(chip) || + chip->bus->core.codec_powered) return -EBUSY; return 0; @@ -1071,14 +1065,11 @@ static int azx_free(struct azx *chip) struct hda_intel *hda = container_of(chip, struct hda_intel, chip); int i; - if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) - && chip->running) + if (azx_has_pm_runtime(chip) && chip->running) pm_runtime_get_noresume(&pci->dev); azx_del_card_list(chip); - azx_notifier_unregister(chip); - hda->init_failed = 1; /* to be sure */ complete_all(&hda->probe_wait); @@ -1394,7 +1385,6 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, hda = kzalloc(sizeof(*hda), GFP_KERNEL); if (!hda) { - dev_err(card->dev, "Cannot allocate hda\n"); pci_disable_device(pci); return -ENOMEM; } @@ -1575,10 +1565,8 @@ static int azx_first_init(struct azx *chip) chip->num_streams = chip->playback_streams + chip->capture_streams; chip->azx_dev = kcalloc(chip->num_streams, sizeof(*chip->azx_dev), GFP_KERNEL); - if (!chip->azx_dev) { - dev_err(card->dev, "cannot malloc azx_dev\n"); + if (!chip->azx_dev) return -ENOMEM; - } err = azx_alloc_stream_pages(chip); if (err < 0) @@ -1615,19 +1603,6 @@ static int azx_first_init(struct azx *chip) return 0; } -static void power_down_all_codecs(struct azx *chip) -{ -#ifdef CONFIG_PM - /* The codecs were powered up in snd_hda_codec_new(). - * Now all initialization done, so turn them down if possible - */ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) { - snd_hda_power_down(codec); - } -#endif -} - #ifdef CONFIG_SND_HDA_PATCH_LOADER /* callback from request_firmware_nowait() */ static void azx_firmware_cb(const struct firmware *fw, void *context) @@ -1896,12 +1871,14 @@ static int azx_probe_continue(struct azx *chip) #endif /* create codec instances */ - err = azx_codec_create(chip, model[dev], - azx_max_codecs[chip->driver_type], - power_save_addr); + err = azx_bus_create(chip, model[dev]); + if (err < 0) + goto out_free; + err = azx_probe_codecs(chip, azx_max_codecs[chip->driver_type]); if (err < 0) goto out_free; + #ifdef CONFIG_SND_HDA_PATCH_LOADER if (chip->fw) { err = snd_hda_load_patch(chip->bus, chip->fw->size, @@ -1920,25 +1897,14 @@ static int azx_probe_continue(struct azx *chip) goto out_free; } - /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = azx_mixer_create(chip); - if (err < 0) - goto out_free; - err = snd_card_register(chip->card); if (err < 0) goto out_free; chip->running = 1; - power_down_all_codecs(chip); - azx_notifier_register(chip); azx_add_card_list(chip); - if ((chip->driver_caps & AZX_DCAPS_PM_RUNTIME) || hda->use_vga_switcheroo) + snd_hda_set_power_save(chip->bus, power_save * 1000); + if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo) pm_runtime_put_noidle(&pci->dev); out_free: @@ -1956,6 +1922,18 @@ static void azx_remove(struct pci_dev *pci) snd_card_free(card); } +static void azx_shutdown(struct pci_dev *pci) +{ + struct snd_card *card = pci_get_drvdata(pci); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + /* PCI IDs */ static const struct pci_device_id azx_ids[] = { /* CPT */ @@ -2017,7 +1995,7 @@ static const struct pci_device_id azx_ids[] = { .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM }, /* Braswell */ { PCI_DEVICE(0x8086, 0x2284), - .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH }, + .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL }, /* ICH6 */ { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH }, @@ -2178,6 +2156,7 @@ static struct pci_driver azx_driver = { .id_table = azx_ids, .probe = azx_probe, .remove = azx_remove, + .shutdown = azx_shutdown, .driver = { .pm = AZX_PM_OPS, }, diff --git a/sound/pci/hda/hda_intel.h b/sound/pci/hda/hda_intel.h index 348611835476..d5231f7216a7 100644 --- a/sound/pci/hda/hda_intel.h +++ b/sound/pci/hda/hda_intel.h @@ -17,7 +17,7 @@ #define __SOUND_HDA_INTEL_H #include <drm/i915_component.h> -#include "hda_priv.h" +#include "hda_controller.h" struct hda_intel { struct azx chip; diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index e664307617bd..d7cfe7b8c32b 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -135,7 +135,7 @@ void snd_hda_jack_tbl_clear(struct hda_codec *codec) #ifdef CONFIG_SND_HDA_INPUT_JACK /* free jack instances manually when clearing/reconfiguring */ if (!codec->bus->shutdown && jack->jack) - snd_device_free(codec->bus->card, jack->jack); + snd_device_free(codec->card, jack->jack); #endif for (cb = jack->callback; cb; cb = next) { next = cb->next; @@ -340,7 +340,7 @@ void snd_hda_jack_report_sync(struct hda_codec *codec) if (!jack->kctl || jack->block_report) continue; state = get_jack_plug_state(jack->pin_sense); - snd_kctl_jack_report(codec->bus->card, jack->kctl, state); + snd_kctl_jack_report(codec->card, jack->kctl, state); #ifdef CONFIG_SND_HDA_INPUT_JACK if (jack->jack) snd_jack_report(jack->jack, @@ -412,11 +412,11 @@ static int __snd_hda_jack_add_kctl(struct hda_codec *codec, hda_nid_t nid, jack->phantom_jack = !!phantom_jack; state = snd_hda_jack_detect(codec, nid); - snd_kctl_jack_report(codec->bus->card, kctl, state); + snd_kctl_jack_report(codec->card, kctl, state); #ifdef CONFIG_SND_HDA_INPUT_JACK if (!phantom_jack) { jack->type = get_input_jack_type(codec, nid); - err = snd_jack_new(codec->bus->card, name, jack->type, + err = snd_jack_new(codec->card, name, jack->type, &jack->jack); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 62658f2f8c9f..3b567f42296b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -127,18 +127,16 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); #endif /* lowlevel accessor with caching; use carefully */ -int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index); -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val); +#define snd_hda_codec_amp_read(codec, nid, ch, dir, idx) \ + snd_hdac_regmap_get_amp(&(codec)->core, nid, ch, dir, idx) +#define snd_hda_codec_amp_update(codec, nid, ch, dir, idx, mask, val) \ + snd_hdac_regmap_update_amp(&(codec)->core, nid, ch, dir, idx, mask, val) int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -void snd_hda_codec_resume_amp(struct hda_codec *codec); - void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, @@ -150,6 +148,8 @@ int __snd_hda_add_vmaster(struct hda_codec *codec, char *name, #define snd_hda_add_vmaster(codec, name, tlv, slaves, suffix) \ __snd_hda_add_vmaster(codec, name, tlv, slaves, suffix, true, NULL) int snd_hda_codec_reset(struct hda_codec *codec); +void snd_hda_codec_register(struct hda_codec *codec); +void snd_hda_codec_cleanup_for_unbind(struct hda_codec *codec); enum { HDA_VMUTE_OFF, @@ -273,29 +273,6 @@ int snd_hda_add_imux_item(struct hda_codec *codec, int index, int *type_index_ret); /* - * Channel mode helper - */ -struct hda_channel_mode { - int channels; - const struct hda_verb *sequence; -}; - -int snd_hda_ch_mode_info(struct hda_codec *codec, - struct snd_ctl_elem_info *uinfo, - const struct hda_channel_mode *chmode, - int num_chmodes); -int snd_hda_ch_mode_get(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int max_channels); -int snd_hda_ch_mode_put(struct hda_codec *codec, - struct snd_ctl_elem_value *ucontrol, - const struct hda_channel_mode *chmode, - int num_chmodes, - int *max_channelsp); - -/* * Multi-channel / digital-out PCM helper */ @@ -351,12 +328,6 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec, struct hda_multi_out *mout); /* - * generic codec parser - */ -int snd_hda_parse_generic_codec(struct hda_codec *codec); -int snd_hda_parse_hdmi_codec(struct hda_codec *codec); - -/* * generic proc interface */ #ifdef CONFIG_PROC_FS @@ -466,23 +437,6 @@ void snd_hda_pick_pin_fixup(struct hda_codec *codec, const struct snd_hda_pin_quirk *pin_quirk, const struct hda_fixup *fixlist); - -/* - * unsolicited event handler - */ - -#define HDA_UNSOL_QUEUE_SIZE 64 - -struct hda_bus_unsolicited { - /* ring buffer */ - u32 queue[HDA_UNSOL_QUEUE_SIZE * 2]; - unsigned int rp, wp; - - /* workqueue */ - struct work_struct work; - struct hda_bus *bus; -}; - /* helper macros to retrieve pin default-config values */ #define get_defcfg_connect(cfg) \ ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT) @@ -560,15 +514,18 @@ int snd_hda_codec_get_pin_target(struct hda_codec *codec, hda_nid_t nid); int snd_hda_codec_set_pin_target(struct hda_codec *codec, hda_nid_t nid, unsigned int val); +#define for_each_hda_codec_node(nid, codec) \ + for ((nid) = (codec)->core.start_nid; (nid) < (codec)->core.end_nid; (nid)++) + /* * get widget capabilities */ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid) { - if (nid < codec->start_nid || - nid >= codec->start_nid + codec->num_nodes) + if (nid < codec->core.start_nid || + nid >= codec->core.start_nid + codec->core.num_nodes) return 0; - return codec->wcaps[nid - codec->start_nid]; + return codec->wcaps[nid - codec->core.start_nid]; } /* get the widget type from widget capability bits */ @@ -592,17 +549,49 @@ static inline unsigned int get_wcaps_channels(u32 wcaps) static inline void snd_hda_override_wcaps(struct hda_codec *codec, hda_nid_t nid, u32 val) { - if (nid >= codec->start_nid && - nid < codec->start_nid + codec->num_nodes) - codec->wcaps[nid - codec->start_nid] = val; + if (nid >= codec->core.start_nid && + nid < codec->core.start_nid + codec->core.num_nodes) + codec->wcaps[nid - codec->core.start_nid] = val; } 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_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, - unsigned int caps); +/** + * snd_hda_query_pin_caps - Query PIN capabilities + * @codec: the HD-auio codec + * @nid: the NID to query + * + * Query PIN capabilities for the given widget. + * Returns the obtained capability bits. + * + * When cap bits have been already read, this doesn't read again but + * returns the cached value. + */ +static inline u32 +snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid) +{ + return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + +} + +/** + * snd_hda_override_pin_caps - Override the pin capabilities + * @codec: the CODEC + * @nid: the NID to override + * @caps: the capability bits to set + * + * Override the cached PIN capabilitiy bits value by the given one. + * + * Returns zero if successful or a negative error code. + */ +static inline int +snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid, + unsigned int caps) +{ + return snd_hdac_override_parm(&codec->core, nid, AC_PAR_PIN_CAP, caps); +} + bool snd_hda_check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int bits); @@ -800,9 +789,13 @@ void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen); /* */ -#define codec_err(codec, fmt, args...) dev_err(&(codec)->dev, fmt, ##args) -#define codec_warn(codec, fmt, args...) dev_warn(&(codec)->dev, fmt, ##args) -#define codec_info(codec, fmt, args...) dev_info(&(codec)->dev, fmt, ##args) -#define codec_dbg(codec, fmt, args...) dev_dbg(&(codec)->dev, fmt, ##args) +#define codec_err(codec, fmt, args...) \ + dev_err(hda_codec_dev(codec), fmt, ##args) +#define codec_warn(codec, fmt, args...) \ + dev_warn(hda_codec_dev(codec), fmt, ##args) +#define codec_info(codec, fmt, args...) \ + dev_info(hda_codec_dev(codec), fmt, ##args) +#define codec_dbg(codec, fmt, args...) \ + dev_dbg(hda_codec_dev(codec), fmt, ##args) #endif /* __SOUND_HDA_LOCAL_H */ diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h deleted file mode 100644 index daf458299753..000000000000 --- a/sound/pci/hda/hda_priv.h +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Common defines for the alsa driver code base for HD Audio. - * - * 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. - */ - -#ifndef __SOUND_HDA_PRIV_H -#define __SOUND_HDA_PRIV_H - -#include <linux/timecounter.h> -#include <sound/core.h> -#include <sound/pcm.h> - -/* - * registers - */ -#define AZX_REG_GCAP 0x00 -#define AZX_GCAP_64OK (1 << 0) /* 64bit address support */ -#define AZX_GCAP_NSDO (3 << 1) /* # of serial data out signals */ -#define AZX_GCAP_BSS (31 << 3) /* # of bidirectional streams */ -#define AZX_GCAP_ISS (15 << 8) /* # of input streams */ -#define AZX_GCAP_OSS (15 << 12) /* # of output streams */ -#define AZX_REG_VMIN 0x02 -#define AZX_REG_VMAJ 0x03 -#define AZX_REG_OUTPAY 0x04 -#define AZX_REG_INPAY 0x06 -#define AZX_REG_GCTL 0x08 -#define AZX_GCTL_RESET (1 << 0) /* controller reset */ -#define AZX_GCTL_FCNTRL (1 << 1) /* flush control */ -#define AZX_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */ -#define AZX_REG_WAKEEN 0x0c -#define AZX_REG_STATESTS 0x0e -#define AZX_REG_GSTS 0x10 -#define AZX_GSTS_FSTS (1 << 1) /* flush status */ -#define AZX_REG_INTCTL 0x20 -#define AZX_REG_INTSTS 0x24 -#define AZX_REG_WALLCLK 0x30 /* 24Mhz source */ -#define AZX_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */ -#define AZX_REG_SSYNC 0x38 -#define AZX_REG_CORBLBASE 0x40 -#define AZX_REG_CORBUBASE 0x44 -#define AZX_REG_CORBWP 0x48 -#define AZX_REG_CORBRP 0x4a -#define AZX_CORBRP_RST (1 << 15) /* read pointer reset */ -#define AZX_REG_CORBCTL 0x4c -#define AZX_CORBCTL_RUN (1 << 1) /* enable DMA */ -#define AZX_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */ -#define AZX_REG_CORBSTS 0x4d -#define AZX_CORBSTS_CMEI (1 << 0) /* memory error indication */ -#define AZX_REG_CORBSIZE 0x4e - -#define AZX_REG_RIRBLBASE 0x50 -#define AZX_REG_RIRBUBASE 0x54 -#define AZX_REG_RIRBWP 0x58 -#define AZX_RIRBWP_RST (1 << 15) /* write pointer reset */ -#define AZX_REG_RINTCNT 0x5a -#define AZX_REG_RIRBCTL 0x5c -#define AZX_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */ -#define AZX_RBCTL_DMA_EN (1 << 1) /* enable DMA */ -#define AZX_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */ -#define AZX_REG_RIRBSTS 0x5d -#define AZX_RBSTS_IRQ (1 << 0) /* response irq */ -#define AZX_RBSTS_OVERRUN (1 << 2) /* overrun irq */ -#define AZX_REG_RIRBSIZE 0x5e - -#define AZX_REG_IC 0x60 -#define AZX_REG_IR 0x64 -#define AZX_REG_IRS 0x68 -#define AZX_IRS_VALID (1<<1) -#define AZX_IRS_BUSY (1<<0) - -#define AZX_REG_DPLBASE 0x70 -#define AZX_REG_DPUBASE 0x74 -#define AZX_DPLBASE_ENABLE 0x1 /* Enable position buffer */ - -/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */ -enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 }; - -/* stream register offsets from stream base */ -#define AZX_REG_SD_CTL 0x00 -#define AZX_REG_SD_STS 0x03 -#define AZX_REG_SD_LPIB 0x04 -#define AZX_REG_SD_CBL 0x08 -#define AZX_REG_SD_LVI 0x0c -#define AZX_REG_SD_FIFOW 0x0e -#define AZX_REG_SD_FIFOSIZE 0x10 -#define AZX_REG_SD_FORMAT 0x12 -#define AZX_REG_SD_BDLPL 0x18 -#define AZX_REG_SD_BDLPU 0x1c - -/* PCI space */ -#define AZX_PCIREG_TCSEL 0x44 - -/* - * other constants - */ - -/* max number of fragments - we may use more if allocating more pages for BDL */ -#define BDL_SIZE 4096 -#define AZX_MAX_BDL_ENTRIES (BDL_SIZE / 16) -#define AZX_MAX_FRAG 32 -/* max buffer size - no h/w limit, you can increase as you like */ -#define AZX_MAX_BUF_SIZE (1024*1024*1024) - -/* RIRB int mask: overrun[2], response[0] */ -#define RIRB_INT_RESPONSE 0x01 -#define RIRB_INT_OVERRUN 0x04 -#define RIRB_INT_MASK 0x05 - -/* STATESTS int mask: S3,SD2,SD1,SD0 */ -#define AZX_MAX_CODECS 8 -#define AZX_DEFAULT_CODECS 4 -#define STATESTS_INT_MASK ((1 << AZX_MAX_CODECS) - 1) - -/* SD_CTL bits */ -#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */ -#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */ -#define SD_CTL_STRIPE (3 << 16) /* stripe control */ -#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */ -#define SD_CTL_DIR (1 << 19) /* bi-directional stream */ -#define SD_CTL_STREAM_TAG_MASK (0xf << 20) -#define SD_CTL_STREAM_TAG_SHIFT 20 - -/* SD_CTL and SD_STS */ -#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */ -#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */ -#define SD_INT_COMPLETE 0x04 /* completion interrupt */ -#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\ - SD_INT_COMPLETE) - -/* SD_STS */ -#define SD_STS_FIFO_READY 0x20 /* FIFO ready */ - -/* INTCTL and INTSTS */ -#define AZX_INT_ALL_STREAM 0xff /* all stream interrupts */ -#define AZX_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */ -#define AZX_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */ - -/* below are so far hardcoded - should read registers in future */ -#define AZX_MAX_CORB_ENTRIES 256 -#define AZX_MAX_RIRB_ENTRIES 256 - -/* driver quirks (capabilities) */ -/* bits 0-7 are used for indicating driver type */ -#define AZX_DCAPS_NO_TCSEL (1 << 8) /* No Intel TCSEL bit */ -#define AZX_DCAPS_NO_MSI (1 << 9) /* No MSI support */ -#define AZX_DCAPS_SNOOP_MASK (3 << 10) /* snoop type mask */ -#define AZX_DCAPS_SNOOP_OFF (1 << 12) /* snoop default off */ -#define AZX_DCAPS_RIRB_DELAY (1 << 13) /* Long delay in read loop */ -#define AZX_DCAPS_RIRB_PRE_DELAY (1 << 14) /* Put a delay before read */ -#define AZX_DCAPS_CTX_WORKAROUND (1 << 15) /* X-Fi workaround */ -#define AZX_DCAPS_POSFIX_LPIB (1 << 16) /* Use LPIB as default */ -#define AZX_DCAPS_POSFIX_VIA (1 << 17) /* Use VIACOMBO as default */ -#define AZX_DCAPS_NO_64BIT (1 << 18) /* No 64bit address */ -#define AZX_DCAPS_SYNC_WRITE (1 << 19) /* sync each cmd write */ -#define AZX_DCAPS_OLD_SSYNC (1 << 20) /* Old SSYNC reg for ICH */ -#define AZX_DCAPS_NO_ALIGN_BUFSIZE (1 << 21) /* no buffer size alignment */ -/* 22 unused */ -#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23) /* BDLE in 4k boundary */ -#define AZX_DCAPS_REVERSE_ASSIGN (1 << 24) /* Assign devices in reverse order */ -#define AZX_DCAPS_COUNT_LPIB_DELAY (1 << 25) /* Take LPIB as delay */ -#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */ -#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */ -#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */ -#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */ -#define AZX_DCAPS_SEPARATE_STREAM_TAG (1 << 30) /* capture and playback use separate stream tag */ - -enum { - AZX_SNOOP_TYPE_NONE , - AZX_SNOOP_TYPE_SCH, - AZX_SNOOP_TYPE_ATI, - AZX_SNOOP_TYPE_NVIDIA, -}; - -/* HD Audio class code */ -#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 - -struct azx_dev { - struct snd_dma_buffer bdl; /* BDL buffer */ - u32 *posbuf; /* position buffer pointer */ - - unsigned int bufsize; /* size of the play buffer in bytes */ - unsigned int period_bytes; /* size of the period in bytes */ - unsigned int frags; /* number for period in the play buffer */ - unsigned int fifo_size; /* FIFO size */ - unsigned long start_wallclk; /* start + minimum wallclk */ - unsigned long period_wallclk; /* wallclk for period */ - - void __iomem *sd_addr; /* stream descriptor pointer */ - - u32 sd_int_sta_mask; /* stream int status mask */ - - /* pcm support */ - struct snd_pcm_substream *substream; /* assigned substream, - * set in PCM open - */ - unsigned int format_val; /* format value to be set in the - * controller and the codec - */ - unsigned char stream_tag; /* assigned stream */ - unsigned char index; /* stream index */ - int assigned_key; /* last device# key assigned to */ - - unsigned int opened:1; - unsigned int running:1; - unsigned int irq_pending:1; - unsigned int prepared:1; - unsigned int locked:1; - /* - * For VIA: - * A flag to ensure DMA position is 0 - * when link position is not greater than FIFO size - */ - unsigned int insufficient:1; - unsigned int wc_marked:1; - unsigned int no_period_wakeup:1; - - struct timecounter azx_tc; - struct cyclecounter azx_cc; - - int delay_negative_threshold; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - /* Allows dsp load to have sole access to the playback stream. */ - struct mutex dsp_mutex; -#endif -}; - -/* CORB/RIRB */ -struct azx_rb { - u32 *buf; /* CORB/RIRB buffer - * Each CORB entry is 4byte, RIRB is 8byte - */ - dma_addr_t addr; /* physical address of CORB/RIRB buffer */ - /* for RIRB */ - unsigned short rp, wp; /* read/write pointers */ - int cmds[AZX_MAX_CODECS]; /* number of pending requests */ - u32 res[AZX_MAX_CODECS]; /* last read value */ -}; - -struct azx; - -/* Functions to read/write to hda registers. */ -struct hda_controller_ops { - /* Register Access */ - void (*reg_writel)(u32 value, u32 __iomem *addr); - u32 (*reg_readl)(u32 __iomem *addr); - void (*reg_writew)(u16 value, u16 __iomem *addr); - u16 (*reg_readw)(u16 __iomem *addr); - void (*reg_writeb)(u8 value, u8 __iomem *addr); - u8 (*reg_readb)(u8 __iomem *addr); - /* Disable msi if supported, PCI only */ - int (*disable_msi_reset_irq)(struct azx *); - /* Allocation ops */ - int (*dma_alloc_pages)(struct azx *chip, - int type, - size_t size, - struct snd_dma_buffer *buf); - void (*dma_free_pages)(struct azx *chip, struct snd_dma_buffer *buf); - int (*substream_alloc_pages)(struct azx *chip, - struct snd_pcm_substream *substream, - size_t size); - int (*substream_free_pages)(struct azx *chip, - struct snd_pcm_substream *substream); - void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, - struct vm_area_struct *area); - /* Check if current position is acceptable */ - int (*position_check)(struct azx *chip, struct azx_dev *azx_dev); -}; - -struct azx_pcm { - struct azx *chip; - struct snd_pcm *pcm; - struct hda_codec *codec; - struct hda_pcm_stream *hinfo[2]; - struct list_head list; -}; - -typedef unsigned int (*azx_get_pos_callback_t)(struct azx *, struct azx_dev *); -typedef int (*azx_get_delay_callback_t)(struct azx *, struct azx_dev *, unsigned int pos); - -struct azx { - struct snd_card *card; - struct pci_dev *pci; - int dev_index; - - /* chip type specific */ - int driver_type; - unsigned int driver_caps; - int playback_streams; - int playback_index_offset; - int capture_streams; - int capture_index_offset; - int num_streams; - const int *jackpoll_ms; /* per-card jack poll interval */ - - /* Register interaction. */ - const struct hda_controller_ops *ops; - - /* position adjustment callbacks */ - azx_get_pos_callback_t get_position[2]; - azx_get_delay_callback_t get_delay[2]; - - /* pci resources */ - unsigned long addr; - void __iomem *remap_addr; - int irq; - - /* locks */ - spinlock_t reg_lock; - struct mutex open_mutex; /* Prevents concurrent open/close operations */ - - /* streams (x num_streams) */ - struct azx_dev *azx_dev; - - /* PCM */ - struct list_head pcm_list; /* azx_pcm list */ - - /* HD codec */ - unsigned short codec_mask; - int codec_probe_mask; /* copied from probe_mask option */ - struct hda_bus *bus; - unsigned int beep_mode; - - /* CORB/RIRB */ - struct azx_rb corb; - struct azx_rb rirb; - - /* CORB/RIRB and position buffers */ - struct snd_dma_buffer rb; - struct snd_dma_buffer posbuf; - -#ifdef CONFIG_SND_HDA_PATCH_LOADER - const struct firmware *fw; -#endif - - /* flags */ - const int *bdl_pos_adj; - int poll_count; - unsigned int running:1; - unsigned int initialized:1; - unsigned int single_cmd:1; - unsigned int polling_mode:1; - unsigned int msi:1; - unsigned int probing:1; /* codec probing phase */ - unsigned int snoop:1; - unsigned int align_buffer_size:1; - unsigned int region_requested:1; - unsigned int disabled:1; /* disabled by VGA-switcher */ - - /* for debugging */ - unsigned int last_cmd[AZX_MAX_CODECS]; - - /* reboot notifier (for mysterious hangup problem at power-down) */ - struct notifier_block reboot_notifier; - -#ifdef CONFIG_SND_HDA_DSP_LOADER - struct azx_dev saved_azx_dev; -#endif -}; - -#ifdef CONFIG_X86 -#define azx_snoop(chip) ((chip)->snoop) -#else -#define azx_snoop(chip) true -#endif - -/* - * macros for easy use - */ - -#define azx_writel(chip, reg, value) \ - ((chip)->ops->reg_writel(value, (chip)->remap_addr + AZX_REG_##reg)) -#define azx_readl(chip, reg) \ - ((chip)->ops->reg_readl((chip)->remap_addr + AZX_REG_##reg)) -#define azx_writew(chip, reg, value) \ - ((chip)->ops->reg_writew(value, (chip)->remap_addr + AZX_REG_##reg)) -#define azx_readw(chip, reg) \ - ((chip)->ops->reg_readw((chip)->remap_addr + AZX_REG_##reg)) -#define azx_writeb(chip, reg, value) \ - ((chip)->ops->reg_writeb(value, (chip)->remap_addr + AZX_REG_##reg)) -#define azx_readb(chip, reg) \ - ((chip)->ops->reg_readb((chip)->remap_addr + AZX_REG_##reg)) - -#define azx_sd_writel(chip, dev, reg, value) \ - ((chip)->ops->reg_writel(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readl(chip, dev, reg) \ - ((chip)->ops->reg_readl((dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_writew(chip, dev, reg, value) \ - ((chip)->ops->reg_writew(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readw(chip, dev, reg) \ - ((chip)->ops->reg_readw((dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_writeb(chip, dev, reg, value) \ - ((chip)->ops->reg_writeb(value, (dev)->sd_addr + AZX_REG_##reg)) -#define azx_sd_readb(chip, dev, reg) \ - ((chip)->ops->reg_readb((dev)->sd_addr + AZX_REG_##reg)) - -#endif /* __SOUND_HDA_PRIV_H */ diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 05e19f78b4cb..ee6230767c64 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -32,6 +32,10 @@ static int dump_coef = -1; module_param(dump_coef, int, 0644); MODULE_PARM_DESC(dump_coef, "Dump processing coefficients in codec proc file (-1=auto, 0=disable, 1=enable)"); +/* always use noncached version */ +#define param_read(codec, nid, parm) \ + snd_hdac_read_parm_uncached(&(codec)->core, nid, parm) + static char *bits_names(unsigned int bits, char *names[], int size) { int i, n; @@ -99,10 +103,10 @@ static void print_nid_array(struct snd_info_buffer *buffer, static void print_nid_pcms(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - int pcm, type; + int type; struct hda_pcm *cpcm; - for (pcm = 0; pcm < codec->num_pcms; pcm++) { - cpcm = &codec->pcm_info[pcm]; + + list_for_each_entry(cpcm, &codec->pcm_list_head, list) { for (type = 0; type < 2; type++) { if (cpcm->stream[type].nid != nid || cpcm->pcm == NULL) continue; @@ -119,9 +123,8 @@ static void print_amp_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid, int dir) { unsigned int caps; - caps = snd_hda_param_read(codec, nid, - dir == HDA_OUTPUT ? - AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); + caps = param_read(codec, nid, dir == HDA_OUTPUT ? + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP); if (caps == -1 || caps == 0) { snd_iprintf(buffer, "N/A\n"); return; @@ -225,8 +228,8 @@ static void print_pcm_formats(struct snd_info_buffer *buffer, static void print_pcm_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int pcm = snd_hda_param_read(codec, nid, AC_PAR_PCM); - unsigned int stream = snd_hda_param_read(codec, nid, AC_PAR_STREAM); + unsigned int pcm = param_read(codec, nid, AC_PAR_PCM); + unsigned int stream = param_read(codec, nid, AC_PAR_STREAM); if (pcm == -1 || stream == -1) { snd_iprintf(buffer, "N/A\n"); return; @@ -273,7 +276,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, static char *jack_conns[4] = { "Jack", "N/A", "Fixed", "Both" }; unsigned int caps, val; - caps = snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP); + caps = param_read(codec, nid, AC_PAR_PIN_CAP); snd_iprintf(buffer, " Pincap 0x%08x:", caps); if (caps & AC_PINCAP_IN) snd_iprintf(buffer, " IN"); @@ -289,7 +292,7 @@ static void print_pin_caps(struct snd_info_buffer *buffer, snd_iprintf(buffer, " Balanced"); if (caps & AC_PINCAP_HDMI) { /* Realtek uses this bit as a different meaning */ - if ((codec->vendor_id >> 16) == 0x10ec) + if ((codec->core.vendor_id >> 16) == 0x10ec) snd_iprintf(buffer, " R/L"); else { if (caps & AC_PINCAP_HBR) @@ -401,8 +404,7 @@ static void print_pin_ctls(struct snd_info_buffer *buffer, static void print_vol_knob(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - unsigned int cap = snd_hda_param_read(codec, nid, - AC_PAR_VOL_KNB_CAP); + unsigned int cap = param_read(codec, nid, AC_PAR_VOL_KNB_CAP); snd_iprintf(buffer, " Volume-Knob: delta=%d, steps=%d, ", (cap >> 7) & 1, cap & 0x7f); cap = snd_hda_codec_read(codec, nid, 0, @@ -487,7 +489,7 @@ static void print_power_state(struct snd_info_buffer *buffer, [ilog2(AC_PWRST_EPSS)] = "EPSS", }; - int sup = snd_hda_param_read(codec, nid, AC_PAR_POWER_STATE); + int sup = param_read(codec, nid, AC_PAR_POWER_STATE); int pwr = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_POWER_STATE, 0); if (sup != -1) @@ -531,8 +533,7 @@ static void print_proc_caps(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int i, ncoeff, oldindex; - unsigned int proc_caps = snd_hda_param_read(codec, nid, - AC_PAR_PROC_CAP); + unsigned int proc_caps = param_read(codec, nid, AC_PAR_PROC_CAP); ncoeff = (proc_caps & AC_PCAP_NUM_COEF) >> AC_PCAP_NUM_COEF_SHIFT; snd_iprintf(buffer, " Processing caps: benign=%d, ncoeff=%d\n", proc_caps & AC_PCAP_BENIGN, ncoeff); @@ -597,7 +598,7 @@ static void print_gpio(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { unsigned int gpio = - snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); unsigned int enable, direction, wake, unsol, sticky, data; int i, max; snd_iprintf(buffer, "GPIO: io=%d, o=%d, i=%d, " @@ -667,13 +668,9 @@ static void print_device_list(struct snd_info_buffer *buffer, } } -static void print_codec_info(struct snd_info_entry *entry, - struct snd_info_buffer *buffer) +static void print_codec_core_info(struct hdac_device *codec, + struct snd_info_buffer *buffer) { - struct hda_codec *codec = entry->private_data; - hda_nid_t nid; - int i, nodes; - snd_iprintf(buffer, "Codec: "); if (codec->vendor_name && codec->chip_name) snd_iprintf(buffer, "%s %s\n", @@ -695,34 +692,43 @@ static void print_codec_info(struct snd_info_entry *entry, snd_iprintf(buffer, "Modem Function Group: 0x%x\n", codec->mfg); else snd_iprintf(buffer, "No Modem Function Group found\n"); +} + +static void print_codec_info(struct snd_info_entry *entry, + struct snd_info_buffer *buffer) +{ + struct hda_codec *codec = entry->private_data; + hda_nid_t nid, fg; + int i, nodes; - if (! codec->afg) + print_codec_core_info(&codec->core, buffer); + fg = codec->core.afg; + if (!fg) return; snd_hda_power_up(codec); snd_iprintf(buffer, "Default PCM:\n"); - print_pcm_caps(buffer, codec, codec->afg); + print_pcm_caps(buffer, codec, fg); snd_iprintf(buffer, "Default Amp-In caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_INPUT); + print_amp_caps(buffer, codec, fg, HDA_INPUT); snd_iprintf(buffer, "Default Amp-Out caps: "); - print_amp_caps(buffer, codec, codec->afg, HDA_OUTPUT); - snd_iprintf(buffer, "State of AFG node 0x%02x:\n", codec->afg); - print_power_state(buffer, codec, codec->afg); + print_amp_caps(buffer, codec, fg, HDA_OUTPUT); + snd_iprintf(buffer, "State of AFG node 0x%02x:\n", fg); + print_power_state(buffer, codec, fg); - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, fg, &nid); if (! nid || nodes < 0) { snd_iprintf(buffer, "Invalid AFG subtree\n"); snd_hda_power_down(codec); return; } - print_gpio(buffer, codec, codec->afg); + print_gpio(buffer, codec, fg); if (codec->proc_widget_hook) - codec->proc_widget_hook(buffer, codec, codec->afg); + codec->proc_widget_hook(buffer, codec, fg); for (i = 0; i < nodes; i++, nid++) { unsigned int wid_caps = - snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + param_read(codec, nid, AC_PAR_AUDIO_WIDGET_CAP); unsigned int wid_type = get_wcaps_type(wid_caps); hda_nid_t *conn = NULL; int conn_len = 0; @@ -860,8 +866,8 @@ int snd_hda_codec_proc_new(struct hda_codec *codec) struct snd_info_entry *entry; int err; - snprintf(name, sizeof(name), "codec#%d", codec->addr); - err = snd_card_proc_new(codec->bus->card, name, &entry); + snprintf(name, sizeof(name), "codec#%d", codec->core.addr); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c index ccc962a1699f..a6e3d9b511ab 100644 --- a/sound/pci/hda/hda_sysfs.c +++ b/sound/pci/hda/hda_sysfs.c @@ -48,33 +48,33 @@ static DEVICE_ATTR_RO(power_on_acct); static DEVICE_ATTR_RO(power_off_acct); #endif /* CONFIG_PM */ -#define CODEC_INFO_SHOW(type) \ +#define CODEC_INFO_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ - return sprintf(buf, "0x%x\n", codec->type); \ + return sprintf(buf, "0x%x\n", codec->field); \ } -#define CODEC_INFO_STR_SHOW(type) \ +#define CODEC_INFO_STR_SHOW(type, field) \ static ssize_t type##_show(struct device *dev, \ struct device_attribute *attr, \ char *buf) \ { \ struct hda_codec *codec = dev_get_drvdata(dev); \ return sprintf(buf, "%s\n", \ - codec->type ? codec->type : ""); \ + codec->field ? codec->field : ""); \ } -CODEC_INFO_SHOW(vendor_id); -CODEC_INFO_SHOW(subsystem_id); -CODEC_INFO_SHOW(revision_id); -CODEC_INFO_SHOW(afg); -CODEC_INFO_SHOW(mfg); -CODEC_INFO_STR_SHOW(vendor_name); -CODEC_INFO_STR_SHOW(chip_name); -CODEC_INFO_STR_SHOW(modelname); +CODEC_INFO_SHOW(vendor_id, core.vendor_id); +CODEC_INFO_SHOW(subsystem_id, core.subsystem_id); +CODEC_INFO_SHOW(revision_id, core.revision_id); +CODEC_INFO_SHOW(afg, core.afg); +CODEC_INFO_SHOW(mfg, core.mfg); +CODEC_INFO_STR_SHOW(vendor_name, core.vendor_name); +CODEC_INFO_STR_SHOW(chip_name, core.chip_name); +CODEC_INFO_STR_SHOW(modelname, modelname); static ssize_t pin_configs_show(struct hda_codec *codec, struct snd_array *list, @@ -149,7 +149,7 @@ static int reconfig_codec(struct hda_codec *codec) err = snd_hda_codec_build_controls(codec); if (err < 0) goto error; - err = snd_card_register(codec->bus->card); + err = snd_card_register(codec->card); error: snd_hda_power_down(codec); return err; @@ -170,7 +170,7 @@ static char *kstrndup_noeol(const char *src, size_t len) return s; } -#define CODEC_INFO_STORE(type) \ +#define CODEC_INFO_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -180,11 +180,11 @@ static ssize_t type##_store(struct device *dev, \ int err = kstrtoul(buf, 0, &val); \ if (err < 0) \ return err; \ - codec->type = val; \ + codec->field = val; \ return count; \ } -#define CODEC_INFO_STR_STORE(type) \ +#define CODEC_INFO_STR_STORE(type, field) \ static ssize_t type##_store(struct device *dev, \ struct device_attribute *attr, \ const char *buf, size_t count) \ @@ -193,17 +193,17 @@ static ssize_t type##_store(struct device *dev, \ char *s = kstrndup_noeol(buf, 64); \ if (!s) \ return -ENOMEM; \ - kfree(codec->type); \ - codec->type = s; \ + kfree(codec->field); \ + codec->field = s; \ return count; \ } -CODEC_INFO_STORE(vendor_id); -CODEC_INFO_STORE(subsystem_id); -CODEC_INFO_STORE(revision_id); -CODEC_INFO_STR_STORE(vendor_name); -CODEC_INFO_STR_STORE(chip_name); -CODEC_INFO_STR_STORE(modelname); +CODEC_INFO_STORE(vendor_id, core.vendor_id); +CODEC_INFO_STORE(subsystem_id, core.subsystem_id); +CODEC_INFO_STORE(revision_id, core.revision_id); +CODEC_INFO_STR_STORE(vendor_name, core.vendor_name); +CODEC_INFO_STR_STORE(chip_name, core.chip_name); +CODEC_INFO_STR_STORE(modelname, modelname); #define CODEC_ACTION_STORE(type) \ static ssize_t type##_store(struct device *dev, \ @@ -552,10 +552,10 @@ static void parse_codec_mode(char *buf, struct hda_bus *bus, *codecp = NULL; if (sscanf(buf, "%i %i %i", &vendorid, &subid, &caddr) == 3) { - list_for_each_entry(codec, &bus->codec_list, list) { - if ((vendorid <= 0 || codec->vendor_id == vendorid) && - (subid <= 0 || codec->subsystem_id == subid) && - codec->addr == caddr) { + list_for_each_codec(codec, bus) { + if ((vendorid <= 0 || codec->core.vendor_id == vendorid) && + (subid <= 0 || codec->core.subsystem_id == subid) && + codec->core.addr == caddr) { *codecp = codec; break; } @@ -595,8 +595,8 @@ static void parse_model_mode(char *buf, struct hda_bus *bus, static void parse_chip_name_mode(char *buf, struct hda_bus *bus, struct hda_codec **codecp) { - kfree((*codecp)->chip_name); - (*codecp)->chip_name = kstrdup(buf, GFP_KERNEL); + kfree((*codecp)->core.chip_name); + (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL); } #define DEFINE_PARSE_ID_MODE(name) \ @@ -605,7 +605,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \ { \ unsigned long val; \ if (!kstrtoul(buf, 0, &val)) \ - (*codecp)->name = val; \ + (*codecp)->core.name = val; \ } DEFINE_PARSE_ID_MODE(vendor_id); diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c index 375e94f4cf52..2e4fd5c56d3b 100644 --- a/sound/pci/hda/hda_tegra.c +++ b/sound/pci/hda/hda_tegra.c @@ -37,7 +37,6 @@ #include "hda_codec.h" #include "hda_controller.h" -#include "hda_priv.h" /* Defines for Nvidia Tegra HDA support */ #define HDA_BAR0 0x8000 @@ -82,7 +81,7 @@ module_param(power_save, bint, 0644); MODULE_PARM_DESC(power_save, "Automatic power-saving timeout (in seconds, 0 = disable)."); #else -static int power_save = 0; +#define power_save 0 #endif /* @@ -250,14 +249,9 @@ static int hda_tegra_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; - struct azx_pcm *p; struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - list_for_each_entry(p, &chip->pcm_list, list) - snd_pcm_suspend_all(p->pcm); - if (chip->initialized) - snd_hda_suspend(chip->bus); azx_stop_chip(chip); azx_enter_link_reset(chip); @@ -278,7 +272,6 @@ static int hda_tegra_resume(struct device *dev) azx_init_chip(chip, 1); - snd_hda_resume(chip->bus); snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; @@ -297,8 +290,6 @@ static int hda_tegra_dev_free(struct snd_device *device) int i; struct azx *chip = device->device_data; - azx_notifier_unregister(chip); - if (chip->initialized) { for (i = 0; i < chip->num_streams; i++) azx_stream_stop(chip, &chip->azx_dev[i]); @@ -344,17 +335,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) return 0; } -/* - * The codecs were powered up in snd_hda_codec_new(). - * Now all initialization done, so turn them down if possible - */ -static void power_down_all_codecs(struct azx *chip) -{ - struct hda_codec *codec; - list_for_each_entry(codec, &chip->bus->codec_list, list) - snd_hda_power_down(codec); -} - static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) { struct snd_card *card = chip->card; @@ -503,21 +483,15 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; /* create codec instances */ - err = azx_codec_create(chip, NULL, 0, &power_save); + err = azx_bus_create(chip, NULL); if (err < 0) goto out_free; - err = azx_codec_configure(chip); + err = azx_probe_codecs(chip, 0); if (err < 0) goto out_free; - /* create PCM streams */ - err = snd_hda_build_pcms(chip->bus); - if (err < 0) - goto out_free; - - /* create mixer controls */ - err = azx_mixer_create(chip); + err = azx_codec_configure(chip); if (err < 0) goto out_free; @@ -526,8 +500,7 @@ static int hda_tegra_probe(struct platform_device *pdev) goto out_free; chip->running = 1; - power_down_all_codecs(chip); - azx_notifier_register(chip); + snd_hda_set_power_save(chip->bus, power_save * 1000); return 0; @@ -541,6 +514,18 @@ static int hda_tegra_remove(struct platform_device *pdev) return snd_card_free(dev_get_drvdata(&pdev->dev)); } +static void hda_tegra_shutdown(struct platform_device *pdev) +{ + struct snd_card *card = dev_get_drvdata(&pdev->dev); + struct azx *chip; + + if (!card) + return; + chip = card->private_data; + if (chip && chip->running) + azx_stop_chip(chip); +} + static struct platform_driver tegra_platform_hda = { .driver = { .name = "tegra-hda", @@ -549,6 +534,7 @@ static struct platform_driver tegra_platform_hda = { }, .probe = hda_tegra_probe, .remove = hda_tegra_remove, + .shutdown = hda_tegra_shutdown, }; module_platform_driver(tegra_platform_hda); diff --git a/sound/pci/hda/hda_trace.h b/sound/pci/hda/hda_trace.h deleted file mode 100644 index 3a1c63161eb1..000000000000 --- a/sound/pci/hda/hda_trace.h +++ /dev/null @@ -1,143 +0,0 @@ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM hda -#define TRACE_INCLUDE_FILE hda_trace - -#if !defined(_TRACE_HDA_H) || defined(TRACE_HEADER_MULTI_READ) -#define _TRACE_HDA_H - -#include <linux/tracepoint.h> - -struct hda_bus; -struct hda_codec; - -DECLARE_EVENT_CLASS(hda_cmd, - - TP_PROTO(struct hda_codec *codec, unsigned int val), - - TP_ARGS(codec, val), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - __field( unsigned int, val ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - __entry->val = (val); - ), - - TP_printk("[%d:%d] val=%x", __entry->card, __entry->addr, __entry->val) -); - -DEFINE_EVENT(hda_cmd, hda_send_cmd, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -DEFINE_EVENT(hda_cmd, hda_get_response, - TP_PROTO(struct hda_codec *codec, unsigned int val), - TP_ARGS(codec, val) -); - -TRACE_EVENT(hda_bus_reset, - - TP_PROTO(struct hda_bus *bus), - - TP_ARGS(bus), - - TP_STRUCT__entry( - __field( unsigned int, card ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - ), - - TP_printk("[%d]", __entry->card) -); - -#ifdef CONFIG_PM -DECLARE_EVENT_CLASS(hda_power, - - TP_PROTO(struct hda_codec *codec), - - TP_ARGS(codec), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - ), - - TP_printk("[%d:%d]", __entry->card, __entry->addr) -); - -DEFINE_EVENT(hda_power, hda_power_down, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -DEFINE_EVENT(hda_power, hda_power_up, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec) -); - -TRACE_EVENT(hda_power_count, - TP_PROTO(struct hda_codec *codec), - TP_ARGS(codec), - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( unsigned int, addr ) - __field( int, power_count ) - __field( int, power_on ) - __field( int, power_transition ) - ), - - TP_fast_assign( - __entry->card = (codec)->bus->card->number; - __entry->addr = (codec)->addr; - __entry->power_count = (codec)->power_count; - __entry->power_on = (codec)->power_on; - __entry->power_transition = (codec)->power_transition; - ), - - TP_printk("[%d:%d] power_count=%d, power_on=%d, power_transition=%d", - __entry->card, __entry->addr, __entry->power_count, - __entry->power_on, __entry->power_transition) -); -#endif /* CONFIG_PM */ - -TRACE_EVENT(hda_unsol_event, - - TP_PROTO(struct hda_bus *bus, u32 res, u32 res_ex), - - TP_ARGS(bus, res, res_ex), - - TP_STRUCT__entry( - __field( unsigned int, card ) - __field( u32, res ) - __field( u32, res_ex ) - ), - - TP_fast_assign( - __entry->card = (bus)->card->number; - __entry->res = res; - __entry->res_ex = res_ex; - ), - - TP_printk("[%d] res=%x, res_ex=%x", __entry->card, - __entry->res, __entry->res_ex) -); - -#endif /* _TRACE_HDA_H */ - -/* This part must be outside protection */ -#undef TRACE_INCLUDE_PATH -#define TRACE_INCLUDE_PATH . -#include <trace/define_trace.h> diff --git a/sound/pci/hda/local.h b/sound/pci/hda/local.h new file mode 100644 index 000000000000..28cb7f98982e --- /dev/null +++ b/sound/pci/hda/local.h @@ -0,0 +1,39 @@ +/* + */ + +#ifndef __HDAC_LOCAL_H +#define __HDAC_LOCAL_H + +int hdac_read_parm(struct hdac_device *codec, hda_nid_t nid, int parm); + +#define get_wcaps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_AUDIO_WIDGET_CAP) +/* get the widget type from widget capability bits */ +static inline int get_wcaps_type(unsigned int wcaps) +{ + if (!wcaps) + return -1; /* invalid type */ + return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; +} + +#define get_pin_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_PIN_CAP) + +static inline +unsigned int get_pin_cfg(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val; + + if (snd_hdac_read(codec, nid, AC_VERB_GET_CONFIG_DEFAULT, 0, &val)) + return -1; + return val; +} + +#define get_amp_caps(codec, nid, dir) \ + hdac_read_parm(codec, nid, (dir) == HDA_OUTPUT ? \ + AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP) + +#define get_power_caps(codec, nid) \ + hdac_read_parm(codec, nid, AC_PAR_POWER_STATE) + +#endif /* __HDAC_LOCAL_H */ diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index d285904cdb64..231f89029779 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -99,7 +99,7 @@ static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, static void ad198x_power_eapd(struct hda_codec *codec) { /* We currently only handle front, HP */ - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x11d41882: case 0x11d4882a: case 0x11d41884: @@ -777,7 +777,6 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, return 0; mutex_lock(&codec->control_mutex); - codec->cached_write = 1; path = snd_hda_get_path_from_idx(codec, spec->smux_paths[spec->cur_smux]); if (path) @@ -786,9 +785,7 @@ static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, if (path) snd_hda_activate_path(codec, path, true, true); spec->cur_smux = val; - codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_cache(codec); /* flush the updates */ return 1; } @@ -1004,18 +1001,17 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct ad198x_spec *spec = codec->spec; - static const struct hda_verb gpio_init_verbs[] = { - {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, - {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, - {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, - {}, - }; switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; spec->gen.own_eapd_ctl = 1; - snd_hda_sequence_write_cache(codec, gpio_init_verbs); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_MASK, 0x02); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DIRECTION, 0x02); + snd_hda_codec_write_cache(codec, 0x01, 0, + AC_VERB_SET_GPIO_DATA, 0x02); break; case HDA_FIXUP_ACT_PROBE: if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -1194,20 +1190,8 @@ MODULE_ALIAS("snd-hda-codec-id:11d4*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Analog Devices HD-audio codec"); -static struct hda_codec_preset_list analog_list = { +static struct hda_codec_driver analog_driver = { .preset = snd_hda_preset_analog, - .owner = THIS_MODULE, }; -static int __init patch_analog_init(void) -{ - return snd_hda_add_codec_preset(&analog_list); -} - -static void __exit patch_analog_exit(void) -{ - snd_hda_delete_codec_preset(&analog_list); -} - -module_init(patch_analog_init) -module_exit(patch_analog_exit) +module_hda_codec_driver(analog_driver); diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c index 5e65999e0d8e..447302695195 100644 --- a/sound/pci/hda/patch_ca0110.c +++ b/sound/pci/hda/patch_ca0110.c @@ -98,20 +98,8 @@ MODULE_ALIAS("snd-hda-codec-id:1102000d"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec"); -static struct hda_codec_preset_list ca0110_list = { +static struct hda_codec_driver ca0110_driver = { .preset = snd_hda_preset_ca0110, - .owner = THIS_MODULE, }; -static int __init patch_ca0110_init(void) -{ - return snd_hda_add_codec_preset(&ca0110_list); -} - -static void __exit patch_ca0110_exit(void) -{ - snd_hda_delete_codec_preset(&ca0110_list); -} - -module_init(patch_ca0110_init) -module_exit(patch_ca0110_exit) +module_hda_codec_driver(ca0110_driver); diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index e0383eea9880..4a4e7b282e4f 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -719,7 +719,6 @@ struct ca0132_spec { unsigned int num_inputs; hda_nid_t shared_mic_nid; hda_nid_t shared_out_nid; - struct hda_pcm pcm_rec[5]; /* PCM information */ /* chip access */ struct mutex chipio_mutex; /* chip access mutex */ @@ -3132,7 +3131,7 @@ static int ca0132_select_out(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_out\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_HP_ASEL - VNODE_START_NID]; @@ -3216,7 +3215,7 @@ static int ca0132_select_out(struct hda_codec *codec) } exit: - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return err < 0 ? err : 0; } @@ -3294,7 +3293,7 @@ static int ca0132_select_mic(struct hda_codec *codec) codec_dbg(codec, "ca0132_select_mic\n"); - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); auto_jack = spec->vnode_lswitch[VNID_AMIC1_ASEL - VNODE_START_NID]; @@ -3327,7 +3326,7 @@ static int ca0132_select_mic(struct hda_codec *codec) ca0132_effects_set(codec, VOICE_FOCUS, 0); } - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } @@ -4036,12 +4035,11 @@ static struct hda_pcm_stream ca0132_pcm_digital_capture = { static int ca0132_build_pcms(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - - codec->pcm_info = info; - codec->num_pcms = 0; + struct hda_pcm *info; - info->name = "CA0132 Analog"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Analog"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ca0132_pcm_analog_playback; info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dacs[0]; info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = @@ -4049,27 +4047,27 @@ static int ca0132_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; - codec->num_pcms++; - info++; - info->name = "CA0132 Analog Mic-In2"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Analog Mic-In2"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[1]; - codec->num_pcms++; - info++; - info->name = "CA0132 What U Hear"; + info = snd_hda_codec_pcm_new(codec, "CA0132 What U Hear"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[2]; - codec->num_pcms++; if (!spec->dig_out && !spec->dig_in) return 0; - info++; - info->name = "CA0132 Digital"; + info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); + if (!info) + return -ENOMEM; info->pcm_type = HDA_PCM_TYPE_SPDIF; if (spec->dig_out) { info->stream[SNDRV_PCM_STREAM_PLAYBACK] = @@ -4081,7 +4079,6 @@ static int ca0132_build_pcms(struct hda_codec *codec) ca0132_pcm_digital_capture; info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; } - codec->num_pcms++; return 0; } @@ -4246,13 +4243,9 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; int i; - hda_nid_t nid; codec_dbg(codec, "ca0132_refresh_widget_caps.\n"); - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) - codec->wcaps[i] = snd_hda_param_read(codec, nid, - AC_PAR_AUDIO_WIDGET_CAP); + snd_hda_codec_update_widgets(codec); for (i = 0; i < spec->multiout.num_dacs; i++) refresh_amp_caps(codec, spec->dacs[i], HDA_OUTPUT); @@ -4352,7 +4345,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) const struct dsp_image_seg *dsp_os_image; const struct firmware *fw_entry; - if (request_firmware(&fw_entry, EFX_FILE, codec->bus->card->dev) != 0) + if (request_firmware(&fw_entry, EFX_FILE, codec->card->dev) != 0) return false; dsp_os_image = (struct dsp_image_seg *)(fw_entry->data); @@ -4413,8 +4406,7 @@ static void hp_callback(struct hda_codec *codec, struct hda_jack_callback *cb) * state machine run. */ cancel_delayed_work_sync(&spec->unsol_hp_work); - queue_delayed_work(codec->bus->workq, &spec->unsol_hp_work, - msecs_to_jiffies(500)); + schedule_delayed_work(&spec->unsol_hp_work, msecs_to_jiffies(500)); cb->tbl->block_report = 1; } @@ -4554,7 +4546,7 @@ static int ca0132_init(struct hda_codec *codec) spec->dsp_state = DSP_DOWNLOAD_INIT; spec->curr_chip_addx = INVALID_CHIP_ADDRESS; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); ca0132_init_unsol(codec); @@ -4585,7 +4577,7 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_jack_report_sync(codec); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return 0; } @@ -4702,20 +4694,8 @@ MODULE_ALIAS("snd-hda-codec-id:11020011"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Creative Sound Core3D codec"); -static struct hda_codec_preset_list ca0132_list = { +static struct hda_codec_driver ca0132_driver = { .preset = snd_hda_preset_ca0132, - .owner = THIS_MODULE, }; -static int __init patch_ca0132_init(void) -{ - return snd_hda_add_codec_preset(&ca0132_list); -} - -static void __exit patch_ca0132_exit(void) -{ - snd_hda_delete_codec_preset(&ca0132_list); -} - -module_init(patch_ca0132_init) -module_exit(patch_ca0132_exit) +module_hda_codec_driver(ca0132_driver); diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index dd2b3d92071f..50e9dd675579 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -1221,20 +1221,8 @@ MODULE_ALIAS("snd-hda-codec-id:10134213"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cirrus Logic HD-audio codec"); -static struct hda_codec_preset_list cirrus_list = { +static struct hda_codec_driver cirrus_driver = { .preset = snd_hda_preset_cirrus, - .owner = THIS_MODULE, }; -static int __init patch_cirrus_init(void) -{ - return snd_hda_add_codec_preset(&cirrus_list); -} - -static void __exit patch_cirrus_exit(void) -{ - snd_hda_delete_codec_preset(&cirrus_list); -} - -module_init(patch_cirrus_init) -module_exit(patch_cirrus_exit) +module_hda_codec_driver(cirrus_driver); diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c index c895a8f21192..617d9012e78a 100644 --- a/sound/pci/hda/patch_cmedia.c +++ b/sound/pci/hda/patch_cmedia.c @@ -137,20 +137,8 @@ MODULE_ALIAS("snd-hda-codec-id:434d4980"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("C-Media HD-audio codec"); -static struct hda_codec_preset_list cmedia_list = { +static struct hda_codec_driver cmedia_driver = { .preset = snd_hda_preset_cmedia, - .owner = THIS_MODULE, }; -static int __init patch_cmedia_init(void) -{ - return snd_hda_add_codec_preset(&cmedia_list); -} - -static void __exit patch_cmedia_exit(void) -{ - snd_hda_delete_codec_preset(&cmedia_list); -} - -module_init(patch_cmedia_init) -module_exit(patch_cmedia_exit) +module_hda_codec_driver(cmedia_driver); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index da67ea8645a6..f8f0dfbef149 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -103,10 +103,9 @@ static int add_beep_ctls(struct hda_codec *codec) static void cx_auto_parse_beep(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) + for_each_hda_codec_node(nid, codec) if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { set_beep_amp(spec, nid, 0, HDA_OUTPUT); break; @@ -120,10 +119,9 @@ static void cx_auto_parse_beep(struct hda_codec *codec) static void cx_auto_parse_eapd(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN) continue; if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) @@ -304,6 +302,7 @@ static void cxt_fixup_headphone_mic(struct hda_codec *codec, switch (action) { case HDA_FIXUP_ACT_PRE_PROBE: spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC; + snd_hdac_regmap_add_vendor_verb(&codec->core, 0x410); break; case HDA_FIXUP_ACT_PROBE: spec->gen.cap_sync_hook = cxt_update_headset_mode_hook; @@ -411,15 +410,11 @@ static void olpc_xo_automic(struct hda_codec *codec, struct hda_jack_callback *jack) { struct conexant_spec *spec = codec->spec; - int saved_cached_write = codec->cached_write; - codec->cached_write = 1; /* in DC mode, we don't handle automic */ if (!spec->dc_enable) snd_hda_gen_mic_autoswitch(codec, jack); olpc_xo_update_mic_pins(codec); - snd_hda_codec_flush_cache(codec); - codec->cached_write = saved_cached_write; if (spec->dc_enable) olpc_xo_update_mic_boost(codec); } @@ -848,7 +843,7 @@ static int patch_conexant_auto(struct hda_codec *codec) struct conexant_spec *spec; int err; - codec_info(codec, "%s: BIOS auto-probing.\n", codec->chip_name); + codec_info(codec, "%s: BIOS auto-probing.\n", codec->core.chip_name); spec = kzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -862,7 +857,7 @@ static int patch_conexant_auto(struct hda_codec *codec) if (spec->dynamic_eapd) spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x14f15045: codec->single_adc_amp = 1; spec->gen.mixer_nid = 0x17; @@ -896,7 +891,7 @@ static int patch_conexant_auto(struct hda_codec *codec) * others may use EAPD really as an amp switch, so it might be * not good to expose it blindly. */ - switch (codec->subsystem_id >> 16) { + switch (codec->core.subsystem_id >> 16) { case 0x103c: spec->gen.vmaster_mute_enum = 1; break; @@ -919,10 +914,10 @@ static int patch_conexant_auto(struct hda_codec *codec) * which falls into the single-cmd mode. * Better to make reset, then. */ - if (!codec->bus->sync_write) { + if (!codec->bus->core.sync_write) { codec_info(codec, "Enable sync_write for stable communication\n"); - codec->bus->sync_write = 1; + codec->bus->core.sync_write = 1; codec->bus->allow_bus_reset = 1; } @@ -1018,20 +1013,8 @@ MODULE_ALIAS("snd-hda-codec-id:14f151d7"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Conexant HD-audio codec"); -static struct hda_codec_preset_list conexant_list = { +static struct hda_codec_driver conexant_driver = { .preset = snd_hda_preset_conexant, - .owner = THIS_MODULE, }; -static int __init patch_conexant_init(void) -{ - return snd_hda_add_codec_preset(&conexant_list); -} - -static void __exit patch_conexant_exit(void) -{ - snd_hda_delete_codec_preset(&conexant_list); -} - -module_init(patch_conexant_init) -module_exit(patch_conexant_exit) +module_hda_codec_driver(conexant_driver); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index b422e406a9cb..5f44f60a6389 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -45,14 +45,14 @@ static bool static_hdmi_pcm; module_param(static_hdmi_pcm, bool, 0644); MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info"); -#define is_haswell(codec) ((codec)->vendor_id == 0x80862807) -#define is_broadwell(codec) ((codec)->vendor_id == 0x80862808) -#define is_skylake(codec) ((codec)->vendor_id == 0x80862809) +#define is_haswell(codec) ((codec)->core.vendor_id == 0x80862807) +#define is_broadwell(codec) ((codec)->core.vendor_id == 0x80862808) +#define is_skylake(codec) ((codec)->core.vendor_id == 0x80862809) #define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \ || is_skylake(codec)) -#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882) -#define is_cherryview(codec) ((codec)->vendor_id == 0x80862883) +#define is_valleyview(codec) ((codec)->core.vendor_id == 0x80862882) +#define is_cherryview(codec) ((codec)->core.vendor_id == 0x80862883) #define is_valleyview_plus(codec) (is_valleyview(codec) || is_cherryview(codec)) struct hdmi_spec_per_cvt { @@ -86,7 +86,6 @@ struct hdmi_spec_per_pin { bool non_pcm; bool chmap_set; /* channel-map override by ALSA API? */ unsigned char chmap[8]; /* ALSA API channel-map */ - char pcm_name[8]; /* filled in build_pcm callbacks */ #ifdef CONFIG_PROC_FS struct snd_info_entry *proc_entry; #endif @@ -132,7 +131,7 @@ struct hdmi_spec { int num_pins; struct snd_array pins; /* struct hdmi_spec_per_pin */ - struct snd_array pcm_rec; /* struct hda_pcm */ + struct hda_pcm *pcm_rec[16]; unsigned int channels_max; /* max over all cvts */ struct hdmi_eld temp_eld; @@ -355,8 +354,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = { ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx)) #define get_cvt(spec, idx) \ ((struct hdmi_spec_per_cvt *)snd_array_elem(&spec->cvts, idx)) -#define get_pcm_rec(spec, idx) \ - ((struct hda_pcm *)snd_array_elem(&spec->pcm_rec, idx)) +#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx]) static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid) { @@ -579,7 +577,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) int err; snprintf(name, sizeof(name), "eld#%d.%d", codec->addr, index); - err = snd_card_proc_new(codec->bus->card, name, &entry); + err = snd_card_proc_new(codec->card, name, &entry); if (err < 0) return err; @@ -594,7 +592,7 @@ static int eld_proc_new(struct hdmi_spec_per_pin *per_pin, int index) static void eld_proc_free(struct hdmi_spec_per_pin *per_pin) { if (!per_pin->codec->bus->shutdown && per_pin->proc_entry) { - snd_device_free(per_pin->codec->bus->card, per_pin->proc_entry); + snd_device_free(per_pin->codec->card, per_pin->proc_entry); per_pin->proc_entry = NULL; } } @@ -1393,13 +1391,12 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec, hda_nid_t pin_nid, int mux_idx) { struct hdmi_spec *spec = codec->spec; - hda_nid_t nid, end_nid; + hda_nid_t nid; int cvt_idx, curr; struct hdmi_spec_per_cvt *per_cvt; /* configure all pins, including "no physical connection" ones */ - end_nid = codec->start_nid + codec->num_nodes; - for (nid = codec->start_nid; nid < end_nid; nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int wid_caps = get_wcaps(codec, nid); unsigned int wid_type = get_wcaps_type(wid_caps); @@ -1548,7 +1545,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) bool eld_changed = false; bool ret; - snd_hda_power_up(codec); + snd_hda_power_up_pm(codec); present = snd_hda_pin_sense(codec, pin_nid); mutex_lock(&per_pin->lock); @@ -1578,9 +1575,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) update_eld = true; } else if (repoll) { - queue_delayed_work(codec->bus->workq, - &per_pin->work, - msecs_to_jiffies(300)); + schedule_delayed_work(&per_pin->work, + msecs_to_jiffies(300)); goto unlock; } } @@ -1624,7 +1620,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) } if (eld_changed) - snd_ctl_notify(codec->bus->card, + snd_ctl_notify(codec->card, SNDRV_CTL_EVENT_MASK_VALUE | SNDRV_CTL_EVENT_MASK_INFO, &per_pin->eld_ctl->id); unlock: @@ -1635,7 +1631,7 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll) jack->block_report = !ret; mutex_unlock(&per_pin->lock); - snd_hda_power_down(codec); + snd_hda_power_down_pm(codec); return ret; } @@ -1731,7 +1727,7 @@ static int hdmi_parse_codec(struct hda_codec *codec) hda_nid_t nid; int i, nodes; - nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); + nodes = snd_hda_get_sub_nodes(codec, codec->core.afg, &nid); if (!nid || nodes < 0) { codec_warn(codec, "HDMI: failed to get afg sub nodes\n"); return -EINVAL; @@ -2056,11 +2052,10 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) struct hdmi_spec_per_pin *per_pin; per_pin = get_pin(spec, pin_idx); - sprintf(per_pin->pcm_name, "HDMI %d", pin_idx); - info = snd_array_new(&spec->pcm_rec); + info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx); if (!info) return -ENOMEM; - info->name = per_pin->pcm_name; + spec->pcm_rec[pin_idx] = info; info->pcm_type = HDA_PCM_TYPE_HDMI; info->own_chmap = true; @@ -2070,9 +2065,6 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec) /* other pstr fields are set in open */ } - codec->num_pcms = spec->num_pins; - codec->pcm_info = spec->pcm_rec.list; - return 0; } @@ -2125,13 +2117,15 @@ static int generic_hdmi_build_controls(struct hda_codec *codec) /* add channel maps */ for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hda_pcm *pcm; struct snd_pcm_chmap *chmap; struct snd_kcontrol *kctl; int i; - if (!codec->pcm_info[pin_idx].pcm) + pcm = spec->pcm_rec[pin_idx]; + if (!pcm || !pcm->pcm) break; - err = snd_pcm_add_chmap_ctls(codec->pcm_info[pin_idx].pcm, + err = snd_pcm_add_chmap_ctls(pcm->pcm, SNDRV_PCM_STREAM_PLAYBACK, NULL, 0, pin_idx, &chmap); if (err < 0) @@ -2186,14 +2180,12 @@ static void hdmi_array_init(struct hdmi_spec *spec, int nums) { snd_array_init(&spec->pins, sizeof(struct hdmi_spec_per_pin), nums); snd_array_init(&spec->cvts, sizeof(struct hdmi_spec_per_cvt), nums); - snd_array_init(&spec->pcm_rec, sizeof(struct hda_pcm), nums); } static void hdmi_array_free(struct hdmi_spec *spec) { snd_array_free(&spec->pins); snd_array_free(&spec->cvts); - snd_array_free(&spec->pcm_rec); } static void generic_hdmi_free(struct hda_codec *codec) @@ -2204,11 +2196,10 @@ static void generic_hdmi_free(struct hda_codec *codec) for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); - cancel_delayed_work(&per_pin->work); + cancel_delayed_work_sync(&per_pin->work); eld_proc_free(per_pin); } - flush_workqueue(codec->bus->workq); hdmi_array_free(spec); kfree(spec); } @@ -2220,8 +2211,7 @@ static int generic_hdmi_resume(struct hda_codec *codec) int pin_idx; codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); @@ -2308,6 +2298,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec) /* enable DP1.2 mode */ vendor_param |= INTEL_EN_DP12; + snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB); snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0, INTEL_SET_VENDOR_VERB, vendor_param); } @@ -2381,11 +2372,10 @@ static int simple_playback_build_pcms(struct hda_codec *codec) chans = get_wcaps(codec, per_cvt->cvt_nid); chans = get_wcaps_channels(chans); - info = snd_array_new(&spec->pcm_rec); + info = snd_hda_codec_pcm_new(codec, "HDMI 0"); if (!info) return -ENOMEM; - info->name = get_pin(spec, 0)->pcm_name; - sprintf(info->name, "HDMI 0"); + spec->pcm_rec[0] = info; info->pcm_type = HDA_PCM_TYPE_HDMI; pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK]; *pstr = spec->pcm_playback; @@ -2393,9 +2383,6 @@ static int simple_playback_build_pcms(struct hda_codec *codec) if (pstr->channels_max <= 2 && chans && chans <= 16) pstr->channels_max = chans; - codec->num_pcms = 1; - codec->pcm_info = info; - return 0; } @@ -2940,7 +2927,8 @@ static int patch_nvhdmi(struct hda_codec *codec) */ #define is_amdhdmi_rev3_or_later(codec) \ - ((codec)->vendor_id == 0x1002aa01 && ((codec)->revision_id & 0xff00) >= 0x0300) + ((codec)->core.vendor_id == 0x1002aa01 && \ + ((codec)->core.revision_id & 0xff00) >= 0x0300) #define has_amd_full_remap_support(codec) is_amdhdmi_rev3_or_later(codec) /* ATI/AMD specific HDA pin verbs, see the AMD HDA Verbs specification */ @@ -3301,15 +3289,6 @@ static int patch_via_hdmi(struct hda_codec *codec) } /* - * called from hda_codec.c for generic HDMI support - */ -int snd_hda_parse_hdmi_codec(struct hda_codec *codec) -{ - return patch_generic_hdmi(codec); -} -EXPORT_SYMBOL_GPL(snd_hda_parse_hdmi_codec); - -/* * patch entries */ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { @@ -3373,6 +3352,8 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = { { .id = 0x80862882, .name = "Valleyview2 HDMI", .patch = patch_generic_hdmi }, { .id = 0x80862883, .name = "Braswell HDMI", .patch = patch_generic_hdmi }, { .id = 0x808629fb, .name = "Crestline HDMI", .patch = patch_generic_hdmi }, +/* special ID for generic HDMI */ +{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi }, {} /* terminator */ }; @@ -3442,20 +3423,8 @@ MODULE_ALIAS("snd-hda-codec-intelhdmi"); MODULE_ALIAS("snd-hda-codec-nvhdmi"); MODULE_ALIAS("snd-hda-codec-atihdmi"); -static struct hda_codec_preset_list intel_list = { +static struct hda_codec_driver hdmi_driver = { .preset = snd_hda_preset_hdmi, - .owner = THIS_MODULE, }; -static int __init patch_hdmi_init(void) -{ - return snd_hda_add_codec_preset(&intel_list); -} - -static void __exit patch_hdmi_exit(void) -{ - snd_hda_delete_codec_preset(&intel_list); -} - -module_init(patch_hdmi_init) -module_exit(patch_hdmi_exit) +module_hda_codec_driver(hdmi_driver); diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f9d12c0a7e5a..b18b9c67b262 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -299,7 +299,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec) coef = alc_get_coef0(codec); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0262: alc_update_coef_idx(codec, 0x7, 0, 1<<5); break; @@ -432,7 +432,7 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) snd_hda_sequence_write(codec, alc_gpio3_init_verbs); break; case ALC_INIT_DEFAULT: - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0260: alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010); break; @@ -498,18 +498,18 @@ static int alc_auto_parse_customize_define(struct hda_codec *codec) if (!codec->bus->pci) return -1; - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); if (!(ass & 1)) { codec_info(codec, "%s: SKU not ready 0x%08x\n", - codec->chip_name, ass); + codec->core.chip_name, ass); return -1; } @@ -585,7 +585,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) goto do_sku; } - ass = codec->subsystem_id & 0xffff; + ass = codec->core.subsystem_id & 0xffff; if (codec->bus->pci && ass != codec->bus->pci->subsystem_device && (ass & 1)) goto do_sku; @@ -600,7 +600,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) * 0 : override */ nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) + if (codec->core.vendor_id == 0x10ec0260) nid = 0x17; ass = snd_hda_codec_get_pincfg(codec, nid); codec_dbg(codec, @@ -621,7 +621,7 @@ static int alc_subsystem_id(struct hda_codec *codec, const hda_nid_t *ports) return 0; do_sku: codec_dbg(codec, "realtek: Enabling init ASM_ID=0x%04x CODEC_ID=%08x\n", - ass & 0xffff, codec->vendor_id); + ass & 0xffff, codec->core.vendor_id); /* * 0 : override * 1 : Swap Jack @@ -799,8 +799,7 @@ static int alc_resume(struct hda_codec *codec) if (!spec->no_depop_delay) msleep(150); /* to avoid pop noise */ codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); hda_call_check_power_status(codec, 0x01); return 0; } @@ -826,9 +825,9 @@ static const struct hda_codec_ops alc_patch_ops = { /* replace the codec chip_name with the given string */ static int alc_codec_rename(struct hda_codec *codec, const char *name) { - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->core.chip_name) { alc_free(codec); return -ENOMEM; } @@ -904,7 +903,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) const struct alc_codec_rename_pci_table *q; for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) + if (p->vendor_id != codec->core.vendor_id) continue; if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) return alc_codec_rename(codec, p->name); @@ -913,7 +912,7 @@ static int alc_codec_rename_from_preset(struct hda_codec *codec) if (!codec->bus->pci) return 0; for (q = rename_pci_tbl; q->codec_vendor_id; q++) { - if (q->codec_vendor_id != codec->vendor_id) + if (q->codec_vendor_id != codec->core.vendor_id) continue; if (q->pci_subvendor != codec->bus->pci->subsystem_vendor) continue; @@ -1785,7 +1784,7 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) { unsigned int gpiostate, gpiomask, gpiodir; - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); if (!muted) @@ -1793,23 +1792,23 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) else gpiostate &= ~(1 << pin); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= (1 << pin); - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= (1 << pin); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); msleep(1); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); } @@ -2269,7 +2268,7 @@ static int patch_alc882(struct hda_codec *codec) spec = codec->spec; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0882: case 0x10ec0885: case 0x10ec0900: @@ -2602,53 +2601,12 @@ static int patch_alc268(struct hda_codec *codec) * ALC269 */ -static int playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - static const 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 = playback_pcm_open, - .prepare = playback_pcm_prepare, - .cleanup = playback_pcm_cleanup - }, }; static const 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 */ }; /* different alc269-variants */ @@ -3101,8 +3059,7 @@ static int alc269_resume(struct hda_codec *codec) msleep(200); } - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); + regcache_sync(codec->core.regmap); hda_call_check_power_status(codec, 0x01); /* on some machine, the BIOS will clear the codec gpio data when enter @@ -3110,7 +3067,7 @@ static int alc269_resume(struct hda_codec *codec) * in the driver. */ if (spec->gpio_led) - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_SET_GPIO_DATA, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_DATA, spec->gpio_led); if (spec->has_alc5505_dsp) @@ -3155,8 +3112,8 @@ static void alc271_fixup_dmic(struct hda_codec *codec, }; unsigned int cfg; - if (strcmp(codec->chip_name, "ALC271X") && - strcmp(codec->chip_name, "ALC269VB")) + if (strcmp(codec->core.chip_name, "ALC271X") && + strcmp(codec->core.chip_name, "ALC269VB")) return; cfg = snd_hda_codec_get_pincfg(codec, 0x12); if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) @@ -3266,7 +3223,7 @@ static unsigned int led_power_filter(struct hda_codec *codec, snd_hda_set_pin_ctl(codec, nid, snd_hda_codec_get_pin_target(codec, nid)); - return AC_PWRST_D0; + return snd_hda_gen_path_power_filter(codec, nid, power_state); } static void alc269_fixup_hp_mute_led(struct hda_codec *codec, @@ -3522,9 +3479,9 @@ static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec, } snd_hda_add_verbs(codec, gpio_init); - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04); - snd_hda_jack_detect_enable_callback(codec, codec->afg, + snd_hda_jack_detect_enable_callback(codec, codec->core.afg, gpio2_mic_hotkey_event); spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook; @@ -3585,6 +3542,14 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) WRITE_COEF(0x32, 0x42a3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x76, 0x000e), WRITE_COEF(0x6c, 0x2400), @@ -3607,7 +3572,7 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3616,6 +3581,10 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3645,6 +3614,14 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, WRITE_COEF(0x26, 0x008c), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0), + UPDATE_COEF(0x56, 0x0006, 0), + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), + UPDATE_COEF(0x66, 0x0008, 0x0008), + UPDATE_COEF(0x67, 0x2000, 0x2000), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x19, 0xa208), WRITE_COEF(0x2e, 0xacf0), @@ -3663,7 +3640,7 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_write_coef_idx(codec, 0x45, 0xc489); @@ -3678,6 +3655,13 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, alc_process_coef_fw(codec, coef0233); snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0x000c, 0); + snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); + alc_process_coef_fw(codec, coef0288); + snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); + break; case 0x10ec0292: snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); alc_process_coef_fw(codec, coef0292); @@ -3713,6 +3697,14 @@ static void alc_headset_mode_default(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xc400), /* Set to TRS type */ + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x76, 0x000e), WRITE_COEF(0x6c, 0x2400), @@ -3733,7 +3725,7 @@ static void alc_headset_mode_default(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3742,6 +3734,11 @@ static void alc_headset_mode_default(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + break; + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3770,6 +3767,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x6b, 0xd429), WRITE_COEF(0x76, 0x0008), @@ -3788,7 +3792,7 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3797,6 +3801,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xd400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3825,6 +3835,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) WRITE_COEF(0x32, 0x4ea3), {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x50, 0x2000, 0x2000), + UPDATE_COEF(0x56, 0x0006, 0x0006), + UPDATE_COEF(0x66, 0x0008, 0), + UPDATE_COEF(0x67, 0x2000, 0), + {} + }; static struct coef_fw coef0292[] = { WRITE_COEF(0x6b, 0xe429), WRITE_COEF(0x76, 0x0008), @@ -3843,7 +3860,7 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3852,6 +3869,12 @@ static void alc_headset_mode_omtp(struct hda_codec *codec) case 0x10ec0283: alc_process_coef_fw(codec, coef0233); break; + case 0x10ec0286: + case 0x10ec0288: + alc_update_coef_idx(codec, 0x4f, 0xfcc0, 0xe400); + msleep(300); + alc_process_coef_fw(codec, coef0288); + break; case 0x10ec0292: alc_process_coef_fw(codec, coef0292); break; @@ -3876,6 +3899,10 @@ static void alc_determine_headset_type(struct hda_codec *codec) conteol) */ {} }; + static struct coef_fw coef0288[] = { + UPDATE_COEF(0x4f, 0xfcc0, 0xd400), /* Check Type */ + {} + }; static struct coef_fw coef0293[] = { UPDATE_COEF(0x4a, 0x000f, 0x0008), /* Combo Jack auto detect */ WRITE_COEF(0x45, 0xD429), /* Set to ctia type */ @@ -3889,7 +3916,7 @@ static void alc_determine_headset_type(struct hda_codec *codec) {} }; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0255: case 0x10ec0256: alc_process_coef_fw(codec, coef0255); @@ -3904,6 +3931,13 @@ static void alc_determine_headset_type(struct hda_codec *codec) val = alc_read_coef_idx(codec, 0x46); is_ctia = (val & 0x0070) == 0x0070; break; + case 0x10ec0286: + case 0x10ec0288: + alc_process_coef_fw(codec, coef0288); + msleep(350); + val = alc_read_coef_idx(codec, 0x50); + is_ctia = (val & 0x0070) == 0x0070; + break; case 0x10ec0292: alc_write_coef_idx(codec, 0x6b, 0xd429); msleep(300); @@ -4087,6 +4121,29 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec, alc_fixup_headset_mode(codec, fix, action); } +static void alc288_update_headset_jack_cb(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + int present; + + alc_update_headset_jack_cb(codec, jack); + /* Headset Mic enable or disable, only for Dell Dino */ + present = spec->gen.hp_jack_present ? 0x40 : 0; + snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + present); +} + +static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + alc_fixup_headset_mode(codec, fix, action); + if (action == HDA_FIXUP_ACT_PROBE) { + struct alc_spec *spec = codec->spec; + spec->gen.hp_automute_hook = alc288_update_headset_jack_cb; + } +} + static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, const struct hda_fixup *fix, int action) { @@ -4127,9 +4184,9 @@ static unsigned int alc_power_filter_xps13(struct hda_codec *codec, /* Avoid pop noises when headphones are plugged in */ if (spec->gen.hp_jack_present) - if (nid == codec->afg || nid == 0x02 || nid == 0x15) + if (nid == codec->core.afg || nid == 0x02 || nid == 0x15) return AC_PWRST_D0; - return power_state; + return snd_hda_gen_path_power_filter(codec, nid, power_state); } static void alc_fixup_dell_xps13(struct hda_codec *codec, @@ -4427,6 +4484,9 @@ enum { ALC286_FIXUP_HP_GPIO_LED, ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY, ALC280_FIXUP_HP_DOCK_PINS, + ALC288_FIXUP_DELL_HEADSET_MODE, + ALC288_FIXUP_DELL1_MIC_NO_PRESENCE, + ALC288_FIXUP_DELL_XPS_13_GPIO6, }; static const struct hda_fixup alc269_fixups[] = { @@ -4922,6 +4982,33 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC280_FIXUP_HP_GPIO4 }, + [ALC288_FIXUP_DELL_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc_fixup_headset_mode_dell_alc288, + .chained = true, + .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED + }, + [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */ + { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE + }, + [ALC288_FIXUP_DELL_XPS_13_GPIO6] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + {0x01, AC_VERB_SET_GPIO_MASK, 0x40}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x40}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, + { } + }, + .chained = true, + .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -5047,12 +5134,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC283_FIXUP_INT_MIC), SND_PCI_QUIRK(0x17aa, 0x501e, "Thinkpad L440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), + SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), @@ -5149,6 +5238,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {0x1b, 0x411111f0}, \ {0x1e, 0x411111f0} +#define ALC288_STANDARD_PINS \ + {0x17, 0x411111f0}, \ + {0x18, 0x411111f0}, \ + {0x19, 0x411111f0}, \ + {0x1a, 0x411111f0}, \ + {0x1e, 0x411111f0} + #define ALC290_STANDARD_PINS \ {0x12, 0x99a30130}, \ {0x13, 0x40000000}, \ @@ -5344,6 +5440,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x19, 0x03a11020}, {0x1d, 0x40e00001}, {0x21, 0x0321101f}), + SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6, + ALC288_STANDARD_PINS, + {0x12, 0x90a60120}, + {0x13, 0x40000000}, + {0x14, 0x90170110}, + {0x1d, 0x4076832d}, + {0x21, 0x0321101f}), SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1, ALC290_STANDARD_PINS, {0x14, 0x411111f0}, @@ -5484,6 +5587,7 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; + codec->power_save_node = 1; snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -5497,7 +5601,7 @@ static int patch_alc269(struct hda_codec *codec) if (has_cdefine_beep(codec)) spec->gen.beep_nid = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0269: spec->codec_variant = ALC269_TYPE_ALC269VA; switch (alc_get_coef0(codec) & 0x00f0) { @@ -5580,6 +5684,7 @@ static int patch_alc269(struct hda_codec *codec) set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); codec->patch_ops = alc_patch_ops; + codec->patch_ops.stream_pm = snd_hda_gen_stream_pm; #ifdef CONFIG_PM codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.resume = alc269_resume; @@ -5841,9 +5946,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static const hda_nid_t alc662_ssids[] = { 0x15, 0x1b, 0x14, 0 }; const hda_nid_t *ssids; - if (codec->vendor_id == 0x10ec0272 || codec->vendor_id == 0x10ec0663 || - codec->vendor_id == 0x10ec0665 || codec->vendor_id == 0x10ec0670 || - codec->vendor_id == 0x10ec0671) + if (codec->core.vendor_id == 0x10ec0272 || codec->core.vendor_id == 0x10ec0663 || + codec->core.vendor_id == 0x10ec0665 || codec->core.vendor_id == 0x10ec0670 || + codec->core.vendor_id == 0x10ec0671) ssids = alc663_ssids; else ssids = alc662_ssids; @@ -5878,7 +5983,7 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec, { if (action == HDA_FIXUP_ACT_BUILD) { struct alc_spec *spec = codec->spec; - spec->gen.pcm_rec[0].stream[0].chmap = asus_pcm_2_1_chmaps; + spec->gen.pcm_rec[0]->stream[0].chmap = asus_pcm_2_1_chmaps; } } @@ -5888,7 +5993,7 @@ static unsigned int gpio_led_power_filter(struct hda_codec *codec, unsigned int power_state) { struct alc_spec *spec = codec->spec; - if (nid == codec->afg && power_state == AC_PWRST_D3 && spec->gpio_led) + if (nid == codec->core.afg && power_state == AC_PWRST_D3 && spec->gpio_led) return AC_PWRST_D0; return power_state; } @@ -6386,7 +6491,7 @@ static int patch_alc662(struct hda_codec *codec) alc_fix_pll_init(codec, 0x20, 0x04, 15); - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0668: spec->init_hook = alc668_restore_default_value; break; @@ -6416,7 +6521,7 @@ static int patch_alc662(struct hda_codec *codec) goto error; if (!spec->gen.no_analog && spec->gen.beep_nid) { - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x10ec0662: set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); break; @@ -6549,20 +6654,8 @@ MODULE_ALIAS("snd-hda-codec-id:10ec*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Realtek HD-audio codec"); -static struct hda_codec_preset_list realtek_list = { +static struct hda_codec_driver realtek_driver = { .preset = snd_hda_preset_realtek, - .owner = THIS_MODULE, }; -static int __init patch_realtek_init(void) -{ - return snd_hda_add_codec_preset(&realtek_list); -} - -static void __exit patch_realtek_exit(void) -{ - snd_hda_delete_codec_preset(&realtek_list); -} - -module_init(patch_realtek_init) -module_exit(patch_realtek_exit) +module_hda_codec_driver(realtek_driver); diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c index 3208ad69583e..5104bebb2286 100644 --- a/sound/pci/hda/patch_si3054.c +++ b/sound/pci/hda/patch_si3054.c @@ -83,7 +83,6 @@ struct si3054_spec { unsigned international; - struct hda_pcm pcm; }; @@ -199,15 +198,15 @@ static const struct hda_pcm_stream si3054_pcm = { static int si3054_build_pcms(struct hda_codec *codec) { - struct si3054_spec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm; - codec->num_pcms = 1; - codec->pcm_info = info; - info->name = "Si3054 Modem"; + struct hda_pcm *info; + + info = snd_hda_codec_pcm_new(codec, "Si3054 Modem"); + if (!info) + return -ENOMEM; info->stream[SNDRV_PCM_STREAM_PLAYBACK] = si3054_pcm; info->stream[SNDRV_PCM_STREAM_CAPTURE] = si3054_pcm; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->mfg; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->mfg; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = codec->core.mfg; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = codec->core.mfg; info->pcm_type = HDA_PCM_TYPE_MODEM; return 0; } @@ -223,8 +222,12 @@ static int si3054_init(struct hda_codec *codec) unsigned wait_count; u16 val; + if (snd_hdac_regmap_add_vendor_verb(&codec->core, + SI3054_VERB_WRITE_NODE)) + return -ENOMEM; + snd_hda_codec_write(codec, AC_NODE_ROOT, 0, AC_VERB_SET_CODEC_RESET, 0); - snd_hda_codec_write(codec, codec->mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); + snd_hda_codec_write(codec, codec->core.mfg, 0, AC_VERB_SET_STREAM_FORMAT, 0); SET_REG(codec, SI3054_LINE_RATE, 9600); SET_REG(codec, SI3054_LINE_LEVEL, SI3054_DTAG_MASK|SI3054_ATAG_MASK); SET_REG(codec, SI3054_EXTENDED_MID, 0); @@ -319,20 +322,8 @@ MODULE_ALIAS("snd-hda-codec-id:18540018"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Si3054 HD-audio modem codec"); -static struct hda_codec_preset_list si3054_list = { +static struct hda_codec_driver si3054_driver = { .preset = snd_hda_preset_si3054, - .owner = THIS_MODULE, }; -static int __init patch_si3054_init(void) -{ - return snd_hda_add_codec_preset(&si3054_list); -} - -static void __exit patch_si3054_exit(void) -{ - snd_hda_delete_codec_preset(&si3054_list); -} - -module_init(patch_si3054_init) -module_exit(patch_si3054_exit) +module_hda_codec_driver(si3054_driver); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 87eff3173ce9..43c99ce4a520 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -299,32 +299,33 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask, unsigned int dir_mask, unsigned int data) { unsigned int gpiostate, gpiomask, gpiodir; + hda_nid_t fg = codec->core.afg; codec_dbg(codec, "%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); - gpiostate = snd_hda_codec_read(codec, codec->afg, 0, + gpiostate = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DATA, 0); gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); - gpiomask = snd_hda_codec_read(codec, codec->afg, 0, + gpiomask = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_MASK, 0); gpiomask |= mask; - gpiodir = snd_hda_codec_read(codec, codec->afg, 0, + gpiodir = snd_hda_codec_read(codec, fg, 0, AC_VERB_GET_GPIO_DIRECTION, 0); gpiodir |= dir_mask; /* Configure GPIOx as CMOS */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e7, 0); + snd_hda_codec_write(codec, fg, 0, 0x7e7, 0); - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, fg, 0, AC_VERB_SET_GPIO_MASK, gpiomask); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DIRECTION, gpiodir); /* sync */ msleep(1); - snd_hda_codec_read(codec, codec->afg, 0, + snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_GPIO_DATA, gpiostate); /* sync */ } @@ -387,7 +388,7 @@ static unsigned int stac_vref_led_power_filter(struct hda_codec *codec, hda_nid_t nid, unsigned int power_state) { - if (nid == codec->afg && power_state == AC_PWRST_D3) + if (nid == codec->core.afg && power_state == AC_PWRST_D3) return AC_PWRST_D1; return snd_hda_gen_path_power_filter(codec, nid, power_state); } @@ -432,7 +433,7 @@ static void stac_update_outputs(struct hda_codec *codec) if (spec->gpio_mute) spec->gen.master_mute = - !(snd_hda_codec_read(codec, codec->afg, 0, + !(snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0) & spec->gpio_mute); snd_hda_gen_update_outputs(codec); @@ -476,7 +477,7 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid, if (val != spec->power_map_bits) { spec->power_map_bits = val; if (do_write) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, val); } } @@ -508,7 +509,8 @@ static void jack_update_power(struct hda_codec *codec, false); } - snd_hda_codec_write(codec, codec->afg, 0, AC_VERB_IDT_SET_POWER_MAP, + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); } @@ -517,10 +519,10 @@ static void stac_vref_event(struct hda_codec *codec, { unsigned int data; - data = snd_hda_codec_read(codec, codec->afg, 0, + data = snd_hda_codec_read(codec, codec->core.afg, 0, AC_VERB_GET_GPIO_DATA, 0); /* toggle VREF state based on GPIOx status */ - snd_hda_codec_write(codec, codec->afg, 0, 0x7e0, + snd_hda_codec_write(codec, codec->core.afg, 0, 0x7e0, !!(data & (1 << event->private_data))); } @@ -622,7 +624,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* Only return the bits defined by the shift value of the * first two bytes of the mask */ - dac_mode = snd_hda_codec_read(codec, codec->afg, 0, + dac_mode = snd_hda_codec_read(codec, codec->core.afg, 0, kcontrol->private_value & 0xFFFF, 0x0); dac_mode >>= spec->aloopback_shift; @@ -634,7 +636,7 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, dac_mode &= ~idx_val; } - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, kcontrol->private_value >> 16, dac_mode); return 1; @@ -658,11 +660,11 @@ static int stac_aloopback_put(struct snd_kcontrol *kcontrol, /* check whether it's a HP laptop with a docking port */ static bool hp_bnb2011_with_dock(struct hda_codec *codec) { - if (codec->vendor_id != 0x111d7605 && - codec->vendor_id != 0x111d76d1) + if (codec->core.vendor_id != 0x111d7605 && + codec->core.vendor_id != 0x111d76d1) return false; - switch (codec->subsystem_id) { + switch (codec->core.subsystem_id) { case 0x103c1618: case 0x103c1619: case 0x103c161a: @@ -733,7 +735,7 @@ static void set_hp_led_gpio(struct hda_codec *codec) if (spec->gpio_led) return; - gpio = snd_hda_param_read(codec, codec->afg, AC_PAR_GPIO_CAP); + gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); gpio &= AC_GPIO_IO_COUNT; if (gpio > 3) spec->gpio_led = 0x08; /* GPIO 3 */ @@ -777,7 +779,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) &spec->gpio_led_polarity, &spec->gpio_led) == 2) { unsigned int max_gpio; - max_gpio = snd_hda_param_read(codec, codec->afg, + max_gpio = snd_hda_param_read(codec, codec->core.afg, AC_PAR_GPIO_CAP); max_gpio &= AC_GPIO_IO_COUNT; if (spec->gpio_led < max_gpio) @@ -807,7 +809,7 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity) * we statically set the GPIO - if not a B-series system * and default polarity is provided */ - if (!hp_blike_system(codec->subsystem_id) && + if (!hp_blike_system(codec->core.subsystem_id) && (default_polarity == 0 || default_polarity == 1)) { set_hp_led_gpio(codec); spec->gpio_led_polarity = default_polarity; @@ -1048,12 +1050,9 @@ static const struct hda_verb stac92hd71bxx_core_init[] = { {} }; -static const struct hda_verb stac92hd71bxx_unmute_core_init[] = { +static const hda_nid_t stac92hd71bxx_unmute_nids[] = { /* 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)}, - {} + 0x0f, 0x0a, 0x0d, 0 }; static const struct hda_verb stac925x_core_init[] = { @@ -2132,8 +2131,10 @@ static void stac92hd83xxx_fixup_hp_mic_led(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->mic_mute_led_gpio = 0x08; /* GPIO3 */ +#ifdef CONFIG_PM /* resetting controller clears GPIO, so we need to keep on */ - codec->bus->power_keep_link_on = 1; + codec->core.power_caps &= ~AC_PWRST_CLKSTOP; +#endif } } @@ -3029,9 +3030,9 @@ static void stac92hd71bxx_fixup_hp_m4(struct hda_codec *codec, return; /* Enable VREF power saving on GPIO1 detect */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x02); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x02; @@ -3091,7 +3092,7 @@ static void stac92hd71bxx_fixup_hp(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (hp_blike_system(codec->subsystem_id)) { + if (hp_blike_system(codec->core.subsystem_id)) { unsigned int pin_cfg = snd_hda_codec_get_pincfg(codec, 0x0f); if (get_defcfg_device(pin_cfg) == AC_JACK_LINE_OUT || get_defcfg_device(pin_cfg) == AC_JACK_SPEAKER || @@ -3790,7 +3791,7 @@ static void stac927x_fixup_dell_dmic(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_PRE_PROBE) return; - if (codec->subsystem_id != 0x1028022f) { + if (codec->core.subsystem_id != 0x1028022f) { /* GPIO2 High = Enable EAPD */ spec->eapd_mask = spec->gpio_mask = 0x04; spec->gpio_dir = spec->gpio_data = 0x04; @@ -4051,9 +4052,9 @@ static void stac9205_fixup_dell_m43(struct hda_codec *codec, snd_hda_apply_pincfgs(codec, dell_9205_m43_pin_configs); /* Enable unsol response for GPIO4/Dock HP connection */ - snd_hda_codec_write_cache(codec, codec->afg, 0, + snd_hda_codec_write_cache(codec, codec->core.afg, 0, AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x10); - jack = snd_hda_jack_detect_enable_callback(codec, codec->afg, + jack = snd_hda_jack_detect_enable_callback(codec, codec->core.afg, stac_vref_event); if (!IS_ERR(jack)) jack->private_data = 0x01; @@ -4223,6 +4224,12 @@ static int stac_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + if (spec->vref_mute_led_nid) { + err = snd_hda_gen_fix_pin_power(codec, spec->vref_mute_led_nid); + if (err < 0) + return err; + } + /* setup analog beep controls */ if (spec->anabeep_nid > 0) { err = stac_auto_create_beep_ctls(codec, @@ -4259,6 +4266,10 @@ static int stac_parse_auto_config(struct hda_codec *codec) if (spec->aloopback_ctl && snd_hda_get_bool_hint(codec, "loopback") == 1) { + unsigned int wr_verb = + spec->aloopback_ctl->private_value >> 16; + if (snd_hdac_regmap_add_vendor_verb(&codec->core, wr_verb)) + return -ENOMEM; if (!snd_hda_gen_add_kctl(&spec->gen, NULL, spec->aloopback_ctl)) return -ENOMEM; } @@ -4294,7 +4305,7 @@ static int stac_init(struct hda_codec *codec) /* sync the power-map */ if (spec->num_pwrs) - snd_hda_codec_write(codec, codec->afg, 0, + snd_hda_codec_write(codec, codec->core.afg, 0, AC_VERB_IDT_SET_POWER_MAP, spec->power_map_bits); @@ -4330,7 +4341,7 @@ static void stac_shutup(struct hda_codec *codec) static void stac92hd_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) snd_iprintf(buffer, "Power-Map: 0x%02x\n", snd_hda_codec_read(codec, nid, 0, AC_VERB_IDT_GET_POWER_MAP, 0)); @@ -4341,7 +4352,7 @@ static void analog_loop_proc_hook(struct snd_info_buffer *buffer, unsigned int verb) { snd_iprintf(buffer, "Analog Loopback: 0x%02x\n", - snd_hda_codec_read(codec, codec->afg, 0, verb, 0)); + snd_hda_codec_read(codec, codec->core.afg, 0, verb, 0)); } /* stac92hd71bxx, stac92hd73xx */ @@ -4349,21 +4360,21 @@ static void stac92hd7x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { stac92hd_proc_hook(buffer, codec, nid); - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfa0); } static void stac9205_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfe0); } static void stac927x_proc_hook(struct snd_info_buffer *buffer, struct hda_codec *codec, hda_nid_t nid) { - if (nid == codec->afg) + if (nid == codec->core.afg) analog_loop_proc_hook(buffer, codec, 0xfeb); } #else @@ -4392,6 +4403,7 @@ static const struct hda_codec_ops stac_patch_ops = { #ifdef CONFIG_PM .suspend = stac_suspend, #endif + .stream_pm = snd_hda_gen_stream_pm, .reboot_notify = stac_shutup, }; @@ -4485,6 +4497,7 @@ static int patch_stac92hd73xx(struct hda_codec *codec) return err; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.mixer_nid = 0x1d; spec->have_spdif_mux = 1; @@ -4587,9 +4600,11 @@ static int patch_stac92hd83xxx(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4636,9 +4651,11 @@ static int patch_stac92hd95(struct hda_codec *codec) if (err < 0) return err; - codec->epss = 0; /* longer delay needed for D3 */ + /* longer delay needed for D3 */ + codec->core.power_caps &= ~AC_PWRST_EPSS; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4672,7 +4689,7 @@ static int patch_stac92hd95(struct hda_codec *codec) static int patch_stac92hd71bxx(struct hda_codec *codec) { struct sigmatel_spec *spec; - const struct hda_verb *unmute_init = stac92hd71bxx_unmute_core_init; + const hda_nid_t *unmute_nids = stac92hd71bxx_unmute_nids; int err; err = alloc_stac_spec(codec); @@ -4680,6 +4697,7 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) return err; spec = codec->spec; + codec->power_save_node = 1; spec->linear_tone_beep = 0; spec->gen.own_eapd_ctl = 1; spec->gen.power_down_unused = 1; @@ -4693,23 +4711,23 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) spec->gpio_dir = 0x01; spec->gpio_data = 0x01; - switch (codec->vendor_id) { + switch (codec->core.vendor_id) { case 0x111d76b6: /* 4 Port without Analog Mixer */ case 0x111d76b7: - unmute_init++; + unmute_nids++; break; case 0x111d7608: /* 5 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 0 || - (codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 0 || + (codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ /* disable VSW */ - unmute_init++; + unmute_nids++; snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0); snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3); break; case 0x111d7603: /* 6 Port with Analog Mixer */ - if ((codec->revision_id & 0xf) == 1) + if ((codec->core.revision_id & 0xf) == 1) spec->stream_delay = 40; /* 40 milliseconds */ break; @@ -4718,8 +4736,12 @@ static int patch_stac92hd71bxx(struct hda_codec *codec) if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB) snd_hda_add_verbs(codec, stac92hd71bxx_core_init); - if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) - snd_hda_sequence_write_cache(codec, unmute_init); + if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) { + const hda_nid_t *p; + for (p = unmute_nids; *p; p++) + snd_hda_codec_amp_init_stereo(codec, *p, HDA_INPUT, 0, + 0xff, 0x00); + } spec->aloopback_ctl = &stac92hd71bxx_loopback; spec->aloopback_mask = 0x50; @@ -5091,20 +5113,8 @@ MODULE_ALIAS("snd-hda-codec-id:111d*"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec"); -static struct hda_codec_preset_list sigmatel_list = { +static struct hda_codec_driver sigmatel_driver = { .preset = snd_hda_preset_sigmatel, - .owner = THIS_MODULE, }; -static int __init patch_sigmatel_init(void) -{ - return snd_hda_add_codec_preset(&sigmatel_list); -} - -static void __exit patch_sigmatel_exit(void) -{ - snd_hda_delete_codec_preset(&sigmatel_list); -} - -module_init(patch_sigmatel_init) -module_exit(patch_sigmatel_exit) +module_hda_codec_driver(sigmatel_driver); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 3de6d3d779c9..31a95cca015d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -99,7 +99,6 @@ struct via_spec { /* HP mode source */ unsigned int dmic_enabled; - unsigned int no_pin_power_ctl; enum VIA_HDA_CODEC codec_type; /* analog low-power control */ @@ -109,8 +108,7 @@ struct via_spec { int hp_work_active; int vt1708_jack_detect; - void (*set_widgets_power_state)(struct hda_codec *codec); - unsigned int dac_stream_tag[4]; + unsigned int beep_amp; }; static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); @@ -133,17 +131,18 @@ static struct via_spec *via_new_spec(struct hda_codec *codec) /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - spec->no_pin_power_ctl = 1; spec->gen.indep_hp = 1; spec->gen.keep_eapd_on = 1; spec->gen.pcm_playback_hook = via_playback_pcm_hook; spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; + codec->power_save_node = 1; + spec->gen.power_down_unused = 1; return spec; } static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) { - u32 vendor_id = codec->vendor_id; + u32 vendor_id = codec->core.vendor_id; u16 ven_id = vendor_id >> 16; u16 dev_id = vendor_id & 0xffff; enum VIA_HDA_CODEC codec_type; @@ -222,98 +221,13 @@ static void vt1708_update_hp_work(struct hda_codec *codec) if (!spec->hp_work_active) { codec->jackpoll_interval = msecs_to_jiffies(100); snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); - queue_delayed_work(codec->bus->workq, - &codec->jackpoll_work, 0); + schedule_delayed_work(&codec->jackpoll_work, 0); spec->hp_work_active = true; } } else if (!hp_detect_with_aa(codec)) vt1708_stop_hp_work(codec); } -static void set_widgets_power_state(struct hda_codec *codec) -{ -#if 0 /* FIXME: the assumed connections don't match always with the - * actual routes by the generic parser, so better to disable - * the control for safety. - */ - struct via_spec *spec = codec->spec; - if (spec->set_widgets_power_state) - spec->set_widgets_power_state(codec); -#endif -} - -static void update_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm) -{ - if (snd_hda_check_power_state(codec, nid, parm)) - return; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); -} - -static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int parm, unsigned int index) -{ - struct via_spec *spec = codec->spec; - unsigned int format; - - if (snd_hda_check_power_state(codec, nid, parm)) - return; - format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - if (format && (spec->dac_stream_tag[index] != format)) - spec->dac_stream_tag[index] = format; - - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); - if (parm == AC_PWRST_D0) { - format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); - if (!format && (spec->dac_stream_tag[index] != format)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_CHANNEL_STREAMID, - spec->dac_stream_tag[index]); - } -} - -static bool smart51_enabled(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - return spec->gen.ext_channel_count > 2; -} - -static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) -{ - struct via_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->gen.multi_ios; i++) - if (spec->gen.multi_io[i].pin == pin) - return true; - return false; -} - -static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, - unsigned int *affected_parm) -{ - unsigned parm; - unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); - unsigned no_presence = (def_conf & AC_DEFCFG_MISC) - >> AC_DEFCFG_MISC_SHIFT - & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ - struct via_spec *spec = codec->spec; - unsigned present = 0; - - no_presence |= spec->no_pin_power_ctl; - if (!no_presence) - present = snd_hda_jack_detect(codec, nid); - if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) - || ((no_presence || present) - && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { - *affected_parm = AC_PWRST_D0; /* if it's connected */ - parm = AC_PWRST_D0; - } else - parm = AC_PWRST_D3; - - update_power_state(codec, nid, parm); -} - static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -324,8 +238,7 @@ static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct via_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; + ucontrol->value.enumerated.item[0] = codec->power_save_node; return 0; } @@ -334,12 +247,12 @@ static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct via_spec *spec = codec->spec; - unsigned int val = !ucontrol->value.enumerated.item[0]; + bool val = !!ucontrol->value.enumerated.item[0]; - if (val == spec->no_pin_power_ctl) + if (val == codec->power_save_node) return 0; - spec->no_pin_power_ctl = val; - set_widgets_power_state(codec); + codec->power_save_node = val; + spec->gen.power_down_unused = val; analog_low_current_mode(codec); return 1; } @@ -355,6 +268,59 @@ static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { {} /* terminator */ }; +#ifdef CONFIG_SND_HDA_INPUT_BEEP +static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid, + int idx, int dir) +{ + spec->gen.beep_nid = nid; + spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); +} + +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new cxt_beep_mixer[] = { + HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), + { } /* end */ +}; + +/* create beep controls if needed */ +static int add_beep_ctls(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + int err; + + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = cxt_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, 0, kctl); + if (err < 0) + return err; + } + } + return 0; +} + +static void auto_parse_beep(struct hda_codec *codec) +{ + struct via_spec *spec = codec->spec; + hda_nid_t nid; + + for_each_hda_codec_node(nid, codec) + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { + set_beep_amp(spec, nid, 0, HDA_OUTPUT); + break; + } +} +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#define add_beep_ctls(codec) 0 +#define auto_parse_beep(codec) +#endif /* check AA path's mute status */ static bool is_aa_path_mute(struct hda_codec *codec) @@ -384,7 +350,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) bool enable; unsigned int verb, parm; - if (spec->no_pin_power_ctl) + if (!codec->power_save_node) enable = false; else enable = is_aa_path_mute(codec) && !spec->gen.active_streams; @@ -424,7 +390,7 @@ static void __analog_low_current_mode(struct hda_codec *codec, bool force) return; /* other codecs are not supported */ } /* send verb */ - snd_hda_codec_write(codec, codec->afg, 0, verb, parm); + snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); } static void analog_low_current_mode(struct hda_codec *codec) @@ -441,8 +407,11 @@ static int via_build_controls(struct hda_codec *codec) if (err < 0) return err; - if (spec->set_widgets_power_state) - spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; + err = add_beep_ctls(codec); + if (err < 0) + return err; + + spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -486,7 +455,6 @@ static int via_suspend(struct hda_codec *codec) static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct via_spec *spec = codec->spec; - set_widgets_power_state(codec); analog_low_current_mode(codec); vt1708_update_hp_work(codec); return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); @@ -504,6 +472,7 @@ static const struct hda_codec_ops via_patch_ops = { .init = via_init, .free = via_free, .unsol_event = snd_hda_jack_unsol_event, + .stream_pm = snd_hda_gen_stream_pm, #ifdef CONFIG_PM .suspend = via_suspend, .check_power_status = via_check_power_status, @@ -574,34 +543,6 @@ static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { {} /* terminator */ }; -static void via_jack_powerstate_event(struct hda_codec *codec, - struct hda_jack_callback *tbl) -{ - set_widgets_power_state(codec); -} - -static void via_set_jack_unsol_events(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->gen.autocfg; - hda_nid_t pin; - int i; - - for (i = 0; i < cfg->line_outs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - via_jack_powerstate_event); - } - - for (i = 0; i < cfg->num_inputs; i++) { - pin = cfg->line_out_pins[i]; - if (pin && is_jack_detectable(codec, pin)) - snd_hda_jack_detect_enable_callback(codec, pin, - via_jack_powerstate_event); - } -} - static const struct badness_table via_main_out_badness = { .no_primary_dac = 0x10000, .no_dac = 0x4000, @@ -631,11 +572,15 @@ static int via_parse_auto_config(struct hda_codec *codec) if (err < 0) return err; + auto_parse_beep(codec); + err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); if (err < 0) return err; - via_set_jack_unsol_events(codec); + /* disable widget PM at start for compatibility */ + codec->power_save_node = 0; + spec->gen.power_down_unused = 0; return 0; } @@ -648,7 +593,6 @@ static int via_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->init_verbs[i]); /* init power states */ - set_widgets_power_state(codec); __analog_low_current_mode(codec, true); snd_hda_gen_init(codec); @@ -676,15 +620,17 @@ static int vt1708_build_pcms(struct hda_codec *codec) int i, err; err = snd_hda_gen_build_pcms(codec); - if (err < 0 || codec->vendor_id != 0x11061708) + if (err < 0 || codec->core.vendor_id != 0x11061708) return err; /* We got noisy outputs on the right channel on VT1708 when * 24bit samples are used. Until any workaround is found, * disable the 24bit format, so far. */ - for (i = 0; i < codec->num_pcms; i++) { - struct hda_pcm *info = &spec->gen.pcm_rec[i]; + for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { + struct hda_pcm *info = spec->gen.pcm_rec[i]; + if (!info) + continue; if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || info->pcm_type != HDA_PCM_TYPE_AUDIO) continue; @@ -766,78 +712,6 @@ static int patch_vt1709(struct hda_codec *codec) return 0; } -static void set_widgets_power_state_vt1708B(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - int is_8ch = 0; - if ((spec->codec_type != VT1708B_4CH) && - (codec->vendor_id != 0x11064397)) - is_8ch = 1; - - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) - == ((spec->codec_type == VT1708S) ? 5 : 0)); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW 0/1 (13h/14h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW6 (22h), SW2 (26h), AOW2 (24h) */ - if (is_8ch) { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x22, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x24, parm); - } else if (codec->vendor_id == 0x11064397) { - /* PW7(23h), SW2(27h), AOW2(25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x25, parm); - } - - /* PW 3/4/7 (1ch/1dh/23h) */ - parm = AC_PWRST_D3; - /* force to D0 for internal Speaker */ - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - if (is_8ch) - set_pin_power_state(codec, 0x23, &parm); - - /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - if (is_8ch) { - update_power_state(codec, 0x25, parm); - update_power_state(codec, 0x27, parm); - } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) - update_power_state(codec, 0x25, parm); -} - static int patch_vt1708S(struct hda_codec *codec); static int patch_vt1708B(struct hda_codec *codec) { @@ -862,9 +736,6 @@ static int patch_vt1708B(struct hda_codec *codec) } codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; - return 0; } @@ -905,19 +776,19 @@ static int patch_vt1708S(struct hda_codec *codec) /* correct names for VT1708BCE */ if (get_codec_type(codec) == VT1708BCE) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* correct names for VT1705 */ - if (codec->vendor_id == 0x11064397) { - kfree(codec->chip_name); - codec->chip_name = kstrdup("VT1705", GFP_KERNEL); - snprintf(codec->bus->card->mixername, - sizeof(codec->bus->card->mixername), - "%s %s", codec->vendor_name, codec->chip_name); + if (codec->core.vendor_id == 0x11064397) { + kfree(codec->core.chip_name); + codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); + snprintf(codec->card->mixername, + sizeof(codec->card->mixername), + "%s %s", codec->core.vendor_name, codec->core.chip_name); } /* automatic parse from the BIOS config */ @@ -930,8 +801,6 @@ static int patch_vt1708S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1708B; return 0; } @@ -945,36 +814,6 @@ static const struct hda_verb vt1702_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1702(struct hda_codec *codec) -{ - int imux_is_smixer = - snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - unsigned int parm; - /* inputs */ - /* PW 1/2/5 (14h/15h/18h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x14, &parm); - set_pin_power_state(codec, 0x15, &parm); - set_pin_power_state(codec, 0x18, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ - /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ - update_power_state(codec, 0x13, parm); - update_power_state(codec, 0x12, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x20, parm); - - /* outputs */ - /* PW 3/4 (16h/17h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x17, &parm); - set_pin_power_state(codec, 0x16, &parm); - /* MW0 (1ah), AOW 0/1 (10h/1dh) */ - update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x1d, parm); -} - static int patch_vt1702(struct hda_codec *codec) { struct via_spec *spec; @@ -1004,8 +843,6 @@ static int patch_vt1702(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1702; return 0; } @@ -1020,71 +857,6 @@ static const struct hda_verb vt1718S_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1718S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm, parm2; - /* MUX6 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x1a, parm); - parm2 = parm; /* for pin 0x0b */ - - /* PW2 (26h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0xa, parm); - - /* PW0 (24h), AOW0 (8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x8, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) - parm = parm2; - update_power_state(codec, 0xb, parm); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); - - /* PW1 (25h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x9, parm); - - if (spec->gen.indep_hp_enabled) { - /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x1b, parm); - update_power_state(codec, 0x34, parm); - update_power_state(codec, 0xc, parm); - } -} - /* Add a connection to the primary DAC from AA-mixer for some codecs * This isn't listed from the raw info, but the chip has a secret connection. */ @@ -1105,8 +877,7 @@ static int add_secret_dac_path(struct hda_codec *codec) } /* find the primary DAC and add to the connection list */ - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { + for_each_hda_codec_node(nid, codec) { unsigned int caps = get_wcaps(codec, nid); if (get_wcaps_type(caps) == AC_WID_AUD_OUT && !(caps & AC_WCAP_DIGITAL)) { @@ -1145,9 +916,6 @@ static int patch_vt1718S(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1718S; - return 0; } @@ -1187,7 +955,6 @@ static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_CONNECT_SEL, index); spec->dmic_enabled = index; - set_widgets_power_state(codec); return 1; } @@ -1222,95 +989,6 @@ static const struct hda_verb vt1716S_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1716S(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int mono_out, present; - /* SW0 (17h) = stereo mixer */ - imux_is_smixer = - (snd_hda_codec_read(codec, 0x17, 0, - AC_VERB_GET_CONNECT_SEL, 0x00) == 5); - /* inputs */ - /* PW 1/2/5 (1ah/1bh/1eh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1a, &parm); - set_pin_power_state(codec, 0x1b, &parm); - set_pin_power_state(codec, 0x1e, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* SW0 (17h), AIW0(13h) */ - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x13, parm); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1e, &parm); - /* PW11 (22h) */ - if (spec->dmic_enabled) - set_pin_power_state(codec, 0x22, &parm); - else - update_power_state(codec, 0x22, AC_PWRST_D3); - - /* SW2(26h), AIW1(14h) */ - update_power_state(codec, 0x26, parm); - update_power_state(codec, 0x14, parm); - - /* outputs */ - /* PW0 (19h), SW1 (18h), AOW1 (11h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x19, &parm); - /* Smart 5.1 PW2(1bh) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1b, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x11, parm); - - /* PW7 (23h), SW3 (27h), AOW3 (25h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x23, &parm); - /* Smart 5.1 PW1(1ah) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1a, &parm); - update_power_state(codec, 0x27, parm); - - /* Smart 5.1 PW5(1eh) */ - if (smart51_enabled(codec)) - set_pin_power_state(codec, 0x1e, &parm); - update_power_state(codec, 0x25, parm); - - /* Mono out */ - /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ - present = snd_hda_jack_detect(codec, 0x1c); - - if (present) - mono_out = 0; - else { - present = snd_hda_jack_detect(codec, 0x1d); - if (!spec->gen.indep_hp_enabled && present) - mono_out = 0; - else - mono_out = 1; - } - parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; - update_power_state(codec, 0x28, parm); - update_power_state(codec, 0x29, parm); - update_power_state(codec, 0x2a, parm); - - /* PW 3/4 (1ch/1dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x1c, &parm); - set_pin_power_state(codec, 0x1d, &parm); - /* HP Independent Mode, power on AOW3 */ - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x25, parm); - - /* force to D0 for internal Speaker */ - /* MW0 (16h), AOW0 (10h) */ - update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); - update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); -} - static int patch_vt1716S(struct hda_codec *codec) { struct via_spec *spec; @@ -1338,8 +1016,6 @@ static int patch_vt1716S(struct hda_codec *codec) spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1716S; return 0; } @@ -1365,98 +1041,6 @@ static const struct hda_verb vt1802_init_verbs[] = { { } }; -static void set_widgets_power_state_vt2002P(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm; - unsigned int present; - /* MUX9 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, parm); - - if (spec->codec_type == VT1802) { - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - } else { - /* PW4 (26h), MW4 (1ch), MUX4(37h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x37, parm); - } - - if (spec->codec_type == VT1802) { - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - } else { - /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x19, parm); - update_power_state(codec, 0x35, parm); - } - - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Class-D */ - /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) - update_power_state(codec, 0x14, parm); - else - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x34, parm); - - /* Mono Out */ - present = snd_hda_jack_detect(codec, 0x26); - - parm = present ? AC_PWRST_D3 : AC_PWRST_D0; - if (spec->codec_type == VT1802) { - /* PW15 (33h), MW8(1ch), MUX8(3ch) */ - update_power_state(codec, 0x33, parm); - update_power_state(codec, 0x1c, parm); - update_power_state(codec, 0x3c, parm); - } else { - /* PW15 (31h), MW8(17h), MUX8(3bh) */ - update_power_state(codec, 0x31, parm); - update_power_state(codec, 0x17, parm); - update_power_state(codec, 0x3b, parm); - } - /* MW9 (21h) */ - if (imux_is_smixer || !is_aa_path_mute(codec)) - update_power_state(codec, 0x21, AC_PWRST_D0); - else - update_power_state(codec, 0x21, AC_PWRST_D3); -} - /* * pin fix-up */ @@ -1540,8 +1124,6 @@ static int patch_vt2002P(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt2002P; return 0; } @@ -1555,81 +1137,6 @@ static const struct hda_verb vt1812_init_verbs[] = { { } }; -static void set_widgets_power_state_vt1812(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - unsigned int parm; - unsigned int present; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* AOW0 (8h)*/ - update_power_state(codec, 0x8, AC_PWRST_D0); - - /* PW4 (28h), MW4 (18h), MUX4(38h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x18, parm); - update_power_state(codec, 0x38, parm); - - /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x15, parm); - update_power_state(codec, 0x35, parm); - if (spec->gen.indep_hp_enabled) - update_power_state(codec, 0x9, AC_PWRST_D0); - - /* Internal Speaker */ - /* PW0 (24h), MW0(14h), MUX0(34h) */ - present = snd_hda_jack_detect(codec, 0x25); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - if (present) { - update_power_state(codec, 0x14, AC_PWRST_D3); - update_power_state(codec, 0x34, AC_PWRST_D3); - } else { - update_power_state(codec, 0x14, AC_PWRST_D0); - update_power_state(codec, 0x34, AC_PWRST_D0); - } - - - /* Mono Out */ - /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ - present = snd_hda_jack_detect(codec, 0x28); - - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x31, &parm); - if (present) { - update_power_state(codec, 0x1c, AC_PWRST_D3); - update_power_state(codec, 0x3c, AC_PWRST_D3); - update_power_state(codec, 0x3e, AC_PWRST_D3); - } else { - update_power_state(codec, 0x1c, AC_PWRST_D0); - update_power_state(codec, 0x3c, AC_PWRST_D0); - update_power_state(codec, 0x3e, AC_PWRST_D0); - } - - /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x33, &parm); - update_power_state(codec, 0x1d, parm); - update_power_state(codec, 0x3d, parm); - -} - /* patch for vt1812 */ static int patch_vt1812(struct hda_codec *codec) { @@ -1656,8 +1163,6 @@ static int patch_vt1812(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt1812; return 0; } @@ -1673,84 +1178,6 @@ static const struct hda_verb vt3476_init_verbs[] = { { } }; -static void set_widgets_power_state_vt3476(struct hda_codec *codec) -{ - struct via_spec *spec = codec->spec; - int imux_is_smixer; - unsigned int parm, parm2; - /* MUX10 (1eh) = stereo mixer */ - imux_is_smixer = - snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; - /* inputs */ - /* PW 5/6/7 (29h/2ah/2bh) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x29, &parm); - set_pin_power_state(codec, 0x2a, &parm); - set_pin_power_state(codec, 0x2b, &parm); - if (imux_is_smixer) - parm = AC_PWRST_D0; - /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ - update_power_state(codec, 0x1e, parm); - update_power_state(codec, 0x1f, parm); - update_power_state(codec, 0x10, parm); - update_power_state(codec, 0x11, parm); - - /* outputs */ - /* PW3 (27h), MW3(37h), AOW3 (bh) */ - if (spec->codec_type == VT1705CF) { - parm = AC_PWRST_D3; - update_power_state(codec, 0x27, parm); - update_power_state(codec, 0x37, parm); - } else { - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x27, &parm); - update_power_state(codec, 0x37, parm); - } - - /* PW2 (26h), MW2(36h), AOW2 (ah) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x26, &parm); - update_power_state(codec, 0x36, parm); - if (smart51_enabled(codec)) { - /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ - set_pin_power_state(codec, 0x2b, &parm); - update_power_state(codec, 0x3b, parm); - update_power_state(codec, 0x1b, parm); - } - update_conv_power_state(codec, 0xa, parm, 2); - - /* PW1 (25h), MW1(35h), AOW1 (9h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x25, &parm); - update_power_state(codec, 0x35, parm); - if (smart51_enabled(codec)) { - /* PW6(2ah), MW6(3ah), MUX6(1ah) */ - set_pin_power_state(codec, 0x2a, &parm); - update_power_state(codec, 0x3a, parm); - update_power_state(codec, 0x1a, parm); - } - update_conv_power_state(codec, 0x9, parm, 1); - - /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x28, &parm); - update_power_state(codec, 0x38, parm); - update_power_state(codec, 0x18, parm); - if (spec->gen.indep_hp_enabled) - update_conv_power_state(codec, 0xb, parm, 3); - parm2 = parm; /* for pin 0x0b */ - - /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ - parm = AC_PWRST_D3; - set_pin_power_state(codec, 0x24, &parm); - update_power_state(codec, 0x34, parm); - if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) - parm = parm2; - update_conv_power_state(codec, 0x8, parm, 0); - /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ - update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); -} - static int patch_vt3476(struct hda_codec *codec) { struct via_spec *spec; @@ -1774,9 +1201,6 @@ static int patch_vt3476(struct hda_codec *codec) spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; codec->patch_ops = via_patch_ops; - - spec->set_widgets_power_state = set_widgets_power_state_vt3476; - return 0; } @@ -1884,23 +1308,11 @@ static const struct hda_codec_preset snd_hda_preset_via[] = { MODULE_ALIAS("snd-hda-codec-id:1106*"); -static struct hda_codec_preset_list via_list = { +static struct hda_codec_driver via_driver = { .preset = snd_hda_preset_via, - .owner = THIS_MODULE, }; MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("VIA HD-audio codec"); -static int __init patch_via_init(void) -{ - return snd_hda_add_codec_preset(&via_list); -} - -static void __exit patch_via_exit(void) -{ - snd_hda_delete_codec_preset(&via_list); -} - -module_init(patch_via_init) -module_exit(patch_via_exit) +module_hda_codec_driver(via_driver); diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c index 6ba0b5517c40..0a4ad5feb82e 100644 --- a/sound/pci/hda/thinkpad_helper.c +++ b/sound/pci/hda/thinkpad_helper.c @@ -21,7 +21,7 @@ static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context, static bool is_thinkpad(struct hda_codec *codec) { bool found = false; - if (codec->subsystem_id >> 16 != 0x17aa) + if (codec->core.subsystem_id >> 16 != 0x17aa) return false; if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found) return true; diff --git a/sound/pci/ice1712/wtm.c b/sound/pci/ice1712/wtm.c index bcf30a387b87..9906119e0954 100644 --- a/sound/pci/ice1712/wtm.c +++ b/sound/pci/ice1712/wtm.c @@ -29,12 +29,19 @@ #include <linux/interrupt.h> #include <linux/init.h> #include <sound/core.h> +#include <sound/tlv.h> +#include <linux/slab.h> #include "ice1712.h" #include "envy24ht.h" #include "wtm.h" #include "stac946x.h" +struct wtm_spec { + /* rate change needs atomic mute/unmute of all dacs*/ + struct mutex mute_mutex; +}; + /* * 2*ADC 6*DAC no1 ringbuffer r/w on i2c bus @@ -68,15 +75,65 @@ static inline unsigned char stac9460_2_get(struct snd_ice1712 *ice, int reg) /* * DAC mute control */ +static void stac9460_dac_mute_all(struct snd_ice1712 *ice, unsigned char mute, + unsigned short int *change_mask) +{ + unsigned char new, old; + int id, idx, change; + + /*stac9460 1*/ + for (id = 0; id < 7; id++) { + if (*change_mask & (0x01 << id)) { + if (id == 0) + idx = STAC946X_MASTER_VOLUME; + else + idx = STAC946X_LF_VOLUME - 1 + id; + old = stac9460_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) { + stac9460_put(ice, idx, new); + *change_mask = *change_mask | (0x01 << id); + } else { + *change_mask = *change_mask & ~(0x01 << id); + } + } + } + + /*stac9460 2*/ + for (id = 0; id < 3; id++) { + if (*change_mask & (0x01 << (id + 7))) { + if (id == 0) + idx = STAC946X_MASTER_VOLUME; + else + idx = STAC946X_LF_VOLUME - 1 + id; + old = stac9460_2_get(ice, idx); + new = (~mute << 7 & 0x80) | (old & ~0x80); + change = (new != old); + if (change) { + stac9460_2_put(ice, idx, new); + *change_mask = *change_mask | (0x01 << id); + } else { + *change_mask = *change_mask & ~(0x01 << id); + } + } + } +} + + + #define stac9460_dac_mute_info snd_ctl_boolean_mono_info static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol); + struct wtm_spec *spec = ice->spec; unsigned char val; int idx, id; + mutex_lock(&spec->mute_mutex); + if (kcontrol->private_value) { idx = STAC946X_MASTER_VOLUME; id = 0; @@ -89,6 +146,8 @@ static int stac9460_dac_mute_get(struct snd_kcontrol *kcontrol, else val = stac9460_2_get(ice, idx - 6); ucontrol->value.integer.value[0] = (~val >> 7) & 0x1; + + mutex_unlock(&spec->mute_mutex); return 0; } @@ -338,8 +397,14 @@ static int stac9460_adc_vol_put(struct snd_kcontrol *kcontrol, /* * MIC / LINE switch fonction */ +static int stac9460_mic_sw_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char * const texts[2] = { "Line In", "Mic" }; + + return snd_ctl_enum_info(uinfo, 1, 2, texts); +} -#define stac9460_mic_sw_info snd_ctl_boolean_mono_info static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -353,7 +418,7 @@ static int stac9460_mic_sw_get(struct snd_kcontrol *kcontrol, val = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); else val = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); - ucontrol->value.integer.value[0] = ~val>>7 & 0x1; + ucontrol->value.enumerated.item[0] = (val >> 7) & 0x1; return 0; } @@ -369,7 +434,7 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, old = stac9460_get(ice, STAC946X_GENERAL_PURPOSE); else old = stac9460_2_get(ice, STAC946X_GENERAL_PURPOSE); - new = (~ucontrol->value.integer.value[0] << 7 & 0x80) | (old & ~0x80); + new = (ucontrol->value.enumerated.item[0] << 7 & 0x80) | (old & ~0x80); change = (new != old); if (change) { if (id == 0) @@ -380,17 +445,63 @@ static int stac9460_mic_sw_put(struct snd_kcontrol *kcontrol, return change; } + +/* + * Handler for setting correct codec rate - called when rate change is detected + */ +static void stac9460_set_rate_val(struct snd_ice1712 *ice, unsigned int rate) +{ + unsigned char old, new; + unsigned short int changed; + struct wtm_spec *spec = ice->spec; + + if (rate == 0) /* no hint - S/PDIF input is master, simply return */ + return; + else if (rate <= 48000) + new = 0x08; /* 256x, base rate mode */ + else if (rate <= 96000) + new = 0x11; /* 256x, mid rate mode */ + else + new = 0x12; /* 128x, high rate mode */ + + old = stac9460_get(ice, STAC946X_MASTER_CLOCKING); + if (old == new) + return; + /* change detected, setting master clock, muting first */ + /* due to possible conflicts with mute controls - mutexing */ + mutex_lock(&spec->mute_mutex); + /* we have to remember current mute status for each DAC */ + changed = 0xFFFF; + stac9460_dac_mute_all(ice, 0, &changed); + /*printk(KERN_DEBUG "Rate change: %d, new MC: 0x%02x\n", rate, new);*/ + stac9460_put(ice, STAC946X_MASTER_CLOCKING, new); + stac9460_2_put(ice, STAC946X_MASTER_CLOCKING, new); + udelay(10); + /* unmuting - only originally unmuted dacs - + * i.e. those changed when muting */ + stac9460_dac_mute_all(ice, 1, &changed); + mutex_unlock(&spec->mute_mutex); +} + + +/*Limits value in dB for fader*/ +static const DECLARE_TLV_DB_SCALE(db_scale_dac, -19125, 75, 0); +static const DECLARE_TLV_DB_SCALE(db_scale_adc, 0, 150, 0); + /* * Control tabs */ static struct snd_kcontrol_new stac9640_controls[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), .name = "Master Playback Switch", .info = stac9460_dac_mute_info, .get = stac9460_dac_mute_get, .put = stac9460_dac_mute_put, - .private_value = 1 + .private_value = 1, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -402,7 +513,7 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "MIC/Line switch", + .name = "MIC/Line Input Enum", .count = 2, .info = stac9460_mic_sw_info, .get = stac9460_mic_sw_get, @@ -419,11 +530,15 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "DAC Volume", .count = 8, .info = stac9460_dac_vol_info, .get = stac9460_dac_vol_get, .put = stac9460_dac_vol_put, + .tlv = { .p = db_scale_dac } }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -435,12 +550,15 @@ static struct snd_kcontrol_new stac9640_controls[] = { }, { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "ADC Volume", .count = 2, .info = stac9460_adc_vol_info, .get = stac9460_adc_vol_get, .put = stac9460_adc_vol_put, - + .tlv = { .p = db_scale_adc } } }; @@ -463,41 +581,53 @@ static int wtm_add_controls(struct snd_ice1712 *ice) static int wtm_init(struct snd_ice1712 *ice) { - static unsigned short stac_inits_prodigy[] = { + static unsigned short stac_inits_wtm[] = { STAC946X_RESET, 0, + STAC946X_MASTER_CLOCKING, 0x11, (unsigned short)-1 }; unsigned short *p; + struct wtm_spec *spec; /*WTM 192M*/ ice->num_total_dacs = 8; ice->num_total_adcs = 4; ice->force_rdma1 = 1; + /*init mutex for dac mute conflict*/ + spec = kzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return -ENOMEM; + ice->spec = spec; + mutex_init(&spec->mute_mutex); + + /*initialize codec*/ - p = stac_inits_prodigy; + p = stac_inits_wtm; for (; *p != (unsigned short)-1; p += 2) { stac9460_put(ice, p[0], p[1]); stac9460_2_put(ice, p[0], p[1]); } + ice->gpio.set_pro_rate = stac9460_set_rate_val; return 0; } static unsigned char wtm_eeprom[] = { - 0x47, /*SYSCONF: clock 192KHz, 4ADC, 8DAC */ - 0x80, /* ACLINK : I2S */ - 0xf8, /* I2S: vol; 96k, 24bit, 192k */ - 0xc1 /*SPDIF: out-en, spidf ext out*/, - 0x9f, /* GPIO_DIR */ - 0xff, /* GPIO_DIR1 */ - 0x7f, /* GPIO_DIR2 */ - 0x9f, /* GPIO_MASK */ - 0xff, /* GPIO_MASK1 */ - 0x7f, /* GPIO_MASK2 */ - 0x16, /* GPIO_STATE */ - 0x80, /* GPIO_STATE1 */ - 0x00, /* GPIO_STATE2 */ + [ICE_EEP2_SYSCONF] = 0x67, /*SYSCONF: clock 192KHz, mpu401, + 4ADC, 8DAC */ + [ICE_EEP2_ACLINK] = 0x80, /* ACLINK : I2S */ + [ICE_EEP2_I2S] = 0xf8, /* I2S: vol; 96k, 24bit, 192k */ + [ICE_EEP2_SPDIF] = 0xc1, /*SPDIF: out-en, spidf ext out*/ + [ICE_EEP2_GPIO_DIR] = 0x9f, + [ICE_EEP2_GPIO_DIR1] = 0xff, + [ICE_EEP2_GPIO_DIR2] = 0x7f, + [ICE_EEP2_GPIO_MASK] = 0x9f, + [ICE_EEP2_GPIO_MASK1] = 0xff, + [ICE_EEP2_GPIO_MASK2] = 0x7f, + [ICE_EEP2_GPIO_STATE] = 0x16, + [ICE_EEP2_GPIO_STATE1] = 0x80, + [ICE_EEP2_GPIO_STATE2] = 0x00, }; diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c index 2c5484eeb963..749069aa6997 100644 --- a/sound/pci/intel8x0.c +++ b/sound/pci/intel8x0.c @@ -1795,7 +1795,7 @@ static struct ac97_pcm ac97_pcm_defs[] = { }, }; -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x0e11, .subdevice = 0x000e, diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index ca67f896d117..cb666c73712d 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -6043,23 +6043,30 @@ hdspm_hw_constraints_aes32_sample_rates = { .mask = 0 }; -static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) +static int snd_hdspm_open(struct snd_pcm_substream *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); spin_lock_irq(&hdspm->lock); - snd_pcm_set_sync(substream); + runtime->hw = (playback) ? snd_hdspm_playback_subinfo : + snd_hdspm_capture_subinfo; + if (playback) { + if (hdspm->capture_substream == NULL) + hdspm_stop_audio(hdspm); - runtime->hw = snd_hdspm_playback_subinfo; - - if (hdspm->capture_substream == NULL) - hdspm_stop_audio(hdspm); + hdspm->playback_pid = current->pid; + hdspm->playback_substream = substream; + } else { + if (hdspm->playback_substream == NULL) + hdspm_stop_audio(hdspm); - hdspm->playback_pid = current->pid; - hdspm->playback_substream = substream; + hdspm->capture_pid = current->pid; + hdspm->capture_substream = substream; + } spin_unlock_irq(&hdspm->lock); @@ -6094,108 +6101,42 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream) &hdspm_hw_constraints_aes32_sample_rates); } else { snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_out_channels, hdspm, + (playback ? + snd_hdspm_hw_rule_rate_out_channels : + snd_hdspm_hw_rule_rate_in_channels), hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); } snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_out_channels, hdspm, + (playback ? snd_hdspm_hw_rule_out_channels : + snd_hdspm_hw_rule_in_channels), hdspm, SNDRV_PCM_HW_PARAM_CHANNELS, -1); snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_out_channels_rate, hdspm, + (playback ? snd_hdspm_hw_rule_out_channels_rate : + snd_hdspm_hw_rule_in_channels_rate), hdspm, SNDRV_PCM_HW_PARAM_RATE, -1); return 0; } -static int snd_hdspm_playback_release(struct snd_pcm_substream *substream) +static int snd_hdspm_release(struct snd_pcm_substream *substream) { struct hdspm *hdspm = snd_pcm_substream_chip(substream); + bool playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); spin_lock_irq(&hdspm->lock); - hdspm->playback_pid = -1; - hdspm->playback_substream = NULL; - - spin_unlock_irq(&hdspm->lock); - - return 0; -} - - -static int snd_hdspm_capture_open(struct snd_pcm_substream *substream) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - spin_lock_irq(&hdspm->lock); - snd_pcm_set_sync(substream); - runtime->hw = snd_hdspm_capture_subinfo; - - if (hdspm->playback_substream == NULL) - hdspm_stop_audio(hdspm); - - hdspm->capture_pid = current->pid; - hdspm->capture_substream = substream; - - spin_unlock_irq(&hdspm->lock); - - snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24); - snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - - switch (hdspm->io_type) { - case AIO: - case RayDAT: - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - 32, 4096); - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - 16384, 16384); - break; - - default: - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - 64, 8192); - snd_pcm_hw_constraint_minmax(runtime, - SNDRV_PCM_HW_PARAM_PERIODS, - 2, 2); - break; - } - - if (AES32 == hdspm->io_type) { - runtime->hw.rates |= SNDRV_PCM_RATE_KNOT; - snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - &hdspm_hw_constraints_aes32_sample_rates); + if (playback) { + hdspm->playback_pid = -1; + hdspm->playback_substream = NULL; } else { - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, - snd_hdspm_hw_rule_rate_in_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); + hdspm->capture_pid = -1; + hdspm->capture_substream = NULL; } - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_in_channels, hdspm, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - - snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, - snd_hdspm_hw_rule_in_channels_rate, hdspm, - SNDRV_PCM_HW_PARAM_RATE, -1); - - return 0; -} - -static int snd_hdspm_capture_release(struct snd_pcm_substream *substream) -{ - struct hdspm *hdspm = snd_pcm_substream_chip(substream); - - spin_lock_irq(&hdspm->lock); - - hdspm->capture_pid = -1; - hdspm->capture_substream = NULL; - spin_unlock_irq(&hdspm->lock); + return 0; } @@ -6413,21 +6354,9 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, return 0; } -static struct snd_pcm_ops snd_hdspm_playback_ops = { - .open = snd_hdspm_playback_open, - .close = snd_hdspm_playback_release, - .ioctl = snd_hdspm_ioctl, - .hw_params = snd_hdspm_hw_params, - .hw_free = snd_hdspm_hw_free, - .prepare = snd_hdspm_prepare, - .trigger = snd_hdspm_trigger, - .pointer = snd_hdspm_hw_pointer, - .page = snd_pcm_sgbuf_ops_page, -}; - -static struct snd_pcm_ops snd_hdspm_capture_ops = { - .open = snd_hdspm_capture_open, - .close = snd_hdspm_capture_release, +static struct snd_pcm_ops snd_hdspm_ops = { + .open = snd_hdspm_open, + .close = snd_hdspm_release, .ioctl = snd_hdspm_ioctl, .hw_params = snd_hdspm_hw_params, .hw_free = snd_hdspm_hw_free, @@ -6521,9 +6450,9 @@ static int snd_hdspm_create_pcm(struct snd_card *card, strcpy(pcm->name, hdspm->card_name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_hdspm_playback_ops); + &snd_hdspm_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_hdspm_capture_ops); + &snd_hdspm_ops); pcm->info_flags = SNDRV_PCM_INFO_JOINT_DUPLEX; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c index 8622283e89f3..3dd038bdb204 100644 --- a/sound/pci/via82xx.c +++ b/sound/pci/via82xx.c @@ -1812,7 +1812,7 @@ static void snd_via82xx_mixer_free_ac97(struct snd_ac97 *ac97) chip->ac97 = NULL; } -static struct ac97_quirk ac97_quirks[] = { +static const struct ac97_quirk ac97_quirks[] = { { .subvendor = 0x1106, .subdevice = 0x4161, diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa0236b..3ba52da18bc6 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig" source "sound/soc/intel/Kconfig" source "sound/soc/mxs/Kconfig" source "sound/soc/pxa/Kconfig" +source "sound/soc/qcom/Kconfig" source "sound/soc/rockchip/Kconfig" source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f67c8db..974ba708b482 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += nuc900/ obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ +obj-$(CONFIG_SND_SOC) += qcom/ obj-$(CONFIG_SND_SOC) += rockchip/ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 1579e994acf8..e7d08806f3e9 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -25,7 +25,8 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + depends on ARCH_AT91 || COMPILE_TEST + depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -35,7 +36,8 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_ATMEL_SOC_WM8904 tristate "Atmel ASoC driver for boards using WM8904 codec" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && I2C + depends on ARCH_AT91 || COMPILE_TEST + depends on ATMEL_SSC && SND_ATMEL_SOC && I2C select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_DMA select SND_SOC_WM8904 @@ -45,7 +47,8 @@ config SND_ATMEL_SOC_WM8904 config SND_AT91_SOC_SAM9X5_WM8731 tristate "SoC Audio support for WM8731-based at91sam9x5 board" - depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC + depends on ARCH_AT91 || COMPILE_TEST + depends on ATMEL_SSC && SND_ATMEL_SOC && SND_SOC_I2C_AND_SPI select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_DMA select SND_SOC_WM8731 diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 466a821da98c..b327e5cc8de3 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -1,10 +1,8 @@ # AT91 Platform Support -snd-soc-atmel-pcm-objs := atmel-pcm.o snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o -obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index b8e7bad05eb1..b6625c8c411b 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -54,7 +54,7 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = { .period_bytes_max = 2 * 0xffff, /* if 2 bytes format */ .periods_min = 8, .periods_max = 1024, /* no limit */ - .buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE, + .buffer_bytes_max = 512 * 1024, }; /** @@ -119,7 +119,7 @@ static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream, static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = { .prepare_slave_config = atmel_pcm_configure_dma, .pcm_hardware = &atmel_pcm_dma_hardware, - .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE, + .prealloc_buffer_size = 64 * 1024, }; int atmel_pcm_dma_platform_register(struct device *dev) diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index a366b3503c28..da861b44413f 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -47,6 +47,85 @@ #include "atmel-pcm.h" +static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, + int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = ATMEL_SSC_DMABUF_SIZE; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", + (void *)buf->area, (void *)(long)buf->addr, size); + + if (!buf->area) + return -ENOMEM; + + buf->bytes = size; + return 0; +} + +static int atmel_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return remap_pfn_range(vma, vma->vm_start, + substream->dma_buffer.addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret; + + ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); + ret = atmel_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); + ret = atmel_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + out: + return ret; +} + +static void atmel_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + dma_free_coherent(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} + /*--------------------------------------------------------------------------*\ * Hardware definition \*--------------------------------------------------------------------------*/ diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c deleted file mode 100644 index 8ae3fa5ac60a..000000000000 --- a/sound/soc/atmel/atmel-pcm.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC. - * - * Copyright (C) 2005 SAN People - * Copyright (C) 2008 Atmel - * - * Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com> - * - * Based on at91-pcm. by: - * Frank Mandarino <fmandarino@endrelia.com> - * Copyright 2006 Endrelia Technologies Inc. - * - * Based on pxa2xx-pcm.c by: - * - * Author: Nicolas Pitre - * Created: Nov 30, 2004 - * Copyright: (C) 2004 MontaVista Software, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/module.h> -#include <linux/dma-mapping.h> -#include <sound/pcm.h> -#include <sound/soc.h> -#include "atmel-pcm.h" - -static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, - int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = ATMEL_SSC_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_coherent(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", - (void *)buf->area, (void *)(long)buf->addr, size); - - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -int atmel_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - return remap_pfn_range(vma, vma->vm_start, - substream->dma_buffer.addr >> PAGE_SHIFT, - vma->vm_end - vma->vm_start, vma->vm_page_prot); -} -EXPORT_SYMBOL_GPL(atmel_pcm_mmap); - -int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n"); - ret = atmel_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n"); - ret = atmel_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - out: - return ret; -} -EXPORT_SYMBOL_GPL(atmel_pcm_new); - -void atmel_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - dma_free_coherent(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} -EXPORT_SYMBOL_GPL(atmel_pcm_free); - diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index 12ae814eff21..6eaf081cad50 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -83,11 +83,6 @@ struct atmel_pcm_dma_params { #define ssc_readx(base, reg) (__raw_readl((base) + (reg))) #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) -int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd); -void atmel_pcm_free(struct snd_pcm *pcm); -int atmel_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma); - #if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) int atmel_pcm_pdc_platform_register(struct device *dev); diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index fb0b7e8b08ff..841d05946b88 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * When the bit clock is input, limit the maximum rate according to the + * Serial Clock Ratio Considerations section from the SSC documentation: + * + * The Transmitter and the Receiver can be programmed to operate + * with the clock signals provided on either the TK or RK pins. + * This allows the SSC to support many slave-mode data transfers. + * In this case, the maximum clock speed allowed on the RK pin is: + * - Peripheral clock divided by 2 if Receiver Frame Synchro is input + * - Peripheral clock divided by 3 if Receiver Frame Synchro is output + * In addition, the maximum clock speed allowed on the TK pin is: + * - Peripheral clock divided by 6 if Transmit Frame Synchro is input + * - Peripheral clock divided by 2 if Transmit Frame Synchro is output + * + * When the bit clock is output, limit the rate according to the + * SSC divider restrictions. + */ +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule->private; + struct ssc_device *ssc = ssc_p->ssc; + struct snd_interval *i = hw_param_interval(params, rule->var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size < 0) + return frame_size; + + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE) + && ssc->clk_from_rk_pin) + /* Receiver Frame Synchro (i.e. capture) + * is output (format is _CFS) and the RK pin + * is used for input (format is _CBM_). + */ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK) + && !ssc->clk_from_rk_pin) + /* Transmit Frame Synchro (i.e. playback) + * is input (format is _CFM) and the TK pin + * is used for input (format _CBM_ but not + * using the RK pin). + */ + mck_div = 6; + break; + } + + switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p->mck_rate / mck_div / frame_size; + + ret = snd_interval_ratnum(i, 1, &r, &num, &den); + if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) { + params->rate_num = num; + params->rate_den = den; + } + break; + + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p->mck_rate / mck_div / frame_size; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, &t); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} /*-------------------------------------------------------------------------*\ * DAI functions @@ -200,6 +288,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; + int ret; pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n", ssc_readl(ssc_p->ssc->regs, SR)); @@ -207,6 +296,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug("atmel_ssc_dai: Starting clock\n"); clk_enable(ssc_p->ssc->clk); + ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk); /* Reset the SSC to keep it at a clean status */ ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST)); @@ -219,6 +309,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, dir_mask = SSC_DIR_MASK_CAPTURE; } + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + atmel_ssc_hw_rule_rate, + ssc_p, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret < 0) { + dev_err(dai->dev, "Failed to specify rate rule: %d\n", ret); + return ret; + } + dma_params = &ssc_dma_params[dai->id][dir]; dma_params->ssc = ssc_p->ssc; dma_params->substream = substream; @@ -783,8 +884,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ -#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) - #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) @@ -804,12 +903,16 @@ static struct snd_soc_dai_driver atmel_ssc_dai = { .playback = { .channels_min = 1, .channels_max = 2, - .rates = ATMEL_SSC_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .capture = { .channels_min = 1, .channels_max = 2, - .rates = ATMEL_SSC_RATES, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 384000, .formats = ATMEL_SSC_FORMATS,}, .ops = &atmel_ssc_dai_ops, }; diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index b1f08d511495..80b153857a88 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h @@ -115,6 +115,7 @@ struct atmel_ssc_info { unsigned short rcmr_period; struct atmel_pcm_dma_params *dma_params[2]; struct atmel_ssc_state ssc_state; + unsigned long mck_rate; }; int atmel_ssc_set_audio(int ssc_id); diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index ea9f0e31f9d4..061c46587628 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -70,6 +70,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MAX98090 if I2C select SND_SOC_MAX98095 if I2C select SND_SOC_MAX98357A if GPIOLIB + select SND_SOC_MAX98925 if I2C select SND_SOC_MAX9850 if I2C select SND_SOC_MAX9768 if I2C select SND_SOC_MAX9877 if I2C @@ -141,7 +142,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_WM8770 if SPI_MASTER select SND_SOC_WM8776 if SND_SOC_I2C_AND_SPI select SND_SOC_WM8782 - select SND_SOC_WM8804 if SND_SOC_I2C_AND_SPI + select SND_SOC_WM8804_I2C if I2C + select SND_SOC_WM8804_SPI if SPI_MASTER select SND_SOC_WM8900 if I2C select SND_SOC_WM8903 if I2C select SND_SOC_WM8904 if I2C @@ -460,6 +462,9 @@ config SND_SOC_MAX98095 config SND_SOC_MAX98357A tristate +config SND_SOC_MAX98925 + tristate + config SND_SOC_MAX9850 tristate @@ -744,8 +749,19 @@ config SND_SOC_WM8782 tristate config SND_SOC_WM8804 - tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver" - depends on SND_SOC_I2C_AND_SPI + tristate + +config SND_SOC_WM8804_I2C + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver I2C" + depends on I2C + select SND_SOC_WM8804 + select REGMAP_I2C + +config SND_SOC_WM8804_SPI + tristate "Wolfson Microelectronics WM8804 S/PDIF transceiver SPI" + depends on SPI_MASTER + select SND_SOC_WM8804 + select REGMAP_SPI config SND_SOC_WM8900 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 69b8666d187a..abe2d7edf65c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -65,6 +65,7 @@ snd-soc-max98088-objs := max98088.o snd-soc-max98090-objs := max98090.o snd-soc-max98095-objs := max98095.o snd-soc-max98357a-objs := max98357a.o +snd-soc-max98925-objs := max98925.o snd-soc-max9850-objs := max9850.o snd-soc-mc13783-objs := mc13783.o snd-soc-ml26124-objs := ml26124.o @@ -145,6 +146,8 @@ snd-soc-wm8770-objs := wm8770.o snd-soc-wm8776-objs := wm8776.o snd-soc-wm8782-objs := wm8782.o snd-soc-wm8804-objs := wm8804.o +snd-soc-wm8804-i2c-objs := wm8804-i2c.o +snd-soc-wm8804-spi-objs := wm8804-spi.o snd-soc-wm8900-objs := wm8900.o snd-soc-wm8903-objs := wm8903.o snd-soc-wm8904-objs := wm8904.o @@ -247,6 +250,7 @@ obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o +obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o @@ -323,6 +327,8 @@ obj-$(CONFIG_SND_SOC_WM8770) += snd-soc-wm8770.o obj-$(CONFIG_SND_SOC_WM8776) += snd-soc-wm8776.o obj-$(CONFIG_SND_SOC_WM8782) += snd-soc-wm8782.o obj-$(CONFIG_SND_SOC_WM8804) += snd-soc-wm8804.o +obj-$(CONFIG_SND_SOC_WM8804_I2C) += snd-soc-wm8804-i2c.o +obj-$(CONFIG_SND_SOC_WM8804_SPI) += snd-soc-wm8804-spi.o obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o obj-$(CONFIG_SND_SOC_WM8904) += snd-soc-wm8904.o diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c index 7895689588da..88ca9cb0ce79 100644 --- a/sound/soc/codecs/ab8500-codec.c +++ b/sound/soc/codecs/ab8500-codec.c @@ -2003,7 +2003,6 @@ static int ab8500_audio_setup_mics(struct snd_soc_codec *codec, return 0; } -EXPORT_SYMBOL_GPL(ab8500_audio_setup_mics); static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec, enum ear_cm_voltage ear_cmv) @@ -2036,7 +2035,6 @@ static int ab8500_audio_set_ear_cmv(struct snd_soc_codec *codec, return 0; } -EXPORT_SYMBOL_GPL(ab8500_audio_set_ear_cmv); static int ab8500_audio_set_bit_delay(struct snd_soc_dai *dai, unsigned int delay) diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c index 70ab35744aba..7ad8e156e2df 100644 --- a/sound/soc/codecs/adau1977.c +++ b/sound/soc/codecs/adau1977.c @@ -938,22 +938,15 @@ int adau1977_probe(struct device *dev, struct regmap *regmap, adau1977->dvdd_reg = NULL; } - adau1977->reset_gpio = devm_gpiod_get(dev, "reset"); - if (IS_ERR(adau1977->reset_gpio)) { - ret = PTR_ERR(adau1977->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return PTR_ERR(adau1977->reset_gpio); - adau1977->reset_gpio = NULL; - } + adau1977->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(adau1977->reset_gpio)) + return PTR_ERR(adau1977->reset_gpio); dev_set_drvdata(dev, adau1977); - if (adau1977->reset_gpio) { - ret = gpiod_direction_output(adau1977->reset_gpio, 0); - if (ret) - return ret; + if (adau1977->reset_gpio) ndelay(100); - } ret = adau1977_power_enable(adau1977); if (ret) diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c index 16ce9f9fefa1..298dedc05140 100644 --- a/sound/soc/codecs/ak4554.c +++ b/sound/soc/codecs/ak4554.c @@ -84,7 +84,7 @@ static int ak4554_soc_remove(struct platform_device *pdev) return 0; } -static struct of_device_id ak4554_of_match[] = { +static const struct of_device_id ak4554_of_match[] = { { .compatible = "asahi-kasei,ak4554" }, {}, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index dde8b49c19ad..13585e88f597 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -97,6 +97,9 @@ #define PMMP (1 << 2) /* MPWR pin Power Management */ #define MGAIN0 (1 << 0) /* MIC amp gain*/ +/* SG_SL2 */ +#define LOPS (1 << 6) /* Stero Line-out Power Save Mode */ + /* TIMER */ #define ZTM(param) ((param & 0x3) << 4) /* ALC Zero Crossing TimeOut */ #define WTM(param) (((param & 0x4) << 4) | ((param & 0x3) << 2)) @@ -168,6 +171,29 @@ static const struct snd_kcontrol_new ak4642_lout_mixer_controls[] = { SOC_DAPM_SINGLE("DACL", SG_SL1, 4, 1, 0), }; +/* event handlers */ +static int ak4642_lout_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMD: + case SND_SOC_DAPM_PRE_PMU: + /* Power save mode ON */ + snd_soc_update_bits(codec, SG_SL2, LOPS, LOPS); + break; + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_POST_PMD: + /* Power save mode OFF */ + mdelay(300); + snd_soc_update_bits(codec, SG_SL2, LOPS, 0); + break; + } + + return 0; +} + static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { /* Outputs */ @@ -182,12 +208,15 @@ static const struct snd_soc_dapm_widget ak4642_dapm_widgets[] = { SND_SOC_DAPM_PGA("DACH", MD_CTL4, 0, 0, NULL, 0), - SND_SOC_DAPM_MIXER("LINEOUT Mixer", PW_MGMT1, 3, 0, + SND_SOC_DAPM_MIXER_E("LINEOUT Mixer", PW_MGMT1, 3, 0, &ak4642_lout_mixer_controls[0], - ARRAY_SIZE(ak4642_lout_mixer_controls)), + ARRAY_SIZE(ak4642_lout_mixer_controls), + ak4642_lout_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), /* DAC */ - SND_SOC_DAPM_DAC("DAC", "HiFi Playback", PW_MGMT1, 2, 0), + SND_SOC_DAPM_DAC("DAC", NULL, PW_MGMT1, 2, 0), }; static const struct snd_soc_dapm_route ak4642_intercon[] = { @@ -205,6 +234,8 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = { {"DACH", NULL, "DAC"}, {"LINEOUT Mixer", "DACL", "DAC"}, + + { "DAC", NULL, "Playback" }, }; /* @@ -468,13 +499,13 @@ static struct snd_soc_dai_driver ak4642_dai = { .name = "ak4642-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE }, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = SNDRV_PCM_RATE_8000_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE }, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index 95d31d6291ac..57da0ceda03f 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -1902,7 +1902,7 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll) static int arizona_enable_fll(struct arizona_fll *fll) { struct arizona *arizona = fll->arizona; - int ret; + unsigned long time_left; bool use_sync = false; int already_enabled = arizona_is_enabled_fll(fll); struct arizona_fll_cfg cfg; @@ -1978,9 +1978,9 @@ static int arizona_enable_fll(struct arizona_fll *fll) regmap_update_bits_async(arizona->regmap, fll->base + 1, ARIZONA_FLL1_FREERUN, 0); - ret = wait_for_completion_timeout(&fll->ok, + time_left = wait_for_completion_timeout(&fll->ok, msecs_to_jiffies(250)); - if (ret == 0) + if (time_left == 0) arizona_fll_warn(fll, "Timed out waiting for lock\n"); return 0; diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index f2b8aad21274..60598b230341 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -437,20 +437,13 @@ static int cs35l32_i2c_probe(struct i2c_client *i2c_client, } /* Reset the Device */ - cs35l32->reset_gpio = devm_gpiod_get(&i2c_client->dev, - "reset-gpios"); - if (IS_ERR(cs35l32->reset_gpio)) { - ret = PTR_ERR(cs35l32->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - cs35l32->reset_gpio = NULL; - } else { - ret = gpiod_direction_output(cs35l32->reset_gpio, 0); - if (ret) - return ret; + cs35l32->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs35l32->reset_gpio)) + return PTR_ERR(cs35l32->reset_gpio); + + if (cs35l32->reset_gpio) gpiod_set_value_cansleep(cs35l32->reset_gpio, 1); - } /* initialize codec */ ret = regmap_read(cs35l32->regmap, CS35L32_DEVID_AB, ®); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index ce6086835ebd..cac48ddf3ba6 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -605,21 +605,14 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client, return ret; } - cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev, - "reset-gpios"); - if (IS_ERR(cs4265->reset_gpio)) { - ret = PTR_ERR(cs4265->reset_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - cs4265->reset_gpio = NULL; - } else { - ret = gpiod_direction_output(cs4265->reset_gpio, 0); - if (ret) - return ret; + cs4265->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4265->reset_gpio)) + return PTR_ERR(cs4265->reset_gpio); + + if (cs4265->reset_gpio) { mdelay(1); gpiod_set_value_cansleep(cs4265->reset_gpio, 1); - } i2c_set_clientdata(i2c_client, cs4265); diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index 7d3a6accaf9a..e770ee6f36da 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -561,10 +561,10 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec) if (gpio_is_valid(cs4271->gpio_nreset)) { /* Reset codec */ gpio_direction_output(cs4271->gpio_nreset, 0); - udelay(1); + mdelay(1); gpio_set_value(cs4271->gpio_nreset, 1); /* Give the codec time to wake up */ - udelay(1); + mdelay(1); } ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index 0b10979513c4..0f334bc1b63c 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -420,7 +420,7 @@ static int cx20442_platform_probe(struct platform_device *pdev) &cx20442_codec_dev, &cx20442_dai, 1); } -static int __exit cx20442_platform_remove(struct platform_device *pdev) +static int cx20442_platform_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; @@ -431,7 +431,7 @@ static struct platform_driver cx20442_platform_driver = { .name = "cx20442-codec", }, .probe = cx20442_platform_probe, - .remove = __exit_p(cx20442_platform_remove), + .remove = cx20442_platform_remove, }; module_platform_driver(cx20442_platform_driver); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index b112b1c2c394..3e33ef2acf3c 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2605,8 +2605,24 @@ err_enable: return ret; } +static void max98090_i2c_shutdown(struct i2c_client *i2c) +{ + struct max98090_priv *max98090 = dev_get_drvdata(&i2c->dev); + + /* + * Enable volume smoothing, disable zero cross. This will cause + * a quick 40ms ramp to mute on shutdown. + */ + regmap_write(max98090->regmap, + M98090_REG_LEVEL_CONTROL, M98090_VSENN_MASK); + regmap_write(max98090->regmap, + M98090_REG_DEVICE_SHUTDOWN, 0x00); + msleep(40); +} + static int max98090_i2c_remove(struct i2c_client *client) { + max98090_i2c_shutdown(client); snd_soc_unregister_codec(&client->dev); return 0; } @@ -2696,6 +2712,7 @@ static struct i2c_driver max98090_i2c_driver = { .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, .probe = max98090_i2c_probe, + .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, }; diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index e9e6efbc21dd..bf3e933ee895 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -26,8 +26,6 @@ #include <sound/soc-dai.h> #include <sound/soc-dapm.h> -#define DRV_NAME "max98357a" - static int max98357a_daiops_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { @@ -87,9 +85,9 @@ static struct snd_soc_dai_ops max98357a_dai_ops = { }; static struct snd_soc_dai_driver max98357a_dai_driver = { - .name = DRV_NAME, + .name = "HiFi", .playback = { - .stream_name = DRV_NAME "-playback", + .stream_name = "HiFi Playback", .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 | SNDRV_PCM_FMTBIT_S32, @@ -127,7 +125,7 @@ static int max98357a_platform_remove(struct platform_device *pdev) #ifdef CONFIG_OF static const struct of_device_id max98357a_device_id[] = { - { .compatible = "maxim," DRV_NAME, }, + { .compatible = "maxim,max98357a" }, {} }; MODULE_DEVICE_TABLE(of, max98357a_device_id); @@ -135,7 +133,7 @@ MODULE_DEVICE_TABLE(of, max98357a_device_id); static struct platform_driver max98357a_platform_driver = { .driver = { - .name = DRV_NAME, + .name = "max98357a", .of_match_table = of_match_ptr(max98357a_device_id), }, .probe = max98357a_platform_probe, @@ -145,4 +143,3 @@ module_platform_driver(max98357a_platform_driver); MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c new file mode 100644 index 000000000000..9b5a17de4690 --- /dev/null +++ b/sound/soc/codecs/max98925.c @@ -0,0 +1,655 @@ +/* + * max98925.c -- ALSA SoC Stereo MAX98925 driver + * Copyright 2013-15 Maxim Integrated Products + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98925.h" + +static const char *const dai_text[] = { + "Left", "Right", "LeftRight", "LeftRightDiv2", +}; + +static const char * const max98925_boost_voltage_text[] = { + "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V", + "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V" +}; + +static SOC_ENUM_SINGLE_DECL(max98925_boost_voltage, + MAX98925_CONFIGURATION, M98925_BST_VOUT_SHIFT, + max98925_boost_voltage_text); + +static const char *const hpf_text[] = { + "Disable", "DC Block", "100Hz", "200Hz", "400Hz", "800Hz", +}; + +static const struct reg_default max98925_reg[] = { + { 0x0B, 0x00 }, /* IRQ Enable0 */ + { 0x0C, 0x00 }, /* IRQ Enable1 */ + { 0x0D, 0x00 }, /* IRQ Enable2 */ + { 0x0E, 0x00 }, /* IRQ Clear0 */ + { 0x0F, 0x00 }, /* IRQ Clear1 */ + { 0x10, 0x00 }, /* IRQ Clear2 */ + { 0x11, 0xC0 }, /* Map0 */ + { 0x12, 0x00 }, /* Map1 */ + { 0x13, 0x00 }, /* Map2 */ + { 0x14, 0xF0 }, /* Map3 */ + { 0x15, 0x00 }, /* Map4 */ + { 0x16, 0xAB }, /* Map5 */ + { 0x17, 0x89 }, /* Map6 */ + { 0x18, 0x00 }, /* Map7 */ + { 0x19, 0x00 }, /* Map8 */ + { 0x1A, 0x06 }, /* DAI Clock Mode 1 */ + { 0x1B, 0xC0 }, /* DAI Clock Mode 2 */ + { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */ + { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */ + { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */ + { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */ + { 0x20, 0x50 }, /* Format */ + { 0x21, 0x00 }, /* TDM Slot Select */ + { 0x22, 0x00 }, /* DOUT Configuration VMON */ + { 0x23, 0x00 }, /* DOUT Configuration IMON */ + { 0x24, 0x00 }, /* DOUT Configuration VBAT */ + { 0x25, 0x00 }, /* DOUT Configuration VBST */ + { 0x26, 0x00 }, /* DOUT Configuration FLAG */ + { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */ + { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */ + { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */ + { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */ + { 0x2B, 0x02 }, /* DOUT Drive Strength */ + { 0x2C, 0x90 }, /* Filters */ + { 0x2D, 0x00 }, /* Gain */ + { 0x2E, 0x02 }, /* Gain Ramping */ + { 0x2F, 0x00 }, /* Speaker Amplifier */ + { 0x30, 0x0A }, /* Threshold */ + { 0x31, 0x00 }, /* ALC Attack */ + { 0x32, 0x80 }, /* ALC Atten and Release */ + { 0x33, 0x00 }, /* ALC Infinite Hold Release */ + { 0x34, 0x92 }, /* ALC Configuration */ + { 0x35, 0x01 }, /* Boost Converter */ + { 0x36, 0x00 }, /* Block Enable */ + { 0x37, 0x00 }, /* Configuration */ + { 0x38, 0x00 }, /* Global Enable */ + { 0x3A, 0x00 }, /* Boost Limiter */ +}; + +static const struct soc_enum max98925_dai_enum = + SOC_ENUM_SINGLE(MAX98925_GAIN, 5, ARRAY_SIZE(dai_text), dai_text); + +static const struct soc_enum max98925_hpf_enum = + SOC_ENUM_SINGLE(MAX98925_FILTERS, 0, ARRAY_SIZE(hpf_text), hpf_text); + +static const struct snd_kcontrol_new max98925_hpf_sel_mux = + SOC_DAPM_ENUM("Rc Filter MUX Mux", max98925_hpf_enum); + +static const struct snd_kcontrol_new max98925_dai_sel_mux = + SOC_DAPM_ENUM("DAI IN MUX Mux", max98925_dai_enum); + +static int max98925_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, + M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(max98925->regmap, + MAX98925_BLOCK_ENABLE, M98925_BST_EN_MASK | + M98925_ADC_IMON_EN_MASK | M98925_ADC_VMON_EN_MASK, 0); + break; + default: + return 0; + } + return 0; +} + +static const struct snd_soc_dapm_widget max98925_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("DAI IN MUX", SND_SOC_NOPM, 0, 0, + &max98925_dai_sel_mux), + SND_SOC_DAPM_MUX("Rc Filter MUX", SND_SOC_NOPM, 0, 0, + &max98925_hpf_sel_mux), + SND_SOC_DAPM_DAC_E("Amp Enable", NULL, MAX98925_BLOCK_ENABLE, + M98925_SPK_EN_SHIFT, 0, max98925_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("Global Enable", MAX98925_GLOBAL_ENABLE, + M98925_EN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("BE_OUT"), +}; + +static const struct snd_soc_dapm_route max98925_audio_map[] = { + {"DAI IN MUX", "Left", "DAI_OUT"}, + {"DAI IN MUX", "Right", "DAI_OUT"}, + {"DAI IN MUX", "LeftRight", "DAI_OUT"}, + {"DAI IN MUX", "LeftRightDiv2", "DAI_OUT"}, + {"Rc Filter MUX", "Disable", "DAI IN MUX"}, + {"Rc Filter MUX", "DC Block", "DAI IN MUX"}, + {"Rc Filter MUX", "100Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "200Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "400Hz", "DAI IN MUX"}, + {"Rc Filter MUX", "800Hz", "DAI IN MUX"}, + {"Amp Enable", NULL, "Rc Filter MUX"}, + {"BE_OUT", NULL, "Amp Enable"}, + {"BE_OUT", NULL, "Global Enable"}, +}; + +static bool max98925_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_VBAT_DATA: + case MAX98925_VBST_DATA: + case MAX98925_LIVE_STATUS0: + case MAX98925_LIVE_STATUS1: + case MAX98925_LIVE_STATUS2: + case MAX98925_STATE0: + case MAX98925_STATE1: + case MAX98925_STATE2: + case MAX98925_FLAG0: + case MAX98925_FLAG1: + case MAX98925_FLAG2: + case MAX98925_REV_VERSION: + return true; + default: + return false; + } +} + +static bool max98925_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98925_IRQ_CLEAR0: + case MAX98925_IRQ_CLEAR1: + case MAX98925_IRQ_CLEAR2: + case MAX98925_ALC_HOLD_RLS: + return false; + default: + return true; + } +} + +static DECLARE_TLV_DB_SCALE(max98925_spk_tlv, -600, 100, 0); + +static const struct snd_kcontrol_new max98925_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", MAX98925_GAIN, + M98925_SPK_GAIN_SHIFT, (1<<M98925_SPK_GAIN_WIDTH)-1, 0, + max98925_spk_tlv), + SOC_SINGLE("Ramp Switch", MAX98925_GAIN_RAMPING, + M98925_SPK_RMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ZCD Switch", MAX98925_GAIN_RAMPING, + M98925_SPK_ZCD_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Switch", MAX98925_THRESHOLD, + M98925_ALC_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Threshold", MAX98925_THRESHOLD, M98925_ALC_TH_SHIFT, + (1<<M98925_ALC_TH_WIDTH)-1, 0), + SOC_ENUM("Boost Output Voltage", max98925_boost_voltage), +}; + +/* codec sample rate and n/m dividers parameter table */ +static const struct { + int rate; + int sr; + int divisors[3][2]; +} rate_table[] = { + { + .rate = 8000, + .sr = 0, + .divisors = { {1, 375}, {5, 1764}, {1, 384} } + }, + { + .rate = 11025, + .sr = 1, + .divisors = { {147, 40000}, {1, 256}, {147, 40960} } + }, + { + .rate = 12000, + .sr = 2, + .divisors = { {1, 250}, {5, 1176}, {1, 256} } + }, + { + .rate = 16000, + .sr = 3, + .divisors = { {2, 375}, {5, 882}, {1, 192} } + }, + { + .rate = 22050, + .sr = 4, + .divisors = { {147, 20000}, {1, 128}, {147, 20480} } + }, + { + .rate = 24000, + .sr = 5, + .divisors = { {1, 125}, {5, 588}, {1, 128} } + }, + { + .rate = 32000, + .sr = 6, + .divisors = { {4, 375}, {5, 441}, {1, 96} } + }, + { + .rate = 44100, + .sr = 7, + .divisors = { {147, 10000}, {1, 64}, {147, 10240} } + }, + { + .rate = 48000, + .sr = 8, + .divisors = { {2, 125}, {5, 294}, {1, 64} } + }, +}; + +static inline int max98925_rate_value(struct snd_soc_codec *codec, + int rate, int clock, int *value, int *n, int *m) +{ + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(rate_table); i++) { + if (rate_table[i].rate >= rate) { + *value = rate_table[i].sr; + *n = rate_table[i].divisors[clock][0]; + *m = rate_table[i].divisors[clock][1]; + ret = 0; + break; + } + } + dev_dbg(codec->dev, "%s: sample rate is %d, returning %d\n", + __func__, rate_table[i].rate, *value); + return ret; +} + +static void max98925_set_sense_data(struct max98925_priv *max98925) +{ + /* set VMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_EN_MASK, M98925_DAI_VMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_VMON, + M98925_DAI_VMON_SLOT_MASK, + max98925->v_slot << M98925_DAI_VMON_SLOT_SHIFT); + /* set IMON slots */ + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_EN_MASK, M98925_DAI_IMON_EN_MASK); + regmap_update_bits(max98925->regmap, + MAX98925_DOUT_CFG_IMON, + M98925_DAI_IMON_SLOT_MASK, + max98925->i_slot << M98925_DAI_IMON_SLOT_SHIFT); +} + +static int max98925_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + unsigned int invert = 0; + + dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt); + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* set DAI to slave mode */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, 0); + max98925_set_sense_data(max98925); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* + * set left channel DAI to master mode, + * right channel always slave + */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_MAS_MASK, M98925_DAI_MAS_MASK); + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + invert = M98925_DAI_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + invert = M98925_DAI_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + invert = M98925_DAI_BCI_MASK | M98925_DAI_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + regmap_update_bits(max98925->regmap, MAX98925_FORMAT, + M98925_DAI_BCI_MASK, invert); + return 0; +} + +static int max98925_set_clock(struct max98925_priv *max98925, + struct snd_pcm_hw_params *params) +{ + unsigned int dai_sr = 0, clock, mdll, n, m; + struct snd_soc_codec *codec = max98925->codec; + int rate = params_rate(params); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98925->ch_size; + + switch (blr_clk_ratio) { + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_32); + break; + case 48: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_48); + break; + case 64: + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_BSEL_MASK, M98925_DAI_BSEL_64); + break; + default: + return -EINVAL; + } + + switch (max98925->sysclk) { + case 6000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx16; + break; + case 11289600: + clock = 1; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12000000: + clock = 0; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + case 12288000: + clock = 2; + mdll = M98925_MDLL_MULT_MCLKx8; + break; + default: + dev_info(max98925->codec->dev, "unsupported sysclk %d\n", + max98925->sysclk); + return -EINVAL; + } + + if (max98925_rate_value(codec, rate, clock, &dai_sr, &n, &m)) + return -EINVAL; + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE2, + M98925_DAI_SR_MASK, dai_sr << M98925_DAI_SR_SHIFT); + /* set DAI m divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_MSBS, m >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_M_LSBS, m & 0xFF); + /* set DAI n divider */ + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_MSBS, n >> 8); + regmap_write(max98925->regmap, + MAX98925_DAI_CLK_DIV_N_LSBS, n & 0xFF); + /* set MDLL */ + regmap_update_bits(max98925->regmap, MAX98925_DAI_CLK_MODE1, + M98925_MDLL_MULT_MASK, mdll << M98925_MDLL_MULT_SHIFT); + return 0; +} + +static int max98925_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (snd_pcm_format_width(params_format(params))) { + case 16: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_16); + max98925->ch_size = 16; + break; + case 24: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_24); + max98925->ch_size = 24; + break; + case 32: + regmap_update_bits(max98925->regmap, + MAX98925_FORMAT, + M98925_DAI_CHANSZ_MASK, M98925_DAI_CHANSZ_32); + max98925->ch_size = 32; + break; + default: + pr_err("%s: format unsupported %d", + __func__, params_format(params)); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: format supported %d", + __func__, params_format(params)); + return max98925_set_clock(max98925, params); +} + +static int max98925_dai_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + switch (clk_id) { + case 0: + /* use MCLK for Left channel, right channel always BCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, 0); + break; + case 1: + /* configure dai clock source to BCLK instead of MCLK */ + regmap_update_bits(max98925->regmap, + MAX98925_DAI_CLK_MODE1, + M98925_DAI_CLK_SOURCE_MASK, + M98925_DAI_CLK_SOURCE_MASK); + break; + default: + return -EINVAL; + } + max98925->sysclk = freq; + return 0; +} + +#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98925_dai_ops = { + .set_sysclk = max98925_dai_set_sysclk, + .set_fmt = max98925_dai_set_fmt, + .hw_params = max98925_dai_hw_params, +}; + +static struct snd_soc_dai_driver max98925_dai[] = { + { + .name = "max98925-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = MAX98925_FORMATS, + }, + .ops = &max98925_dai_ops, + } +}; + +static int max98925_probe(struct snd_soc_codec *codec) +{ + struct max98925_priv *max98925 = snd_soc_codec_get_drvdata(codec); + + max98925->codec = codec; + codec->control_data = max98925->regmap; + regmap_write(max98925->regmap, MAX98925_GLOBAL_ENABLE, 0x00); + /* It's not the default but we need to set DAI_DLY */ + regmap_write(max98925->regmap, + MAX98925_FORMAT, M98925_DAI_DLY_MASK); + regmap_write(max98925->regmap, MAX98925_TDM_SLOT_SELECT, 0xC8); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG1, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG2, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG3, 0xFF); + regmap_write(max98925->regmap, MAX98925_DOUT_HIZ_CFG4, 0xF0); + regmap_write(max98925->regmap, MAX98925_FILTERS, 0xD8); + regmap_write(max98925->regmap, MAX98925_ALC_CONFIGURATION, 0xF8); + regmap_write(max98925->regmap, MAX98925_CONFIGURATION, 0xF0); + /* Disable ALC muting */ + regmap_write(max98925->regmap, MAX98925_BOOST_LIMITER, 0xF8); + return 0; +} + +static const struct snd_soc_codec_driver soc_codec_dev_max98925 = { + .probe = max98925_probe, + .controls = max98925_snd_controls, + .num_controls = ARRAY_SIZE(max98925_snd_controls), + .dapm_routes = max98925_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98925_audio_map), + .dapm_widgets = max98925_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets), +}; + +static const struct regmap_config max98925_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX98925_REV_VERSION, + .reg_defaults = max98925_reg, + .num_reg_defaults = ARRAY_SIZE(max98925_reg), + .volatile_reg = max98925_volatile_register, + .readable_reg = max98925_readable_register, + .cache_type = REGCACHE_RBTREE, +}; + +static int max98925_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret, reg; + u32 value; + struct max98925_priv *max98925; + + max98925 = devm_kzalloc(&i2c->dev, + sizeof(*max98925), GFP_KERNEL); + if (!max98925) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98925); + max98925->regmap = devm_regmap_init_i2c(i2c, &max98925_regmap); + if (IS_ERR(max98925->regmap)) { + ret = PTR_ERR(max98925->regmap); + dev_err(&i2c->dev, + "Failed to allocate regmap: %d\n", ret); + goto err_out; + } + + if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) { + if (value > M98925_DAI_VMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "vmon slot number is wrong:\n"); + return -EINVAL; + } + max98925->v_slot = value; + } + if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) { + if (value > M98925_DAI_IMON_SLOT_1E_1F) { + dev_err(&i2c->dev, "imon slot number is wrong:\n"); + return -EINVAL; + } + max98925->i_slot = value; + } + ret = regmap_read(max98925->regmap, + MAX98925_REV_VERSION, ®); + if ((ret < 0) || + ((reg != MAX98925_VERSION) && + (reg != MAX98925_VERSION1))) { + dev_err(&i2c->dev, + "device initialization error (%d 0x%02X)\n", + ret, reg); + goto err_out; + } + dev_info(&i2c->dev, "device version 0x%02X\n", reg); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98925, + max98925_dai, ARRAY_SIZE(max98925_dai)); + if (ret < 0) + dev_err(&i2c->dev, + "Failed to register codec: %d\n", ret); +err_out: + return ret; +} + +static int max98925_i2c_remove(struct i2c_client *client) +{ + snd_soc_unregister_codec(&client->dev); + return 0; +} + +static const struct i2c_device_id max98925_i2c_id[] = { + { "max98925", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max98925_i2c_id); + +static const struct of_device_id max98925_of_match[] = { + { .compatible = "maxim,max98925", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98925_of_match); + +static struct i2c_driver max98925_i2c_driver = { + .driver = { + .name = "max98925", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max98925_of_match), + .pm = NULL, + }, + .probe = max98925_i2c_probe, + .remove = max98925_i2c_remove, + .id_table = max98925_i2c_id, +}; + +module_i2c_driver(max98925_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98925 driver"); +MODULE_AUTHOR("Ralph Birt <rdbirt@gmail.com>, Anish kumar <anish.kumar@maximintegrated.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98925.h b/sound/soc/codecs/max98925.h new file mode 100644 index 000000000000..3783248f2780 --- /dev/null +++ b/sound/soc/codecs/max98925.h @@ -0,0 +1,832 @@ +/* + * max98925.h -- MAX98925 ALSA SoC Audio driver + * + * Copyright 2013-2015 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98925_H +#define _MAX98925_H + +#define MAX98925_VERSION 0x51 +#define MAX98925_VERSION1 0x80 +#define MAX98925_VBAT_DATA 0x00 +#define MAX98925_VBST_DATA 0x01 +#define MAX98925_LIVE_STATUS0 0x02 +#define MAX98925_LIVE_STATUS1 0x03 +#define MAX98925_LIVE_STATUS2 0x04 +#define MAX98925_STATE0 0x05 +#define MAX98925_STATE1 0x06 +#define MAX98925_STATE2 0x07 +#define MAX98925_FLAG0 0x08 +#define MAX98925_FLAG1 0x09 +#define MAX98925_FLAG2 0x0A +#define MAX98925_IRQ_ENABLE0 0x0B +#define MAX98925_IRQ_ENABLE1 0x0C +#define MAX98925_IRQ_ENABLE2 0x0D +#define MAX98925_IRQ_CLEAR0 0x0E +#define MAX98925_IRQ_CLEAR1 0x0F +#define MAX98925_IRQ_CLEAR2 0x10 +#define MAX98925_MAP0 0x11 +#define MAX98925_MAP1 0x12 +#define MAX98925_MAP2 0x13 +#define MAX98925_MAP3 0x14 +#define MAX98925_MAP4 0x15 +#define MAX98925_MAP5 0x16 +#define MAX98925_MAP6 0x17 +#define MAX98925_MAP7 0x18 +#define MAX98925_MAP8 0x19 +#define MAX98925_DAI_CLK_MODE1 0x1A +#define MAX98925_DAI_CLK_MODE2 0x1B +#define MAX98925_DAI_CLK_DIV_M_MSBS 0x1C +#define MAX98925_DAI_CLK_DIV_M_LSBS 0x1D +#define MAX98925_DAI_CLK_DIV_N_MSBS 0x1E +#define MAX98925_DAI_CLK_DIV_N_LSBS 0x1F +#define MAX98925_FORMAT 0x20 +#define MAX98925_TDM_SLOT_SELECT 0x21 +#define MAX98925_DOUT_CFG_VMON 0x22 +#define MAX98925_DOUT_CFG_IMON 0x23 +#define MAX98925_DOUT_CFG_VBAT 0x24 +#define MAX98925_DOUT_CFG_VBST 0x25 +#define MAX98925_DOUT_CFG_FLAG 0x26 +#define MAX98925_DOUT_HIZ_CFG1 0x27 +#define MAX98925_DOUT_HIZ_CFG2 0x28 +#define MAX98925_DOUT_HIZ_CFG3 0x29 +#define MAX98925_DOUT_HIZ_CFG4 0x2A +#define MAX98925_DOUT_DRV_STRENGTH 0x2B +#define MAX98925_FILTERS 0x2C +#define MAX98925_GAIN 0x2D +#define MAX98925_GAIN_RAMPING 0x2E +#define MAX98925_SPK_AMP 0x2F +#define MAX98925_THRESHOLD 0x30 +#define MAX98925_ALC_ATTACK 0x31 +#define MAX98925_ALC_ATTEN_RLS 0x32 +#define MAX98925_ALC_HOLD_RLS 0x33 +#define MAX98925_ALC_CONFIGURATION 0x34 +#define MAX98925_BOOST_CONVERTER 0x35 +#define MAX98925_BLOCK_ENABLE 0x36 +#define MAX98925_CONFIGURATION 0x37 +#define MAX98925_GLOBAL_ENABLE 0x38 +#define MAX98925_BOOST_LIMITER 0x3A +#define MAX98925_REV_VERSION 0xFF + +#define MAX98925_REG_CNT (MAX98925_R03A_BOOST_LIMITER+1) + +/* MAX98925 Register Bit Fields */ + +/* MAX98925_R002_LIVE_STATUS0 */ +#define M98925_THERMWARN_STATUS_MASK (1<<3) +#define M98925_THERMWARN_STATUS_SHIFT 3 +#define M98925_THERMWARN_STATUS_WIDTH 1 +#define M98925_THERMSHDN_STATUS_MASK (1<<1) +#define M98925_THERMSHDN_STATUS_SHIFT 1 +#define M98925_THERMSHDN_STATUS_WIDTH 1 + +/* MAX98925_R003_LIVE_STATUS1 */ +#define M98925_SPKCURNT_STATUS_MASK (1<<5) +#define M98925_SPKCURNT_STATUS_SHIFT 5 +#define M98925_SPKCURNT_STATUS_WIDTH 1 +#define M98925_WATCHFAIL_STATUS_MASK (1<<4) +#define M98925_WATCHFAIL_STATUS_SHIFT 4 +#define M98925_WATCHFAIL_STATUS_WIDTH 1 +#define M98925_ALCINFH_STATUS_MASK (1<<3) +#define M98925_ALCINFH_STATUS_SHIFT 3 +#define M98925_ALCINFH_STATUS_WIDTH 1 +#define M98925_ALCACT_STATUS_MASK (1<<2) +#define M98925_ALCACT_STATUS_SHIFT 2 +#define M98925_ALCACT_STATUS_WIDTH 1 +#define M98925_ALCMUT_STATUS_MASK (1<<1) +#define M98925_ALCMUT_STATUS_SHIFT 1 +#define M98925_ALCMUT_STATUS_WIDTH 1 +#define M98925_ACLP_STATUS_MASK (1<<0) +#define M98925_ACLP_STATUS_SHIFT 0 +#define M98925_ACLP_STATUS_WIDTH 1 + +/* MAX98925_R004_LIVE_STATUS2 */ +#define M98925_SLOTOVRN_STATUS_MASK (1<<6) +#define M98925_SLOTOVRN_STATUS_SHIFT 6 +#define M98925_SLOTOVRN_STATUS_WIDTH 1 +#define M98925_INVALSLOT_STATUS_MASK (1<<5) +#define M98925_INVALSLOT_STATUS_SHIFT 5 +#define M98925_INVALSLOT_STATUS_WIDTH 1 +#define M98925_SLOTCNFLT_STATUS_MASK (1<<4) +#define M98925_SLOTCNFLT_STATUS_SHIFT 4 +#define M98925_SLOTCNFLT_STATUS_WIDTH 1 +#define M98925_VBSTOVFL_STATUS_MASK (1<<3) +#define M98925_VBSTOVFL_STATUS_SHIFT 3 +#define M98925_VBSTOVFL_STATUS_WIDTH 1 +#define M98925_VBATOVFL_STATUS_MASK (1<<2) +#define M98925_VBATOVFL_STATUS_SHIFT 2 +#define M98925_VBATOVFL_STATUS_WIDTH 1 +#define M98925_IMONOVFL_STATUS_MASK (1<<1) +#define M98925_IMONOVFL_STATUS_SHIFT 1 +#define M98925_IMONOVFL_STATUS_WIDTH 1 +#define M98925_VMONOVFL_STATUS_MASK (1<<0) +#define M98925_VMONOVFL_STATUS_SHIFT 0 +#define M98925_VMONOVFL_STATUS_WIDTH 1 + +/* MAX98925_R005_STATE0 */ +#define M98925_THERMWARN_END_STATE_MASK (1<<3) +#define M98925_THERMWARN_END_STATE_SHIFT 3 +#define M98925_THERMWARN_END_STATE_WIDTH 1 +#define M98925_THERMWARN_BGN_STATE_MASK (1<<2) +#define M98925_THERMWARN_BGN_STATE_SHIFT 1 +#define M98925_THERMWARN_BGN_STATE_WIDTH 1 +#define M98925_THERMSHDN_END_STATE_MASK (1<<1) +#define M98925_THERMSHDN_END_STATE_SHIFT 1 +#define M98925_THERMSHDN_END_STATE_WIDTH 1 +#define M98925_THERMSHDN_BGN_STATE_MASK (1<<0) +#define M98925_THERMSHDN_BGN_STATE_SHIFT 0 +#define M98925_THERMSHDN_BGN_STATE_WIDTH 1 + +/* MAX98925_R006_STATE1 */ +#define M98925_SPRCURNT_STATE_MASK (1<<5) +#define M98925_SPRCURNT_STATE_SHIFT 5 +#define M98925_SPRCURNT_STATE_WIDTH 1 +#define M98925_WATCHFAIL_STATE_MASK (1<<4) +#define M98925_WATCHFAIL_STATE_SHIFT 4 +#define M98925_WATCHFAIL_STATE_WIDTH 1 +#define M98925_ALCINFH_STATE_MASK (1<<3) +#define M98925_ALCINFH_STATE_SHIFT 3 +#define M98925_ALCINFH_STATE_WIDTH 1 +#define M98925_ALCACT_STATE_MASK (1<<2) +#define M98925_ALCACT_STATE_SHIFT 2 +#define M98925_ALCACT_STATE_WIDTH 1 +#define M98925_ALCMUT_STATE_MASK (1<<1) +#define M98925_ALCMUT_STATE_SHIFT 1 +#define M98925_ALCMUT_STATE_WIDTH 1 +#define M98925_ALCP_STATE_MASK (1<<0) +#define M98925_ALCP_STATE_SHIFT 0 +#define M98925_ALCP_STATE_WIDTH 1 + +/* MAX98925_R007_STATE2 */ +#define M98925_SLOTOVRN_STATE_MASK (1<<6) +#define M98925_SLOTOVRN_STATE_SHIFT 6 +#define M98925_SLOTOVRN_STATE_WIDTH 1 +#define M98925_INVALSLOT_STATE_MASK (1<<5) +#define M98925_INVALSLOT_STATE_SHIFT 5 +#define M98925_INVALSLOT_STATE_WIDTH 1 +#define M98925_SLOTCNFLT_STATE_MASK (1<<4) +#define M98925_SLOTCNFLT_STATE_SHIFT 4 +#define M98925_SLOTCNFLT_STATE_WIDTH 1 +#define M98925_VBSTOVFL_STATE_MASK (1<<3) +#define M98925_VBSTOVFL_STATE_SHIFT 3 +#define M98925_VBSTOVFL_STATE_WIDTH 1 +#define M98925_VBATOVFL_STATE_MASK (1<<2) +#define M98925_VBATOVFL_STATE_SHIFT 2 +#define M98925_VBATOVFL_STATE_WIDTH 1 +#define M98925_IMONOVFL_STATE_MASK (1<<1) +#define M98925_IMONOVFL_STATE_SHIFT 1 +#define M98925_IMONOVFL_STATE_WIDTH 1 +#define M98925_VMONOVFL_STATE_MASK (1<<0) +#define M98925_VMONOVFL_STATE_SHIFT 0 +#define M98925_VMONOVFL_STATE_WIDTH 1 + +/* MAX98925_R008_FLAG0 */ +#define M98925_THERMWARN_END_FLAG_MASK (1<<3) +#define M98925_THERMWARN_END_FLAG_SHIFT 3 +#define M98925_THERMWARN_END_FLAG_WIDTH 1 +#define M98925_THERMWARN_BGN_FLAG_MASK (1<<2) +#define M98925_THERMWARN_BGN_FLAG_SHIFT 2 +#define M98925_THERMWARN_BGN_FLAG_WIDTH 1 +#define M98925_THERMSHDN_END_FLAG_MASK (1<<1) +#define M98925_THERMSHDN_END_FLAG_SHIFT 1 +#define M98925_THERMSHDN_END_FLAG_WIDTH 1 +#define M98925_THERMSHDN_BGN_FLAG_MASK (1<<0) +#define M98925_THERMSHDN_BGN_FLAG_SHIFT 0 +#define M98925_THERMSHDN_BGN_FLAG_WIDTH 1 + +/* MAX98925_R009_FLAG1 */ +#define M98925_SPKCURNT_FLAG_MASK (1<<5) +#define M98925_SPKCURNT_FLAG_SHIFT 5 +#define M98925_SPKCURNT_FLAG_WIDTH 1 +#define M98925_WATCHFAIL_FLAG_MASK (1<<4) +#define M98925_WATCHFAIL_FLAG_SHIFT 4 +#define M98925_WATCHFAIL_FLAG_WIDTH 1 +#define M98925_ALCINFH_FLAG_MASK (1<<3) +#define M98925_ALCINFH_FLAG_SHIFT 3 +#define M98925_ALCINFH_FLAG_WIDTH 1 +#define M98925_ALCACT_FLAG_MASK (1<<2) +#define M98925_ALCACT_FLAG_SHIFT 2 +#define M98925_ALCACT_FLAG_WIDTH 1 +#define M98925_ALCMUT_FLAG_MASK (1<<1) +#define M98925_ALCMUT_FLAG_SHIFT 1 +#define M98925_ALCMUT_FLAG_WIDTH 1 +#define M98925_ALCP_FLAG_MASK (1<<0) +#define M98925_ALCP_FLAG_SHIFT 0 +#define M98925_ALCP_FLAG_WIDTH 1 + +/* MAX98925_R00A_FLAG2 */ +#define M98925_SLOTOVRN_FLAG_MASK (1<<6) +#define M98925_SLOTOVRN_FLAG_SHIFT 6 +#define M98925_SLOTOVRN_FLAG_WIDTH 1 +#define M98925_INVALSLOT_FLAG_MASK (1<<5) +#define M98925_INVALSLOT_FLAG_SHIFT 5 +#define M98925_INVALSLOT_FLAG_WIDTH 1 +#define M98925_SLOTCNFLT_FLAG_MASK (1<<4) +#define M98925_SLOTCNFLT_FLAG_SHIFT 4 +#define M98925_SLOTCNFLT_FLAG_WIDTH 1 +#define M98925_VBSTOVFL_FLAG_MASK (1<<3) +#define M98925_VBSTOVFL_FLAG_SHIFT 3 +#define M98925_VBSTOVFL_FLAG_WIDTH 1 +#define M98925_VBATOVFL_FLAG_MASK (1<<2) +#define M98925_VBATOVFL_FLAG_SHIFT 2 +#define M98925_VBATOVFL_FLAG_WIDTH 1 +#define M98925_IMONOVFL_FLAG_MASK (1<<1) +#define M98925_IMONOVFL_FLAG_SHIFT 1 +#define M98925_IMONOVFL_FLAG_WIDTH 1 +#define M98925_VMONOVFL_FLAG_MASK (1<<0) +#define M98925_VMONOVFL_FLAG_SHIFT 0 +#define M98925_VMONOVFL_FLAG_WIDTH 1 + +/* MAX98925_R00B_IRQ_ENABLE0 */ +#define M98925_THERMWARN_END_EN_MASK (1<<3) +#define M98925_THERMWARN_END_EN_SHIFT 3 +#define M98925_THERMWARN_END_EN_WIDTH 1 +#define M98925_THERMWARN_BGN_EN_MASK (1<<2) +#define M98925_THERMWARN_BGN_EN_SHIFT 2 +#define M98925_THERMWARN_BGN_EN_WIDTH 1 +#define M98925_THERMSHDN_END_EN_MASK (1<<1) +#define M98925_THERMSHDN_END_EN_SHIFT 1 +#define M98925_THERMSHDN_END_EN_WIDTH 1 +#define M98925_THERMSHDN_BGN_EN_MASK (1<<0) +#define M98925_THERMSHDN_BGN_EN_SHIFT 0 +#define M98925_THERMSHDN_BGN_EN_WIDTH 1 + +/* MAX98925_R00C_IRQ_ENABLE1 */ +#define M98925_SPKCURNT_EN_MASK (1<<5) +#define M98925_SPKCURNT_EN_SHIFT 5 +#define M98925_SPKCURNT_EN_WIDTH 1 +#define M98925_WATCHFAIL_EN_MASK (1<<4) +#define M98925_WATCHFAIL_EN_SHIFT 4 +#define M98925_WATCHFAIL_EN_WIDTH 1 +#define M98925_ALCINFH_EN_MASK (1<<3) +#define M98925_ALCINFH_EN_SHIFT 3 +#define M98925_ALCINFH_EN_WIDTH 1 +#define M98925_ALCACT_EN_MASK (1<<2) +#define M98925_ALCACT_EN_SHIFT 2 +#define M98925_ALCACT_EN_WIDTH 1 +#define M98925_ALCMUT_EN_MASK (1<<1) +#define M98925_ALCMUT_EN_SHIFT 1 +#define M98925_ALCMUT_EN_WIDTH 1 +#define M98925_ALCP_EN_MASK (1<<0) +#define M98925_ALCP_EN_SHIFT 0 +#define M98925_ALCP_EN_WIDTH 1 + +/* MAX98925_R00D_IRQ_ENABLE2 */ +#define M98925_SLOTOVRN_EN_MASK (1<<6) +#define M98925_SLOTOVRN_EN_SHIFT 6 +#define M98925_SLOTOVRN_EN_WIDTH 1 +#define M98925_INVALSLOT_EN_MASK (1<<5) +#define M98925_INVALSLOT_EN_SHIFT 5 +#define M98925_INVALSLOT_EN_WIDTH 1 +#define M98925_SLOTCNFLT_EN_MASK (1<<4) +#define M98925_SLOTCNFLT_EN_SHIFT 4 +#define M98925_SLOTCNFLT_EN_WIDTH 1 +#define M98925_VBSTOVFL_EN_MASK (1<<3) +#define M98925_VBSTOVFL_EN_SHIFT 3 +#define M98925_VBSTOVFL_EN_WIDTH 1 +#define M98925_VBATOVFL_EN_MASK (1<<2) +#define M98925_VBATOVFL_EN_SHIFT 2 +#define M98925_VBATOVFL_EN_WIDTH 1 +#define M98925_IMONOVFL_EN_MASK (1<<1) +#define M98925_IMONOVFL_EN_SHIFT 1 +#define M98925_IMONOVFL_EN_WIDTH 1 +#define M98925_VMONOVFL_EN_MASK (1<<0) +#define M98925_VMONOVFL_EN_SHIFT 0 +#define M98925_VMONOVFL_EN_WIDTH 1 + +/* MAX98925_R00E_IRQ_CLEAR0 */ +#define M98925_THERMWARN_END_CLR_MASK (1<<3) +#define M98925_THERMWARN_END_CLR_SHIFT 3 +#define M98925_THERMWARN_END_CLR_WIDTH 1 +#define M98925_THERMWARN_BGN_CLR_MASK (1<<2) +#define M98925_THERMWARN_BGN_CLR_SHIFT 2 +#define M98925_THERMWARN_BGN_CLR_WIDTH 1 +#define M98925_THERMSHDN_END_CLR_MASK (1<<1) +#define M98925_THERMSHDN_END_CLR_SHIFT 1 +#define M98925_THERMSHDN_END_CLR_WIDTH 1 +#define M98925_THERMSHDN_BGN_CLR_MASK (1<<0) +#define M98925_THERMSHDN_BGN_CLR_SHIFT 0 +#define M98925_THERMSHDN_BGN_CLR_WIDTH 1 + +/* MAX98925_R00F_IRQ_CLEAR1 */ +#define M98925_SPKCURNT_CLR_MASK (1<<5) +#define M98925_SPKCURNT_CLR_SHIFT 5 +#define M98925_SPKCURNT_CLR_WIDTH 1 +#define M98925_WATCHFAIL_CLR_MASK (1<<4) +#define M98925_WATCHFAIL_CLR_SHIFT 4 +#define M98925_WATCHFAIL_CLR_WIDTH 1 +#define M98925_ALCINFH_CLR_MASK (1<<3) +#define M98925_ALCINFH_CLR_SHIFT 3 +#define M98925_ALCINFH_CLR_WIDTH 1 +#define M98925_ALCACT_CLR_MASK (1<<2) +#define M98925_ALCACT_CLR_SHIFT 2 +#define M98925_ALCACT_CLR_WIDTH 1 +#define M98925_ALCMUT_CLR_MASK (1<<1) +#define M98925_ALCMUT_CLR_SHIFT 1 +#define M98925_ALCMUT_CLR_WIDTH 1 +#define M98925_ALCP_CLR_MASK (1<<0) +#define M98925_ALCP_CLR_SHIFT 0 +#define M98925_ALCP_CLR_WIDTH 1 + +/* MAX98925_R010_IRQ_CLEAR2 */ +#define M98925_SLOTOVRN_CLR_MASK (1<<6) +#define M98925_SLOTOVRN_CLR_SHIFT 6 +#define M98925_SLOTOVRN_CLR_WIDTH 1 +#define M98925_INVALSLOT_CLR_MASK (1<<5) +#define M98925_INVALSLOT_CLR_SHIFT 5 +#define M98925_INVALSLOT_CLR_WIDTH 1 +#define M98925_SLOTCNFLT_CLR_MASK (1<<4) +#define M98925_SLOTCNFLT_CLR_SHIFT 4 +#define M98925_SLOTCNFLT_CLR_WIDTH 1 +#define M98925_VBSTOVFL_CLR_MASK (1<<3) +#define M98925_VBSTOVFL_CLR_SHIFT 3 +#define M98925_VBSTOVFL_CLR_WIDTH 1 +#define M98925_VBATOVFL_CLR_MASK (1<<2) +#define M98925_VBATOVFL_CLR_SHIFT 2 +#define M98925_VBATOVFL_CLR_WIDTH 1 +#define M98925_IMONOVFL_CLR_MASK (1<<1) +#define M98925_IMONOVFL_CLR_SHIFT 1 +#define M98925_IMONOVFL_CLR_WIDTH 1 +#define M98925_VMONOVFL_CLR_MASK (1<<0) +#define M98925_VMONOVFL_CLR_SHIFT 0 +#define M98925_VMONOVFL_CLR_WIDTH 1 + +/* MAX98925_R011_MAP0 */ +#define M98925_ER_THERMWARN_EN_MASK (1<<7) +#define M98925_ER_THERMWARN_EN_SHIFT 7 +#define M98925_ER_THERMWARN_EN_WIDTH 1 +#define M98925_ER_THERMWARN_MAP_MASK (0x07<<4) +#define M98925_ER_THERMWARN_MAP_SHIFT 4 +#define M98925_ER_THERMWARN_MAP_WIDTH 3 + +/* MAX98925_R012_MAP1 */ +#define M98925_ER_ALCMUT_EN_MASK (1<<7) +#define M98925_ER_ALCMUT_EN_SHIFT 7 +#define M98925_ER_ALCMUT_EN_WIDTH 1 +#define M98925_ER_ALCMUT_MAP_MASK (0x07<<4) +#define M98925_ER_ALCMUT_MAP_SHIFT 4 +#define M98925_ER_ALCMUT_MAP_WIDTH 3 +#define M98925_ER_ALCP_EN_MASK (1<<3) +#define M98925_ER_ALCP_EN_SHIFT 3 +#define M98925_ER_ALCP_EN_WIDTH 1 +#define M98925_ER_ALCP_MAP_MASK (0x07<<0) +#define M98925_ER_ALCP_MAP_SHIFT 0 +#define M98925_ER_ALCP_MAP_WIDTH 3 + +/* MAX98925_R013_MAP2 */ +#define M98925_ER_ALCINFH_EN_MASK (1<<7) +#define M98925_ER_ALCINFH_EN_SHIFT 7 +#define M98925_ER_ALCINFH_EN_WIDTH 1 +#define M98925_ER_ALCINFH_MAP_MASK (0x07<<4) +#define M98925_ER_ALCINFH_MAP_SHIFT 4 +#define M98925_ER_ALCINFH_MAP_WIDTH 3 +#define M98925_ER_ALCACT_EN_MASK (1<<3) +#define M98925_ER_ALCACT_EN_SHIFT 3 +#define M98925_ER_ALCACT_EN_WIDTH 1 +#define M98925_ER_ALCACT_MAP_MASK (0x07<<0) +#define M98925_ER_ALCACT_MAP_SHIFT 0 +#define M98925_ER_ALCACT_MAP_WIDTH 3 + +/* MAX98925_R014_MAP3 */ +#define M98925_ER_SPKCURNT_EN_MASK (1<<7) +#define M98925_ER_SPKCURNT_EN_SHIFT 7 +#define M98925_ER_SPKCURNT_EN_WIDTH 1 +#define M98925_ER_SPKCURNT_MAP_MASK (0x07<<4) +#define M98925_ER_SPKCURNT_MAP_SHIFT 4 +#define M98925_ER_SPKCURNT_MAP_WIDTH 3 + +/* MAX98925_R015_MAP4 */ +/* RESERVED */ + +/* MAX98925_R016_MAP5 */ +#define M98925_ER_IMONOVFL_EN_MASK (1<<7) +#define M98925_ER_IMONOVFL_EN_SHIFT 7 +#define M98925_ER_IMONOVFL_EN_WIDTH 1 +#define M98925_ER_IMONOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_IMONOVFL_MAP_SHIFT 4 +#define M98925_ER_IMONOVFL_MAP_WIDTH 3 +#define M98925_ER_VMONOVFL_EN_MASK (1<<3) +#define M98925_ER_VMONOVFL_EN_SHIFT 3 +#define M98925_ER_VMONOVFL_EN_WIDTH 1 +#define M98925_ER_VMONOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VMONOVFL_MAP_SHIFT 0 +#define M98925_ER_VMONOVFL_MAP_WIDTH 3 + +/* MAX98925_R017_MAP6 */ +#define M98925_ER_VBSTOVFL_EN_MASK (1<<7) +#define M98925_ER_VBSTOVFL_EN_SHIFT 7 +#define M98925_ER_VBSTOVFL_EN_WIDTH 1 +#define M98925_ER_VBSTOVFL_MAP_MASK (0x07<<4) +#define M98925_ER_VBSTOVFL_MAP_SHIFT 4 +#define M98925_ER_VBSTOVFL_MAP_WIDTH 3 +#define M98925_ER_VBATOVFL_EN_MASK (1<<3) +#define M98925_ER_VBATOVFL_EN_SHIFT 3 +#define M98925_ER_VBATOVFL_EN_WIDTH 1 +#define M98925_ER_VBATOVFL_MAP_MASK (0x07<<0) +#define M98925_ER_VBATOVFL_MAP_SHIFT 0 +#define M98925_ER_VBATOVFL_MAP_WIDTH 3 + +/* MAX98925_R018_MAP7 */ +#define M98925_ER_INVALSLOT_EN_MASK (1<<7) +#define M98925_ER_INVALSLOT_EN_SHIFT 7 +#define M98925_ER_INVALSLOT_EN_WIDTH 1 +#define M98925_ER_INVALSLOT_MAP_MASK (0x07<<4) +#define M98925_ER_INVALSLOT_MAP_SHIFT 4 +#define M98925_ER_INVALSLOT_MAP_WIDTH 3 +#define M98925_ER_SLOTCNFLT_EN_MASK (1<<3) +#define M98925_ER_SLOTCNFLT_EN_SHIFT 3 +#define M98925_ER_SLOTCNFLT_EN_WIDTH 1 +#define M98925_ER_SLOTCNFLT_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTCNFLT_MAP_SHIFT 0 +#define M98925_ER_SLOTCNFLT_MAP_WIDTH 3 + +/* MAX98925_R019_MAP8 */ +#define M98925_ER_SLOTOVRN_EN_MASK (1<<3) +#define M98925_ER_SLOTOVRN_EN_SHIFT 3 +#define M98925_ER_SLOTOVRN_EN_WIDTH 1 +#define M98925_ER_SLOTOVRN_MAP_MASK (0x07<<0) +#define M98925_ER_SLOTOVRN_MAP_SHIFT 0 +#define M98925_ER_SLOTOVRN_MAP_WIDTH 3 + +/* MAX98925_R01A_DAI_CLK_MODE1 */ +#define M98925_DAI_CLK_SOURCE_MASK (1<<6) +#define M98925_DAI_CLK_SOURCE_SHIFT 6 +#define M98925_DAI_CLK_SOURCE_WIDTH 1 +#define M98925_MDLL_MULT_MASK (0x0F<<0) +#define M98925_MDLL_MULT_SHIFT 0 +#define M98925_MDLL_MULT_WIDTH 4 + +#define M98925_MDLL_MULT_MCLKx8 6 +#define M98925_MDLL_MULT_MCLKx16 8 + +/* MAX98925_R01B_DAI_CLK_MODE2 */ +#define M98925_DAI_SR_MASK (0x0F<<4) +#define M98925_DAI_SR_SHIFT 4 +#define M98925_DAI_SR_WIDTH 4 +#define M98925_DAI_MAS_MASK (1<<3) +#define M98925_DAI_MAS_SHIFT 3 +#define M98925_DAI_MAS_WIDTH 1 +#define M98925_DAI_BSEL_MASK (0x07<<0) +#define M98925_DAI_BSEL_SHIFT 0 +#define M98925_DAI_BSEL_WIDTH 3 + +#define M98925_DAI_BSEL_32 (0 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_48 (1 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_64 (2 << M98925_DAI_BSEL_SHIFT) +#define M98925_DAI_BSEL_256 (6 << M98925_DAI_BSEL_SHIFT) + +/* MAX98925_R01C_DAI_CLK_DIV_M_MSBS */ +#define M98925_DAI_M_MSBS_MASK (0xFF<<0) +#define M98925_DAI_M_MSBS_SHIFT 0 +#define M98925_DAI_M_MSBS_WIDTH 8 + +/* MAX98925_R01D_DAI_CLK_DIV_M_LSBS */ +#define M98925_DAI_M_LSBS_MASK (0xFF<<0) +#define M98925_DAI_M_LSBS_SHIFT 0 +#define M98925_DAI_M_LSBS_WIDTH 8 + +/* MAX98925_R01E_DAI_CLK_DIV_N_MSBS */ +#define M98925_DAI_N_MSBS_MASK (0x7F<<0) +#define M98925_DAI_N_MSBS_SHIFT 0 +#define M98925_DAI_N_MSBS_WIDTH 7 + +/* MAX98925_R01F_DAI_CLK_DIV_N_LSBS */ +#define M98925_DAI_N_LSBS_MASK (0xFF<<0) +#define M98925_DAI_N_LSBS_SHIFT 0 +#define M98925_DAI_N_LSBS_WIDTH 8 + +/* MAX98925_R020_FORMAT */ +#define M98925_DAI_CHANSZ_MASK (0x03<<6) +#define M98925_DAI_CHANSZ_SHIFT 6 +#define M98925_DAI_CHANSZ_WIDTH 2 +#define M98925_DAI_EXTBCLK_HIZ_MASK (1<<4) +#define M98925_DAI_EXTBCLK_HIZ_SHIFT 4 +#define M98925_DAI_EXTBCLK_HIZ_WIDTH 1 +#define M98925_DAI_WCI_MASK (1<<3) +#define M98925_DAI_WCI_SHIFT 3 +#define M98925_DAI_WCI_WIDTH 1 +#define M98925_DAI_BCI_MASK (1<<2) +#define M98925_DAI_BCI_SHIFT 2 +#define M98925_DAI_BCI_WIDTH 1 +#define M98925_DAI_DLY_MASK (1<<1) +#define M98925_DAI_DLY_SHIFT 1 +#define M98925_DAI_DLY_WIDTH 1 +#define M98925_DAI_TDM_MASK (1<<0) +#define M98925_DAI_TDM_SHIFT 0 +#define M98925_DAI_TDM_WIDTH 1 + +#define M98925_DAI_CHANSZ_16 (1 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_24 (2 << M98925_DAI_CHANSZ_SHIFT) +#define M98925_DAI_CHANSZ_32 (3 << M98925_DAI_CHANSZ_SHIFT) + +/* MAX98925_R021_TDM_SLOT_SELECT */ +#define M98925_DAI_DO_EN_MASK (1<<7) +#define M98925_DAI_DO_EN_SHIFT 7 +#define M98925_DAI_DO_EN_WIDTH 1 +#define M98925_DAI_DIN_EN_MASK (1<<6) +#define M98925_DAI_DIN_EN_SHIFT 6 +#define M98925_DAI_DIN_EN_WIDTH 1 +#define M98925_DAI_INR_SOURCE_MASK (0x07<<3) +#define M98925_DAI_INR_SOURCE_SHIFT 3 +#define M98925_DAI_INR_SOURCE_WIDTH 3 +#define M98925_DAI_INL_SOURCE_MASK (0x07<<0) +#define M98925_DAI_INL_SOURCE_SHIFT 0 +#define M98925_DAI_INL_SOURCE_WIDTH 3 + +/* MAX98925_R022_DOUT_CFG_VMON */ +#define M98925_DAI_VMON_EN_MASK (1<<5) +#define M98925_DAI_VMON_EN_SHIFT 5 +#define M98925_DAI_VMON_EN_WIDTH 1 +#define M98925_DAI_VMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VMON_SLOT_SHIFT 0 +#define M98925_DAI_VMON_SLOT_WIDTH 5 + +#define M98925_DAI_VMON_SLOT_00_01 (0 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_01_02 (1 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_02_03 (2 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_03_04 (3 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_04_05 (4 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_05_06 (5 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_06_07 (6 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_07_08 (7 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_08_09 (8 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_09_0A (9 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0A_0B (10 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0B_0C (11 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0C_0D (12 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0D_0E (13 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0E_0F (14 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_0F_10 (15 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_10_11 (16 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_11_12 (17 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_12_13 (18 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_13_14 (19 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_14_15 (20 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_15_16 (21 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_16_17 (22 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_17_18 (23 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_18_19 (24 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_19_1A (25 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1A_1B (26 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1B_1C (27 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1C_1D (28 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1D_1E (29 << M98925_DAI_VMON_SLOT_SHIFT) +#define M98925_DAI_VMON_SLOT_1E_1F (30 << M98925_DAI_VMON_SLOT_SHIFT) + +/* MAX98925_R023_DOUT_CFG_IMON */ +#define M98925_DAI_IMON_EN_MASK (1<<5) +#define M98925_DAI_IMON_EN_SHIFT 5 +#define M98925_DAI_IMON_EN_WIDTH 1 +#define M98925_DAI_IMON_SLOT_MASK (0x1F<<0) +#define M98925_DAI_IMON_SLOT_SHIFT 0 +#define M98925_DAI_IMON_SLOT_WIDTH 5 + +#define M98925_DAI_IMON_SLOT_00_01 (0 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_01_02 (1 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_02_03 (2 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_03_04 (3 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_04_05 (4 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_05_06 (5 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_06_07 (6 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_07_08 (7 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_08_09 (8 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_09_0A (9 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0A_0B (10 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0B_0C (11 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0C_0D (12 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0D_0E (13 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0E_0F (14 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_0F_10 (15 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_10_11 (16 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_11_12 (17 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_12_13 (18 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_13_14 (19 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_14_15 (20 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_15_16 (21 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_16_17 (22 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_17_18 (23 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_18_19 (24 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_19_1A (25 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1A_1B (26 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1B_1C (27 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1C_1D (28 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1D_1E (29 << M98925_DAI_IMON_SLOT_SHIFT) +#define M98925_DAI_IMON_SLOT_1E_1F (30 << M98925_DAI_IMON_SLOT_SHIFT) + +/* MAX98925_R024_DOUT_CFG_VBAT */ +#define M98925_DAI_VBAT_EN_MASK (1<<5) +#define M98925_DAI_VBAT_EN_SHIFT 5 +#define M98925_DAI_VBAT_EN_WIDTH 1 +#define M98925_DAI_VBAT_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBAT_SLOT_SHIFT 0 +#define M98925_DAI_VBAT_SLOT_WIDTH 5 + +/* MAX98925_R025_DOUT_CFG_VBST */ +#define M98925_DAI_VBST_EN_MASK (1<<5) +#define M98925_DAI_VBST_EN_SHIFT 5 +#define M98925_DAI_VBST_EN_WIDTH 1 +#define M98925_DAI_VBST_SLOT_MASK (0x1F<<0) +#define M98925_DAI_VBST_SLOT_SHIFT 0 +#define M98925_DAI_VBST_SLOT_WIDTH 5 + +/* MAX98925_R026_DOUT_CFG_FLAG */ +#define M98925_DAI_FLAG_EN_MASK (1<<5) +#define M98925_DAI_FLAG_EN_SHIFT 5 +#define M98925_DAI_FLAG_EN_WIDTH 1 +#define M98925_DAI_FLAG_SLOT_MASK (0x1F<<0) +#define M98925_DAI_FLAG_SLOT_SHIFT 0 +#define M98925_DAI_FLAG_SLOT_WIDTH 5 + +/* MAX98925_R027_DOUT_HIZ_CFG1 */ +#define M98925_DAI_SLOT_HIZ_CFG1_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG1_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG1_WIDTH 8 + +/* MAX98925_R028_DOUT_HIZ_CFG2 */ +#define M98925_DAI_SLOT_HIZ_CFG2_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG2_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG2_WIDTH 8 + +/* MAX98925_R029_DOUT_HIZ_CFG3 */ +#define M98925_DAI_SLOT_HIZ_CFG3_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG3_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG3_WIDTH 8 + +/* MAX98925_R02A_DOUT_HIZ_CFG4 */ +#define M98925_DAI_SLOT_HIZ_CFG4_MASK (0xFF<<0) +#define M98925_DAI_SLOT_HIZ_CFG4_SHIFT 0 +#define M98925_DAI_SLOT_HIZ_CFG4_WIDTH 8 + +/* MAX98925_R02B_DOUT_DRV_STRENGTH */ +#define M98925_DAI_OUT_DRIVE_MASK (0x03<<0) +#define M98925_DAI_OUT_DRIVE_SHIFT 0 +#define M98925_DAI_OUT_DRIVE_WIDTH 2 + +/* MAX98925_R02C_FILTERS */ +#define M98925_ADC_DITHER_EN_MASK (1<<7) +#define M98925_ADC_DITHER_EN_SHIFT 7 +#define M98925_ADC_DITHER_EN_WIDTH 1 +#define M98925_IV_DCB_EN_MASK (1<<6) +#define M98925_IV_DCB_EN_SHIFT 6 +#define M98925_IV_DCB_EN_WIDTH 1 +#define M98925_DAC_DITHER_EN_MASK (1<<4) +#define M98925_DAC_DITHER_EN_SHIFT 4 +#define M98925_DAC_DITHER_EN_WIDTH 1 +#define M98925_DAC_FILTER_MODE_MASK (1<<3) +#define M98925_DAC_FILTER_MODE_SHIFT 3 +#define M98925_DAC_FILTER_MODE_WIDTH 1 +#define M98925_DAC_HPF_MASK (0x07<<0) +#define M98925_DAC_HPF_SHIFT 0 +#define M98925_DAC_HPF_WIDTH 3 +#define M98925_DAC_HPF_DISABLE (0 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_DC_BLOCK (1 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_100 (2 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_200 (3 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_400 (4 << M98925_DAC_HPF_SHIFT) +#define M98925_DAC_HPF_EN_800 (5 << M98925_DAC_HPF_SHIFT) + +/* MAX98925_R02D_GAIN */ +#define M98925_DAC_IN_SEL_MASK (0x03<<5) +#define M98925_DAC_IN_SEL_SHIFT 5 +#define M98925_DAC_IN_SEL_WIDTH 2 +#define M98925_SPK_GAIN_MASK (0x1F<<0) +#define M98925_SPK_GAIN_SHIFT 0 +#define M98925_SPK_GAIN_WIDTH 5 + +#define M98925_DAC_IN_SEL_LEFT_DAI (0 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_RIGHT_DAI (1 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_SUMMED_DAI (2 << M98925_DAC_IN_SEL_SHIFT) +#define M98925_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << M98925_DAC_IN_SEL_SHIFT) + +/* MAX98925_R02E_GAIN_RAMPING */ +#define M98925_SPK_RMP_EN_MASK (1<<1) +#define M98925_SPK_RMP_EN_SHIFT 1 +#define M98925_SPK_RMP_EN_WIDTH 1 +#define M98925_SPK_ZCD_EN_MASK (1<<0) +#define M98925_SPK_ZCD_EN_SHIFT 0 +#define M98925_SPK_ZCD_EN_WIDTH 1 + +/* MAX98925_R02F_SPK_AMP */ +#define M98925_SPK_MODE_MASK (1<<0) +#define M98925_SPK_MODE_SHIFT 0 +#define M98925_SPK_MODE_WIDTH 1 + +/* MAX98925_R030_THRESHOLD */ +#define M98925_ALC_EN_MASK (1<<5) +#define M98925_ALC_EN_SHIFT 5 +#define M98925_ALC_EN_WIDTH 1 +#define M98925_ALC_TH_MASK (0x1F<<0) +#define M98925_ALC_TH_SHIFT 0 +#define M98925_ALC_TH_WIDTH 5 + +/* MAX98925_R031_ALC_ATTACK */ +#define M98925_ALC_ATK_STEP_MASK (0x0F<<4) +#define M98925_ALC_ATK_STEP_SHIFT 4 +#define M98925_ALC_ATK_STEP_WIDTH 4 +#define M98925_ALC_ATK_RATE_MASK (0x7<<0) +#define M98925_ALC_ATK_RATE_SHIFT 0 +#define M98925_ALC_ATK_RATE_WIDTH 3 + +/* MAX98925_R032_ALC_ATTEN_RLS */ +#define M98925_ALC_MAX_ATTEN_MASK (0x0F<<4) +#define M98925_ALC_MAX_ATTEN_SHIFT 4 +#define M98925_ALC_MAX_ATTEN_WIDTH 4 +#define M98925_ALC_RLS_RATE_MASK (0x7<<0) +#define M98925_ALC_RLS_RATE_SHIFT 0 +#define M98925_ALC_RLS_RATE_WIDTH 3 + +/* MAX98925_R033_ALC_HOLD_RLS */ +#define M98925_ALC_RLS_TGR_MASK (1<<0) +#define M98925_ALC_RLS_TGR_SHIFT 0 +#define M98925_ALC_RLS_TGR_WIDTH 1 + +/* MAX98925_R034_ALC_CONFIGURATION */ +#define M98925_ALC_MUTE_EN_MASK (1<<7) +#define M98925_ALC_MUTE_EN_SHIFT 7 +#define M98925_ALC_MUTE_EN_WIDTH 1 +#define M98925_ALC_MUTE_DLY_MASK (0x07<<4) +#define M98925_ALC_MUTE_DLY_SHIFT 4 +#define M98925_ALC_MUTE_DLY_WIDTH 3 +#define M98925_ALC_RLS_DBT_MASK (0x07<<0) +#define M98925_ALC_RLS_DBT_SHIFT 0 +#define M98925_ALC_RLS_DBT_WIDTH 3 + +/* MAX98925_R035_BOOST_CONVERTER */ +#define M98925_BST_SYNC_MASK (1<<7) +#define M98925_BST_SYNC_SHIFT 7 +#define M98925_BST_SYNC_WIDTH 1 +#define M98925_BST_PHASE_MASK (0x03<<4) +#define M98925_BST_PHASE_SHIFT 4 +#define M98925_BST_PHASE_WIDTH 2 +#define M98925_BST_SKIP_MODE_MASK (0x03<<0) +#define M98925_BST_SKIP_MODE_SHIFT 0 +#define M98925_BST_SKIP_MODE_WIDTH 2 + +/* MAX98925_R036_BLOCK_ENABLE */ +#define M98925_BST_EN_MASK (1<<7) +#define M98925_BST_EN_SHIFT 7 +#define M98925_BST_EN_WIDTH 1 +#define M98925_WATCH_EN_MASK (1<<6) +#define M98925_WATCH_EN_SHIFT 6 +#define M98925_WATCH_EN_WIDTH 1 +#define M98925_CLKMON_EN_MASK (1<<5) +#define M98925_CLKMON_EN_SHIFT 5 +#define M98925_CLKMON_EN_WIDTH 1 +#define M98925_SPK_EN_MASK (1<<4) +#define M98925_SPK_EN_SHIFT 4 +#define M98925_SPK_EN_WIDTH 1 +#define M98925_ADC_VBST_EN_MASK (1<<3) +#define M98925_ADC_VBST_EN_SHIFT 3 +#define M98925_ADC_VBST_EN_WIDTH 1 +#define M98925_ADC_VBAT_EN_MASK (1<<2) +#define M98925_ADC_VBAT_EN_SHIFT 2 +#define M98925_ADC_VBAT_EN_WIDTH 1 +#define M98925_ADC_IMON_EN_MASK (1<<1) +#define M98925_ADC_IMON_EN_SHIFT 1 +#define M98925_ADC_IMON_EN_WIDTH 1 +#define M98925_ADC_VMON_EN_MASK (1<<0) +#define M98925_ADC_VMON_EN_SHIFT 0 +#define M98925_ADC_VMON_EN_WIDTH 1 + +/* MAX98925_R037_CONFIGURATION */ +#define M98925_BST_VOUT_MASK (0x0F<<4) +#define M98925_BST_VOUT_SHIFT 4 +#define M98925_BST_VOUT_WIDTH 4 +#define M98925_THERMWARN_LEVEL_MASK (0x03<<2) +#define M98925_THERMWARN_LEVEL_SHIFT 2 +#define M98925_THERMWARN_LEVEL_WIDTH 2 +#define M98925_WATCH_TIME_MASK (0x03<<0) +#define M98925_WATCH_TIME_SHIFT 0 +#define M98925_WATCH_TIME_WIDTH 2 + +/* MAX98925_R038_GLOBAL_ENABLE */ +#define M98925_EN_MASK (1<<7) +#define M98925_EN_SHIFT 7 +#define M98925_EN_WIDTH 1 + +/* MAX98925_R03A_BOOST_LIMITER */ +#define M98925_BST_ILIM_MASK (0x1F<<3) +#define M98925_BST_ILIM_SHIFT 3 +#define M98925_BST_ILIM_WIDTH 5 + +/* MAX98925_R0FF_VERSION */ +#define M98925_REV_ID_MASK (0xFF<<0) +#define M98925_REV_ID_SHIFT 0 +#define M98925_REV_ID_WIDTH 8 + +struct max98925_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + struct max98925_pdata *pdata; + unsigned int sysclk; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spk_gain; + unsigned int ch_size; +}; +#endif diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 474cae82a874..5a30fdd0da00 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -54,6 +54,9 @@ struct pcm512x_priv { int pll_d; int pll_p; unsigned long real_pll; + unsigned long overclock_pll; + unsigned long overclock_dac; + unsigned long overclock_dsp; }; /* @@ -224,6 +227,90 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg) } } +static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_pll; + return 0; +} + +static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec->dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_pll = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dsp; + return 0; +} + +static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec->dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dsp = ucontrol->value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = pcm512x->overclock_dac; + return 0; +} + +static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec->dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x->overclock_dac = ucontrol->value.integer.value[0]; + return 0; +} + static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); @@ -304,9 +391,9 @@ static const struct soc_enum pcm512x_veds = static const struct snd_kcontrol_new pcm512x_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), -SOC_DOUBLE_TLV("Playback Volume", PCM512x_ANALOG_GAIN_CTRL, +SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL, PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), -SOC_DOUBLE_TLV("Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, +SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, PCM512x_RQMR_SHIFT, 1, 1), @@ -328,6 +415,13 @@ SOC_ENUM("Volume Ramp Up Rate", pcm512x_vnuf), SOC_ENUM("Volume Ramp Up Step", pcm512x_vnus), SOC_ENUM("Volume Ramp Down Emergency Rate", pcm512x_vedf), SOC_ENUM("Volume Ramp Down Emergency Step", pcm512x_veds), + +SOC_SINGLE_EXT("Max Overclock PLL", SND_SOC_NOPM, 0, 20, 0, + pcm512x_overclock_pll_get, pcm512x_overclock_pll_put), +SOC_SINGLE_EXT("Max Overclock DSP", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put), +SOC_SINGLE_EXT("Max Overclock DAC", SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dac_get, pcm512x_overclock_dac_put), }; static const struct snd_soc_dapm_widget pcm512x_dapm_widgets[] = { @@ -346,6 +440,45 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { "OUTR", NULL, "DACR" }, }; +static unsigned long pcm512x_pll_max(struct pcm512x_priv *pcm512x) +{ + return 25000000 + 25000000 * pcm512x->overclock_pll / 100; +} + +static unsigned long pcm512x_dsp_max(struct pcm512x_priv *pcm512x) +{ + return 50000000 + 50000000 * pcm512x->overclock_dsp / 100; +} + +static unsigned long pcm512x_dac_max(struct pcm512x_priv *pcm512x, + unsigned long rate) +{ + return rate + rate * pcm512x->overclock_dac / 100; +} + +static unsigned long pcm512x_sck_max(struct pcm512x_priv *pcm512x) +{ + if (!pcm512x->pll_out) + return 25000000; + return pcm512x_pll_max(pcm512x); +} + +static unsigned long pcm512x_ncp_target(struct pcm512x_priv *pcm512x, + unsigned long dac_rate) +{ + /* + * If the DAC is not actually overclocked, use the good old + * NCP target rate... + */ + if (dac_rate <= 6144000) + return 1536000; + /* + * ...but if the DAC is in fact overclocked, bump the NCP target + * rate to get the recommended dividers even when overclocking. + */ + return pcm512x_dac_max(pcm512x, 1536000); +} + static const u32 pcm512x_dai_rates[] = { 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000, 384000, @@ -359,6 +492,7 @@ static const struct snd_pcm_hw_constraint_list constraints_slave = { static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { + struct pcm512x_priv *pcm512x = rule->private; struct snd_interval ranges[2]; int frame_size; @@ -377,7 +511,7 @@ static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, */ memset(ranges, 0, sizeof(ranges)); ranges[0].min = 8000; - ranges[0].max = 25000000 / frame_size / 2; + ranges[0].max = pcm512x_sck_max(pcm512x) / frame_size / 2; ranges[1].min = DIV_ROUND_UP(16000000, frame_size); ranges[1].max = 384000; break; @@ -408,7 +542,7 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, return snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, pcm512x_hw_rule_rate, - NULL, + pcm512x, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); @@ -517,6 +651,8 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, unsigned long bclk_rate) { struct device *dev = dai->dev; + struct snd_soc_codec *codec = dai->codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); unsigned long sck_rate; int pow2; @@ -527,9 +663,10 @@ static unsigned long pcm512x_find_sck(struct snd_soc_dai *dai, * as many factors of 2 as possible, as that makes it easier * to find a fast DAC rate */ - pow2 = 1 << fls((25000000 - 16000000) / bclk_rate); + pow2 = 1 << fls((pcm512x_pll_max(pcm512x) - 16000000) / bclk_rate); for (; pow2; pow2 >>= 1) { - sck_rate = rounddown(25000000, bclk_rate * pow2); + sck_rate = rounddown(pcm512x_pll_max(pcm512x), + bclk_rate * pow2); if (sck_rate >= 16000000) break; } @@ -576,8 +713,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai, /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */ if (pllin_rate / den > 20000000 && num < 8) { - num *= 20000000 / (pllin_rate / den); - den *= 20000000 / (pllin_rate / den); + num *= DIV_ROUND_UP(pllin_rate / den, 20000000); + den *= DIV_ROUND_UP(pllin_rate / den, 20000000); } dev_dbg(dev, "num / den = %lu / %lu\n", num, den); @@ -678,7 +815,7 @@ static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, return 0; /* futile, quit early */ /* run DAC no faster than 6144000 Hz */ - for (dac_rate = rounddown(6144000, osr_rate); + for (dac_rate = rounddown(pcm512x_dac_max(pcm512x, 6144000), osr_rate); dac_rate; dac_rate -= osr_rate) { @@ -805,7 +942,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, osr_rate = 16 * sample_rate; /* run DSP no faster than 50 MHz */ - dsp_div = mck_rate > 50000000 ? 2 : 1; + dsp_div = mck_rate > pcm512x_dsp_max(pcm512x) ? 2 : 1; dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); if (dac_rate) { @@ -836,7 +973,8 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, dacsrc_rate = pllin_rate; } else { /* run DAC no faster than 6144000 Hz */ - unsigned long dac_mul = 6144000 / osr_rate; + unsigned long dac_mul = pcm512x_dac_max(pcm512x, 6144000) + / osr_rate; unsigned long sck_mul = sck_rate / osr_rate; for (; dac_mul; dac_mul--) { @@ -863,28 +1001,30 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, dacsrc_rate = sck_rate; } + osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); + if (osr_div > 128) { + dev_err(dev, "Failed to find OSR divider\n"); + return -EINVAL; + } + dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); if (dac_div > 128) { dev_err(dev, "Failed to find DAC divider\n"); return -EINVAL; } + dac_rate = dacsrc_rate / dac_div; - ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000); - if (ncp_div > 128 || dacsrc_rate / dac_div / ncp_div > 2048000) { + ncp_div = DIV_ROUND_CLOSEST(dac_rate, + pcm512x_ncp_target(pcm512x, dac_rate)); + if (ncp_div > 128 || dac_rate / ncp_div > 2048000) { /* run NCP no faster than 2048000 Hz, but why? */ - ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000); + ncp_div = DIV_ROUND_UP(dac_rate, 2048000); if (ncp_div > 128) { dev_err(dev, "Failed to find NCP divider\n"); return -EINVAL; } } - osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); - if (osr_div > 128) { - dev_err(dev, "Failed to find OSR divider\n"); - return -EINVAL; - } - idac = mck_rate / (dsp_div * sample_rate); ret = regmap_write(pcm512x->regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); @@ -937,11 +1077,11 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, return ret; } - if (sample_rate <= 48000) + if (sample_rate <= pcm512x_dac_max(pcm512x, 48000)) fssp = PCM512x_FSSP_48KHZ; - else if (sample_rate <= 96000) + else if (sample_rate <= pcm512x_dac_max(pcm512x, 96000)) fssp = PCM512x_FSSP_96KHZ; - else if (sample_rate <= 192000) + else if (sample_rate <= pcm512x_dac_max(pcm512x, 192000)) fssp = PCM512x_FSSP_192KHZ; else fssp = PCM512x_FSSP_384KHZ; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 9b541e52da8c..0fcda35a3a93 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -395,9 +395,20 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) rt286->jack = jack; - /* Send an initial empty report */ - snd_soc_jack_report(rt286->jack, 0, - SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + if (jack) { + /* enable IRQ */ + if (rt286->jack->status & SND_JACK_HEADPHONE) + snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); + /* Send an initial empty report */ + snd_soc_jack_report(rt286->jack, rt286->jack->status, + SND_JACK_MICROPHONE | SND_JACK_HEADPHONE); + } else { + /* disable IRQ */ + regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x0); + snd_soc_dapm_disable_pin(&codec->dapm, "LDO1"); + } + snd_soc_dapm_sync(&codec->dapm); return 0; } @@ -1037,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec) struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); rt286->codec = codec; - codec->dapm.bias_level = SND_SOC_BIAS_OFF; if (rt286->i2c->irq) { regmap_update_bits(rt286->regmap, @@ -1209,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, { struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt286_priv *rt286; - int i, ret; + int i, ret, val; rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), GFP_KERNEL); @@ -1224,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c, return ret; } - regmap_read(rt286->regmap, - RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); - if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) { + ret = regmap_read(rt286->regmap, + RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val); + if (ret != 0) { + dev_err(&i2c->dev, "I2C error %d\n", ret); + return ret; + } + if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) { dev_err(&i2c->dev, - "Device with ID register %x is not rt286\n", ret); + "Device with ID register %x is not rt286\n", val); return -ENODEV; } @@ -1236,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c, rt286->i2c = i2c; i2c_set_clientdata(i2c, rt286); + /* restore codec default */ + for (i = 0; i < INDEX_CACHE_SIZE; i++) + regmap_write(rt286->regmap, rt286->index_cache[i].reg, + rt286->index_cache[i].def); + for (i = 0; i < ARRAY_SIZE(rt286_reg); i++) + regmap_write(rt286->regmap, rt286_reg[i].reg, + rt286_reg[i].def); + if (pdata) rt286->pdata = *pdata; diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index c61852742ee3..2c10d77727af 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1675,7 +1675,7 @@ static const struct i2c_device_id rt5631_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); #ifdef CONFIG_OF -static struct of_device_id rt5631_i2c_dt_ids[] = { +static const struct of_device_id rt5631_i2c_dt_ids[] = { { .compatible = "realtek,rt5631"}, { .compatible = "realtek,alc5631"}, { } diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index c9a4c5be083b..69528ae5410c 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -1270,6 +1270,8 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on) snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_HP_L | RT5645_PWR_HP_R | RT5645_PWR_HA, 0); + snd_soc_update_bits(codec, RT5645_DEPOP_M2, + RT5645_DEPOP_MASK, 0); } } } @@ -1538,8 +1540,6 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY_S("adc stereo1 filter", 1, RT5645_PWR_DIG2, RT5645_PWR_ADC_S1F_BIT, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY_S("adc stereo2 filter", 1, RT5645_PWR_DIG2, - RT5645_PWR_ADC_S2F_BIT, 0, NULL, 0), SND_SOC_DAPM_MIXER_E("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0, rt5645_sto1_adc_l_mix, ARRAY_SIZE(rt5645_sto1_adc_l_mix), NULL, 0), @@ -1729,7 +1729,6 @@ static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = { static const struct snd_soc_dapm_route rt5645_dapm_routes[] = { { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc }, - { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc }, { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc }, { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc }, { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc }, @@ -2052,7 +2051,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_codec *codec = dai->codec; struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - unsigned int val_len = 0, val_clk, mask_clk; + unsigned int val_len = 0, val_clk, mask_clk, dl_sft; int pre_div, bclk_ms, frame_size; rt5645->lrck[dai->id] = params_rate(params); @@ -2066,6 +2065,16 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size); return -EINVAL; } + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + dl_sft = 4; + break; + default: + dl_sft = 2; + break; + } + bclk_ms = frame_size > 32; rt5645->bclk[dai->id] = rt5645->lrck[dai->id] * (32 << bclk_ms); @@ -2078,13 +2087,13 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, case 16: break; case 20: - val_len |= RT5645_I2S_DL_20; + val_len = 0x1; break; case 24: - val_len |= RT5645_I2S_DL_24; + val_len = 0x2; break; case 8: - val_len |= RT5645_I2S_DL_8; + val_len = 0x3; break; default: return -EINVAL; @@ -2096,7 +2105,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, val_clk = bclk_ms << RT5645_I2S_BCLK_MS1_SFT | pre_div << RT5645_I2S_PD1_SFT; snd_soc_update_bits(codec, RT5645_I2S1_SDP, - RT5645_I2S_DL_MASK, val_len); + (0x3 << dl_sft), (val_len << dl_sft)); snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); break; case RT5645_AIF2: @@ -2104,7 +2113,7 @@ static int rt5645_hw_params(struct snd_pcm_substream *substream, val_clk = bclk_ms << RT5645_I2S_BCLK_MS2_SFT | pre_div << RT5645_I2S_PD2_SFT; snd_soc_update_bits(codec, RT5645_I2S2_SDP, - RT5645_I2S_DL_MASK, val_len); + (0x3 << dl_sft), (val_len << dl_sft)); snd_soc_update_bits(codec, RT5645_ADDA_CLK1, mask_clk, val_clk); break; default: @@ -2119,7 +2128,16 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); - unsigned int reg_val = 0; + unsigned int reg_val = 0, pol_sft; + + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + pol_sft = 8; + break; + default: + pol_sft = 7; + break; + } switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: @@ -2137,7 +2155,7 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_NF: - reg_val |= RT5645_I2S_BP_INV; + reg_val |= (1 << pol_sft); break; default: return -EINVAL; @@ -2161,12 +2179,12 @@ static int rt5645_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (dai->id) { case RT5645_AIF1: snd_soc_update_bits(codec, RT5645_I2S1_SDP, - RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_MS_MASK | (1 << pol_sft) | RT5645_I2S_DF_MASK, reg_val); break; case RT5645_AIF2: snd_soc_update_bits(codec, RT5645_I2S2_SDP, - RT5645_I2S_MS_MASK | RT5645_I2S_BP_MASK | + RT5645_I2S_MS_MASK | (1 << pol_sft) | RT5645_I2S_DF_MASK, reg_val); break; default: @@ -2285,23 +2303,42 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { struct snd_soc_codec *codec = dai->codec; - unsigned int val = 0; + struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec); + unsigned int i_slot_sft, o_slot_sft, i_width_sht, o_width_sht, en_sft; + unsigned int mask, val = 0; + switch (rt5645->codec_type) { + case CODEC_TYPE_RT5650: + en_sft = 15; + i_slot_sft = 10; + o_slot_sft = 8; + i_width_sht = 6; + o_width_sht = 4; + mask = 0x8ff0; + break; + default: + en_sft = 14; + i_slot_sft = o_slot_sft = 12; + i_width_sht = o_width_sht = 10; + mask = 0x7c00; + break; + } if (rx_mask || tx_mask) { - val |= (1 << 14); - snd_soc_update_bits(codec, RT5645_BASS_BACK, - RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); + val |= (1 << en_sft); + if (rt5645->codec_type == CODEC_TYPE_RT5645) + snd_soc_update_bits(codec, RT5645_BASS_BACK, + RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB); } switch (slots) { case 4: - val |= (1 << 12); + val |= (1 << i_slot_sft) | (1 << o_slot_sft); break; case 6: - val |= (2 << 12); + val |= (2 << i_slot_sft) | (2 << o_slot_sft); break; case 8: - val |= (3 << 12); + val |= (3 << i_slot_sft) | (3 << o_slot_sft); break; case 2: default: @@ -2310,20 +2347,20 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, switch (slot_width) { case 20: - val |= (1 << 10); + val |= (1 << i_width_sht) | (1 << o_width_sht); break; case 24: - val |= (2 << 10); + val |= (2 << i_width_sht) | (2 << o_width_sht); break; case 32: - val |= (3 << 10); + val |= (3 << i_width_sht) | (3 << o_width_sht); break; case 16: default: break; } - snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, 0x7c00, val); + snd_soc_update_bits(codec, RT5645_TDM_CTRL_1, mask, val); return 0; } @@ -2361,7 +2398,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_OFF: snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100); - snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128); + snd_soc_update_bits(codec, RT5645_GEN_CTRL1, + RT5645_DIG_GATE_CTRL, 0); snd_soc_update_bits(codec, RT5645_PWR_ANLG1, RT5645_PWR_VREF1 | RT5645_PWR_MB | RT5645_PWR_BG | RT5645_PWR_VREF2 | @@ -2598,7 +2636,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5645 = { static const struct regmap_config rt5645_regmap = { .reg_bits = 8, .val_bits = 16, - + .use_single_rw = true, .max_register = RT5645_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5645_ranges) * RT5645_PR_SPACING), .volatile_reg = rt5645_volatile_register, diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index dbfd98c22f4d..db78e9462876 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -804,8 +804,6 @@ #define RT5645_PWR_DAC_MF_L_BIT 10 #define RT5645_PWR_DAC_MF_R (0x1 << 9) #define RT5645_PWR_DAC_MF_R_BIT 9 -#define RT5645_PWR_ADC_S2F (0x1 << 8) -#define RT5645_PWR_ADC_S2F_BIT 8 #define RT5645_PWR_PDM1 (0x1 << 7) #define RT5645_PWR_PDM1_BIT 7 #define RT5645_PWR_PDM2 (0x1 << 6) diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index fd102613d20d..cc7f84a150a7 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -403,6 +403,189 @@ static bool rt5670_readable_register(struct device *dev, unsigned int reg) } } +/** + * rt5670_headset_detect - Detect headset. + * @codec: SoC audio codec device. + * @jack_insert: Jack insert or not. + * + * Detect whether is headset or not when jack inserted. + * + * Returns detect status. + */ + +static int rt5670_headset_detect(struct snd_soc_codec *codec, int jack_insert) +{ + int val; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (jack_insert) { + snd_soc_dapm_force_enable_pin(&codec->dapm, + "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_DET_MODE | RT5670_CBJ_MN_JD, + RT5670_CBJ_MN_JD); + snd_soc_write(codec, RT5670_GPIO_CTRL2, 0x0004); + snd_soc_update_bits(codec, RT5670_GPIO_CTRL1, + RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); + snd_soc_update_bits(codec, RT5670_CJ_CTRL1, + RT5670_CBJ_BST1_EN, RT5670_CBJ_BST1_EN); + snd_soc_write(codec, RT5670_JD_CTRL3, 0x00f0); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, RT5670_CBJ_MN_JD); + snd_soc_update_bits(codec, RT5670_CJ_CTRL2, + RT5670_CBJ_MN_JD, 0); + msleep(300); + val = snd_soc_read(codec, RT5670_CJ_CTRL3) & 0x7; + if (val == 0x1 || val == 0x2) { + rt5670->jack_type = SND_JACK_HEADSET; + /* for push button */ + snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x8); + snd_soc_update_bits(codec, RT5670_IL_CMD, 0x40, 0x40); + snd_soc_read(codec, RT5670_IL_CMD); + } else { + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = SND_JACK_HEADPHONE; + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + } else { + snd_soc_update_bits(codec, RT5670_INT_IRQ_ST, 0x8, 0x0); + snd_soc_update_bits(codec, RT5670_GEN_CTRL3, 0x4, 0x4); + rt5670->jack_type = 0; + snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power"); + snd_soc_dapm_sync(&codec->dapm); + } + + return rt5670->jack_type; +} + +void rt5670_jack_suspend(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + rt5670->jack_type_saved = rt5670->jack_type; + rt5670_headset_detect(codec, 0); +} +EXPORT_SYMBOL_GPL(rt5670_jack_suspend); + +void rt5670_jack_resume(struct snd_soc_codec *codec) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + + if (rt5670->jack_type_saved) + rt5670_headset_detect(codec, 1); +} +EXPORT_SYMBOL_GPL(rt5670_jack_resume); + +static int rt5670_button_detect(struct snd_soc_codec *codec) +{ + int btn_type, val; + + val = snd_soc_read(codec, RT5670_IL_CMD); + btn_type = val & 0xff80; + snd_soc_write(codec, RT5670_IL_CMD, val); + if (btn_type != 0) { + msleep(20); + val = snd_soc_read(codec, RT5670_IL_CMD); + snd_soc_write(codec, RT5670_IL_CMD, val); + } + + return btn_type; +} + +static int rt5670_irq_detection(void *data) +{ + struct rt5670_priv *rt5670 = (struct rt5670_priv *)data; + struct snd_soc_jack_gpio *gpio = &rt5670->hp_gpio; + struct snd_soc_jack *jack = rt5670->jack; + int val, btn_type, report = jack->status; + + if (rt5670->pdata.jd_mode == 1) /* 2 port */ + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0070; + else + val = snd_soc_read(rt5670->codec, RT5670_A_JD_CTRL1) & 0x0020; + + switch (val) { + /* jack in */ + case 0x30: /* 2 port */ + case 0x0: /* 1 port or 2 port */ + if (rt5670->jack_type == 0) { + report = rt5670_headset_detect(rt5670->codec, 1); + /* for push button and jack out */ + gpio->debounce_time = 25; + break; + } + btn_type = 0; + if (snd_soc_read(rt5670->codec, RT5670_INT_IRQ_ST) & 0x4) { + /* button pressed */ + report = SND_JACK_HEADSET; + btn_type = rt5670_button_detect(rt5670->codec); + switch (btn_type) { + case 0x2000: /* up */ + report |= SND_JACK_BTN_1; + break; + case 0x0400: /* center */ + report |= SND_JACK_BTN_0; + break; + case 0x0080: /* down */ + report |= SND_JACK_BTN_2; + break; + default: + dev_err(rt5670->codec->dev, + "Unexpected button code 0x%04x\n", + btn_type); + break; + } + } + if (btn_type == 0)/* button release */ + report = rt5670->jack_type; + + break; + /* jack out */ + case 0x70: /* 2 port */ + case 0x10: /* 2 port */ + case 0x20: /* 1 port */ + report = 0; + snd_soc_update_bits(rt5670->codec, RT5670_INT_IRQ_ST, 0x1, 0x0); + rt5670_headset_detect(rt5670->codec, 0); + gpio->debounce_time = 150; /* for jack in */ + break; + default: + break; + } + + return report; +} + +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); + int ret; + + rt5670->jack = jack; + rt5670->hp_gpio.gpiod_dev = codec->dev; + rt5670->hp_gpio.name = "headphone detect"; + rt5670->hp_gpio.report = SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2; + rt5670->hp_gpio.debounce_time = 150; + rt5670->hp_gpio.wake = true; + rt5670->hp_gpio.data = (struct rt5670_priv *)rt5670; + rt5670->hp_gpio.jack_status_check = rt5670_irq_detection; + + ret = snd_soc_jack_add_gpios(rt5670->jack, 1, + &rt5670->hp_gpio); + if (ret) { + dev_err(codec->dev, "Adding jack GPIO failed\n"); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(rt5670_set_jack_detect); + static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0); static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0); @@ -517,11 +700,9 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm); - unsigned int val; + struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); - val = snd_soc_read(codec, RT5670_GLB_CLK); - val &= RT5670_SCLK_SRC_MASK; - if (val == RT5670_SCLK_SRC_PLL1) + if (rt5670->sysclk_src == RT5670_SCLK_S_PLL1) return 1; else return 0; @@ -2271,16 +2452,6 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); unsigned int reg_val = 0; - if (freq == rt5670->sysclk && clk_id == rt5670->sysclk_src) - return 0; - - if (rt5670->pdata.jd_mode) { - if (clk_id == RT5670_SCLK_S_PLL1) - snd_soc_dapm_force_enable_pin(&codec->dapm, "PLL1"); - else - snd_soc_dapm_disable_pin(&codec->dapm, "PLL1"); - snd_soc_dapm_sync(&codec->dapm); - } switch (clk_id) { case RT5670_SCLK_S_MCLK: reg_val |= RT5670_SCLK_SRC_MCLK; @@ -2298,7 +2469,8 @@ static int rt5670_set_dai_sysclk(struct snd_soc_dai *dai, snd_soc_update_bits(codec, RT5670_GLB_CLK, RT5670_SCLK_SRC_MASK, reg_val); rt5670->sysclk = freq; - rt5670->sysclk_src = clk_id; + if (clk_id != RT5670_SCLK_S_RCCLK) + rt5670->sysclk_src = clk_id; dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id); @@ -2517,6 +2689,7 @@ static int rt5670_remove(struct snd_soc_codec *codec) struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec); regmap_write(rt5670->regmap, RT5670_RESET, 0); + snd_soc_jack_free_gpios(rt5670->jack, 1, &rt5670->hp_gpio); return 0; } @@ -2676,6 +2849,7 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, if (dmi_check_system(dmi_platform_intel_braswell)) { rt5670->pdata.dmic_en = true; rt5670->pdata.dmic1_data_pin = RT5670_DMIC_DATA_IN2P; + rt5670->pdata.dev_gpio = true; rt5670->pdata.jd_mode = 1; } @@ -2717,12 +2891,17 @@ static int rt5670_i2c_probe(struct i2c_client *i2c, regmap_update_bits(rt5670->regmap, RT5670_IN2, RT5670_IN_DF2, RT5670_IN_DF2); - if (i2c->irq) { + if (rt5670->pdata.dev_gpio) { + /* for push button */ + regmap_write(rt5670->regmap, RT5670_IL_CMD, 0x0000); + regmap_write(rt5670->regmap, RT5670_IL_CMD2, 0x0010); + regmap_write(rt5670->regmap, RT5670_IL_CMD3, 0x0014); + /* for irq */ regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL1, RT5670_GP1_PIN_MASK, RT5670_GP1_PIN_IRQ); regmap_update_bits(rt5670->regmap, RT5670_GPIO_CTRL2, RT5670_GP1_PF_MASK, RT5670_GP1_PF_OUT); - + regmap_update_bits(rt5670->regmap, RT5670_DIG_MISC, 0x8, 0x8); } if (rt5670->pdata.jd_mode) { diff --git a/sound/soc/codecs/rt5670.h b/sound/soc/codecs/rt5670.h index 21f8e18c13c4..dc2b46236c5c 100644 --- a/sound/soc/codecs/rt5670.h +++ b/sound/soc/codecs/rt5670.h @@ -1950,17 +1950,20 @@ enum { }; enum { + RT5670_DMIC1_DISABLED, RT5670_DMIC_DATA_GPIO6, RT5670_DMIC_DATA_IN2P, RT5670_DMIC_DATA_GPIO7, }; enum { + RT5670_DMIC2_DISABLED, RT5670_DMIC_DATA_GPIO8, RT5670_DMIC_DATA_IN3N, }; enum { + RT5670_DMIC3_DISABLED, RT5670_DMIC_DATA_GPIO9, RT5670_DMIC_DATA_GPIO10, RT5670_DMIC_DATA_GPIO5, @@ -1985,6 +1988,8 @@ struct rt5670_priv { struct snd_soc_codec *codec; struct rt5670_platform_data pdata; struct regmap *regmap; + struct snd_soc_jack *jack; + struct snd_soc_jack_gpio hp_gpio; int sysclk; int sysclk_src; @@ -1999,6 +2004,11 @@ struct rt5670_priv { int dsp_sw; /* expected parameter setting */ int dsp_rate; int jack_type; + int jack_type_saved; }; +void rt5670_jack_suspend(struct snd_soc_codec *codec); +void rt5670_jack_resume(struct snd_soc_codec *codec); +int rt5670_set_jack_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); #endif /* __RT5670_H__ */ diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index fb9c20eace3f..af182586712d 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -718,11 +718,24 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on) RT5677_LDO1_SEL_MASK, 0x0); regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2, RT5677_PWR_LDO1, RT5677_PWR_LDO1); - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, - RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); - regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, - RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK, - RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS); + switch (rt5677->type) { + case RT5677: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1, + RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC); + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_PLL2_PR_SRC_MASK | + RT5677_DSP_CLK_SRC_MASK, + RT5677_PLL2_PR_SRC_MCLK2 | + RT5677_DSP_CLK_SRC_BYPASS); + break; + case RT5676: + regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2, + RT5677_DSP_CLK_SRC_MASK, + RT5677_DSP_CLK_SRC_BYPASS); + break; + default: + break; + } regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff); regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd); rt5677_set_dsp_mode(codec, true); @@ -1021,6 +1034,169 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source, return 0; } +/** + * rt5677_sel_asrc_clk_src - select ASRC clock source for a set of filters + * @codec: SoC audio codec device. + * @filter_mask: mask of filters. + * @clk_src: clock source + * + * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5677 can + * only support standard 32fs or 64fs i2s format, ASRC should be enabled to + * support special i2s clock format such as Intel's 100fs(100 * sampling rate). + * ASRC function will track i2s clock and generate a corresponding system clock + * for codec. This function provides an API to select the clock source for a + * set of filters specified by the mask. And the codec driver will turn on ASRC + * for these filters if ASRC is selected as their clock source. + */ +int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src) +{ + struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); + unsigned int asrc3_mask = 0, asrc3_value = 0; + unsigned int asrc4_mask = 0, asrc4_value = 0; + unsigned int asrc5_mask = 0, asrc5_value = 0; + unsigned int asrc6_mask = 0, asrc6_value = 0; + unsigned int asrc7_mask = 0, asrc7_value = 0; + + switch (clk_src) { + case RT5677_CLK_SEL_SYS: + case RT5677_CLK_SEL_I2S1_ASRC: + case RT5677_CLK_SEL_I2S2_ASRC: + case RT5677_CLK_SEL_I2S3_ASRC: + case RT5677_CLK_SEL_I2S4_ASRC: + case RT5677_CLK_SEL_I2S5_ASRC: + case RT5677_CLK_SEL_I2S6_ASRC: + case RT5677_CLK_SEL_SYS2: + case RT5677_CLK_SEL_SYS3: + case RT5677_CLK_SEL_SYS4: + case RT5677_CLK_SEL_SYS5: + case RT5677_CLK_SEL_SYS6: + case RT5677_CLK_SEL_SYS7: + break; + + default: + return -EINVAL; + } + + /* ASRC 3 */ + if (filter_mask & RT5677_DA_STEREO_FILTER) { + asrc3_mask |= RT5677_DA_STO_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_STO_CLK_SEL_MASK) + | (clk_src << RT5677_DA_STO_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_L_FILTER) { + asrc3_mask |= RT5677_DA_MONO2L_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO2_R_FILTER) { + asrc3_mask |= RT5677_DA_MONO2R_CLK_SEL_MASK; + asrc3_value = (asrc3_value & ~RT5677_DA_MONO2R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO2R_CLK_SEL_SFT); + } + + if (asrc3_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_3, asrc3_mask, + asrc3_value); + + /* ASRC 4 */ + if (filter_mask & RT5677_DA_MONO3_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO3L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO3_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO3R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO3R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO3R_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_L_FILTER) { + asrc4_mask |= RT5677_DA_MONO4L_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4L_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4L_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DA_MONO4_R_FILTER) { + asrc4_mask |= RT5677_DA_MONO4R_CLK_SEL_MASK; + asrc4_value = (asrc4_value & ~RT5677_DA_MONO4R_CLK_SEL_MASK) + | (clk_src << RT5677_DA_MONO4R_CLK_SEL_SFT); + } + + if (asrc4_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_4, asrc4_mask, + asrc4_value); + + /* ASRC 5 */ + if (filter_mask & RT5677_AD_STEREO1_FILTER) { + asrc5_mask |= RT5677_AD_STO1_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO1_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO1_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO2_FILTER) { + asrc5_mask |= RT5677_AD_STO2_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO2_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO2_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO3_FILTER) { + asrc5_mask |= RT5677_AD_STO3_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO3_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_STEREO4_FILTER) { + asrc5_mask |= RT5677_AD_STO4_CLK_SEL_MASK; + asrc5_value = (asrc5_value & ~RT5677_AD_STO4_CLK_SEL_MASK) + | (clk_src << RT5677_AD_STO4_CLK_SEL_SFT); + } + + if (asrc5_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_5, asrc5_mask, + asrc5_value); + + /* ASRC 6 */ + if (filter_mask & RT5677_AD_MONO_L_FILTER) { + asrc6_mask |= RT5677_AD_MONOL_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOL_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOL_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_AD_MONO_R_FILTER) { + asrc6_mask |= RT5677_AD_MONOR_CLK_SEL_MASK; + asrc6_value = (asrc6_value & ~RT5677_AD_MONOR_CLK_SEL_MASK) + | (clk_src << RT5677_AD_MONOR_CLK_SEL_SFT); + } + + if (asrc6_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_6, asrc6_mask, + asrc6_value); + + /* ASRC 7 */ + if (filter_mask & RT5677_DSP_OB_0_3_FILTER) { + asrc7_mask |= RT5677_DSP_OB_0_3_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_0_3_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_0_3_CLK_SEL_SFT); + } + + if (filter_mask & RT5677_DSP_OB_4_7_FILTER) { + asrc7_mask |= RT5677_DSP_OB_4_7_CLK_SEL_MASK; + asrc7_value = (asrc7_value & ~RT5677_DSP_OB_4_7_CLK_SEL_MASK) + | (clk_src << RT5677_DSP_OB_4_7_CLK_SEL_SFT); + } + + if (asrc7_mask) + regmap_update_bits(rt5677->regmap, RT5677_ASRC_7, asrc7_mask, + asrc7_value); + + return 0; +} +EXPORT_SYMBOL_GPL(rt5677_sel_asrc_clk_src); + /* Digital Mixer */ static const struct snd_kcontrol_new rt5677_sto1_adc_l_mix[] = { SOC_DAPM_SINGLE("ADC1 Switch", RT5677_STO1_ADC_MIXER, @@ -4500,10 +4676,10 @@ static int rt5677_suspend(struct snd_soc_codec *codec) if (!rt5677->dsp_vad_en) { regcache_cache_only(rt5677->regmap, true); regcache_mark_dirty(rt5677->regmap); - } - if (gpio_is_valid(rt5677->pow_ldo2)) - gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + if (gpio_is_valid(rt5677->pow_ldo2)) + gpio_set_value_cansleep(rt5677->pow_ldo2, 0); + } return 0; } @@ -4512,12 +4688,12 @@ static int rt5677_resume(struct snd_soc_codec *codec) { struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec); - if (gpio_is_valid(rt5677->pow_ldo2)) { - gpio_set_value_cansleep(rt5677->pow_ldo2, 1); - msleep(10); - } - if (!rt5677->dsp_vad_en) { + if (gpio_is_valid(rt5677->pow_ldo2)) { + gpio_set_value_cansleep(rt5677->pow_ldo2, 1); + msleep(10); + } + regcache_cache_only(rt5677->regmap, false); regcache_sync(rt5677->regmap); } @@ -4733,7 +4909,8 @@ static const struct regmap_config rt5677_regmap = { }; static const struct i2c_device_id rt5677_i2c_id[] = { - { "rt5677", 0 }, + { "rt5677", RT5677 }, + { "rt5676", RT5676 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); @@ -4850,6 +5027,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c, i2c_set_clientdata(i2c, rt5677); + rt5677->type = id->driver_data; + if (pdata) rt5677->pdata = *pdata; diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h index c0a625f290cc..9dceb41d18ea 100644 --- a/sound/soc/codecs/rt5677.h +++ b/sound/soc/codecs/rt5677.h @@ -1406,6 +1406,46 @@ #define RT5677_DSP_CLK_SRC_PLL2 (0x0 << 7) #define RT5677_DSP_CLK_SRC_BYPASS (0x1 << 7) +/* ASRC Control 3 (0x85) */ +#define RT5677_DA_STO_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_STO_CLK_SEL_SFT 12 +#define RT5677_DA_MONO2L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO2L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO2R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO2R_CLK_SEL_SFT 0 + +/* ASRC Control 4 (0x86) */ +#define RT5677_DA_MONO3L_CLK_SEL_MASK (0xf << 12) +#define RT5677_DA_MONO3L_CLK_SEL_SFT 12 +#define RT5677_DA_MONO3R_CLK_SEL_MASK (0xf << 8) +#define RT5677_DA_MONO3R_CLK_SEL_SFT 8 +#define RT5677_DA_MONO4L_CLK_SEL_MASK (0xf << 4) +#define RT5677_DA_MONO4L_CLK_SEL_SFT 4 +#define RT5677_DA_MONO4R_CLK_SEL_MASK (0xf << 0) +#define RT5677_DA_MONO4R_CLK_SEL_SFT 0 + +/* ASRC Control 5 (0x87) */ +#define RT5677_AD_STO1_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_STO1_CLK_SEL_SFT 12 +#define RT5677_AD_STO2_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_STO2_CLK_SEL_SFT 8 +#define RT5677_AD_STO3_CLK_SEL_MASK (0xf << 4) +#define RT5677_AD_STO3_CLK_SEL_SFT 4 +#define RT5677_AD_STO4_CLK_SEL_MASK (0xf << 0) +#define RT5677_AD_STO4_CLK_SEL_SFT 0 + +/* ASRC Control 6 (0x88) */ +#define RT5677_AD_MONOL_CLK_SEL_MASK (0xf << 12) +#define RT5677_AD_MONOL_CLK_SEL_SFT 12 +#define RT5677_AD_MONOR_CLK_SEL_MASK (0xf << 8) +#define RT5677_AD_MONOR_CLK_SEL_SFT 8 + +/* ASRC Control 7 (0x89) */ +#define RT5677_DSP_OB_0_3_CLK_SEL_MASK (0xf << 12) +#define RT5677_DSP_OB_0_3_CLK_SEL_SFT 12 +#define RT5677_DSP_OB_4_7_CLK_SEL_MASK (0xf << 8) +#define RT5677_DSP_OB_4_7_CLK_SEL_SFT 8 + /* VAD Function Control 4 (0x9f) */ #define RT5677_VAD_SRC_MASK (0x7 << 8) #define RT5677_VAD_SRC_SFT 8 @@ -1665,6 +1705,47 @@ enum { RT5677_IRQ_JD3, }; +enum rt5677_type { + RT5677, + RT5676, +}; + +/* ASRC clock source selection */ +enum { + RT5677_CLK_SEL_SYS, + RT5677_CLK_SEL_I2S1_ASRC, + RT5677_CLK_SEL_I2S2_ASRC, + RT5677_CLK_SEL_I2S3_ASRC, + RT5677_CLK_SEL_I2S4_ASRC, + RT5677_CLK_SEL_I2S5_ASRC, + RT5677_CLK_SEL_I2S6_ASRC, + RT5677_CLK_SEL_SYS2, + RT5677_CLK_SEL_SYS3, + RT5677_CLK_SEL_SYS4, + RT5677_CLK_SEL_SYS5, + RT5677_CLK_SEL_SYS6, + RT5677_CLK_SEL_SYS7, +}; + +/* filter mask */ +enum { + RT5677_DA_STEREO_FILTER = 0x1, + RT5677_DA_MONO2_L_FILTER = (0x1 << 1), + RT5677_DA_MONO2_R_FILTER = (0x1 << 2), + RT5677_DA_MONO3_L_FILTER = (0x1 << 3), + RT5677_DA_MONO3_R_FILTER = (0x1 << 4), + RT5677_DA_MONO4_L_FILTER = (0x1 << 5), + RT5677_DA_MONO4_R_FILTER = (0x1 << 6), + RT5677_AD_STEREO1_FILTER = (0x1 << 7), + RT5677_AD_STEREO2_FILTER = (0x1 << 8), + RT5677_AD_STEREO3_FILTER = (0x1 << 9), + RT5677_AD_STEREO4_FILTER = (0x1 << 10), + RT5677_AD_MONO_L_FILTER = (0x1 << 11), + RT5677_AD_MONO_R_FILTER = (0x1 << 12), + RT5677_DSP_OB_0_3_FILTER = (0x1 << 13), + RT5677_DSP_OB_4_7_FILTER = (0x1 << 14), +}; + struct rt5677_priv { struct snd_soc_codec *codec; struct rt5677_platform_data pdata; @@ -1681,6 +1762,7 @@ struct rt5677_priv { int pll_in; int pll_out; int pow_ldo2; /* POW_LDO2 pin */ + enum rt5677_type type; #ifdef CONFIG_GPIOLIB struct gpio_chip gpio_chip; #endif @@ -1690,4 +1772,7 @@ struct rt5677_priv { bool is_vref_slow; }; +int rt5677_sel_asrc_clk_src(struct snd_soc_codec *codec, + unsigned int filter_mask, unsigned int clk_src); + #endif /* __RT5677_H__ */ diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 82095d6cd070..7947c0ebb1ed 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -783,19 +783,21 @@ static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec) snd_soc_write(codec, SN95031_BTNCTRL2, 0x01); } -static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack) +static int sn95031_get_headset_state(struct snd_soc_codec *codec, + struct snd_soc_jack *mfld_jack) { - int micbias = sn95031_get_mic_bias(mfld_jack->codec); + int micbias = sn95031_get_mic_bias(codec); int jack_type = snd_soc_jack_get_type(mfld_jack, micbias); pr_debug("jack type detected = %d\n", jack_type); if (jack_type == SND_JACK_HEADSET) - sn95031_enable_jack_btn(mfld_jack->codec); + sn95031_enable_jack_btn(codec); return jack_type; } -void sn95031_jack_detection(struct mfld_jack_data *jack_data) +void sn95031_jack_detection(struct snd_soc_codec *codec, + struct mfld_jack_data *jack_data) { unsigned int status; unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET; @@ -809,11 +811,11 @@ void sn95031_jack_detection(struct mfld_jack_data *jack_data) status = SND_JACK_HEADSET | SND_JACK_BTN_1; } else if (jack_data->intr_id & 0x4) { pr_debug("headset or headphones inserted\n"); - status = sn95031_get_headset_state(jack_data->mfld_jack); + status = sn95031_get_headset_state(codec, jack_data->mfld_jack); } else if (jack_data->intr_id & 0x8) { pr_debug("headset or headphones removed\n"); status = 0; - sn95031_disable_jack_btn(jack_data->mfld_jack->codec); + sn95031_disable_jack_btn(codec); } else { pr_err("unidentified interrupt\n"); return; diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h index 20376d234fb8..7651fe4e6a45 100644 --- a/sound/soc/codecs/sn95031.h +++ b/sound/soc/codecs/sn95031.h @@ -127,6 +127,7 @@ struct mfld_jack_data { struct snd_soc_jack *mfld_jack; }; -extern void sn95031_jack_detection(struct mfld_jack_data *jack_data); +extern void sn95031_jack_detection(struct snd_soc_codec *codec, + struct mfld_jack_data *jack_data); #endif diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index bda2ee18769e..669e3228241e 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1213,27 +1213,15 @@ static int sta350_i2c_probe(struct i2c_client *i2c, #endif /* GPIOs */ - sta350->gpiod_nreset = devm_gpiod_get(dev, "reset"); - if (IS_ERR(sta350->gpiod_nreset)) { - ret = PTR_ERR(sta350->gpiod_nreset); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta350->gpiod_nreset = NULL; - } else { - gpiod_direction_output(sta350->gpiod_nreset, 0); - } - - sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down"); - if (IS_ERR(sta350->gpiod_power_down)) { - ret = PTR_ERR(sta350->gpiod_power_down); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - sta350->gpiod_power_down = NULL; - } else { - gpiod_direction_output(sta350->gpiod_power_down, 0); - } + sta350->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(sta350->gpiod_nreset)) + return PTR_ERR(sta350->gpiod_nreset); + + sta350->gpiod_power_down = devm_gpiod_get(dev, "power-down", + GPIOD_OUT_LOW); + if (IS_ERR(sta350->gpiod_power_down)) + return PTR_ERR(sta350->gpiod_power_down); /* regulators */ for (i = 0; i < ARRAY_SIZE(sta350->supplies); i++) diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index ae23acdd2708..dfb4ff5cc9ea 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -485,16 +485,9 @@ static int tas2552_probe(struct i2c_client *client, if (data == NULL) return -ENOMEM; - data->enable_gpio = devm_gpiod_get(dev, "enable"); - if (IS_ERR(data->enable_gpio)) { - ret = PTR_ERR(data->enable_gpio); - if (ret != -ENOENT && ret != -ENOSYS) - return ret; - - data->enable_gpio = NULL; - } else { - gpiod_direction_output(data->enable_gpio, 0); - } + data->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(data->enable_gpio)) + return PTR_ERR(data->enable_gpio); data->tas2552_client = client; data->regmap = devm_regmap_init_i2c(client, &tas2552_regmap_config); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index f13701995482..78a94af65518 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -31,7 +31,7 @@ static int tlv320aic23_i2c_probe(struct i2c_client *i2c, return tlv320aic23_probe(&i2c->dev, regmap); } -static int __exit tlv320aic23_i2c_remove(struct i2c_client *i2c) +static int tlv320aic23_i2c_remove(struct i2c_client *i2c) { snd_soc_unregister_codec(&i2c->dev); return 0; @@ -56,7 +56,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .of_match_table = of_match_ptr(tlv320aic23_of_match), }, .probe = tlv320aic23_i2c_probe, - .remove = __exit_p(tlv320aic23_i2c_remove), + .remove = tlv320aic23_i2c_remove, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 15599845a660..5a9da28f4f33 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1554,7 +1554,6 @@ static int wm2200_probe(struct snd_soc_codec *codec) int ret; wm2200->codec = codec; - codec->dapm.bias_level = SND_SOC_BIAS_OFF; ret = snd_soc_add_codec_controls(codec, wm_adsp1_fw_controls, 2); if (ret != 0) @@ -1942,6 +1941,7 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct wm2200_priv *wm2200 = snd_soc_codec_get_drvdata(codec); struct _fll_div factors; int ret, i, timeout; + unsigned long time_left; if (!Fout) { dev_dbg(codec->dev, "FLL disabled"); @@ -2021,9 +2021,10 @@ static int wm2200_set_fll(struct snd_soc_codec *codec, int fll_id, int source, /* Poll for the lock; will use the interrupt to exit quickly */ for (i = 0; i < timeout; i++) { if (i2c->irq) { - ret = wait_for_completion_timeout(&wm2200->fll_lock, - msecs_to_jiffies(25)); - if (ret > 0) + time_left = wait_for_completion_timeout( + &wm2200->fll_lock, + msecs_to_jiffies(25)); + if (time_left > 0) break; } else { msleep(1); diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index ea09db585aa1..96740379b711 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -1762,6 +1762,7 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct _fll_div factors; struct wm5100_fll *fll; int ret, base, lock, i, timeout; + unsigned long time_left; switch (fll_id) { case WM5100_FLL1: @@ -1842,9 +1843,9 @@ static int wm5100_set_fll(struct snd_soc_codec *codec, int fll_id, int source, /* Poll for the lock; will use interrupt when we can test */ for (i = 0; i < timeout; i++) { if (i2c->irq) { - ret = wait_for_completion_timeout(&fll->lock, - msecs_to_jiffies(25)); - if (ret > 0) + time_left = wait_for_completion_timeout(&fll->lock, + msecs_to_jiffies(25)); + if (time_left > 0) break; } else { msleep(1); diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 6d0fe0ac95a3..0c6d1bc0526e 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -1861,7 +1861,6 @@ static unsigned int wm5102_digital_vu[] = { ARIZONA_DAC_DIGITAL_VOLUME_2L, ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_DAC_DIGITAL_VOLUME_3L, - ARIZONA_DAC_DIGITAL_VOLUME_3R, ARIZONA_DAC_DIGITAL_VOLUME_4L, ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_DAC_DIGITAL_VOLUME_5L, diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index c81a9eab3e3e..c65e5a75fc1a 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -69,14 +69,14 @@ struct wm8350_data { struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; int fll_freq_out; int fll_freq_in; + struct delayed_work pga_work; }; /* * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. */ -static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) +static inline int wm8350_out1_ramp_step(struct wm8350_data *wm8350_data) { - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1; struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; @@ -140,9 +140,8 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) /* * Ramp OUT2 PGA volume to minimise pops at stream startup and shutdown. */ -static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) +static inline int wm8350_out2_ramp_step(struct wm8350_data *wm8350_data) { - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out2 = &wm8350_data->out2; struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; @@ -210,10 +209,8 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) */ static void wm8350_pga_work(struct work_struct *work) { - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350_data *wm8350_data = + container_of(work, struct wm8350_data, pga_work.work); struct wm8350_output *out1 = &wm8350_data->out1, *out2 = &wm8350_data->out2; int i, out1_complete, out2_complete; @@ -226,9 +223,9 @@ static void wm8350_pga_work(struct work_struct *work) for (i = 0; i <= 63; i++) { out1_complete = 1, out2_complete = 1; if (out1->ramp != WM8350_RAMP_NONE) - out1_complete = wm8350_out1_ramp_step(codec); + out1_complete = wm8350_out1_ramp_step(wm8350_data); if (out2->ramp != WM8350_RAMP_NONE) - out2_complete = wm8350_out2_ramp_step(codec); + out2_complete = wm8350_out2_ramp_step(wm8350_data); /* ramp finished ? */ if (out1_complete && out2_complete) @@ -283,7 +280,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_UP; out->active = 1; - schedule_delayed_work(&codec->dapm.delayed_work, + schedule_delayed_work(&wm8350_data->pga_work, msecs_to_jiffies(1)); break; @@ -291,7 +288,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_DOWN; out->active = 0; - schedule_delayed_work(&codec->dapm.delayed_work, + schedule_delayed_work(&wm8350_data->pga_work, msecs_to_jiffies(1)); break; } @@ -1492,7 +1489,7 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&priv->pga_work, wm8350_pga_work); INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work); INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work); @@ -1578,7 +1575,7 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) /* if there was any work waiting then we run it now and * wait for its completion */ - flush_delayed_work(&codec->dapm.delayed_work); + flush_delayed_work(&priv->pga_work); wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index c6d10533e2bd..2245b6a32f3d 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -25,6 +25,7 @@ #include <linux/spi/spi.h> #include <linux/of_device.h> #include <linux/mutex.h> +#include <linux/clk.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -45,6 +46,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = { /* codec private data */ struct wm8731_priv { struct regmap *regmap; + struct clk *mclk; struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES]; const struct snd_pcm_hw_constraint_list *constraints; unsigned int sysclk; @@ -390,6 +392,8 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai, switch (clk_id) { case WM8731_SYSCLK_XTAL: case WM8731_SYSCLK_MCLK: + if (wm8731->mclk && clk_set_rate(wm8731->mclk, freq)) + return -EINVAL; wm8731->sysclk_type = clk_id; break; default: @@ -491,6 +495,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, switch (level) { case SND_SOC_BIAS_ON: + if (wm8731->mclk) + clk_prepare_enable(wm8731->mclk); break; case SND_SOC_BIAS_PREPARE: break; @@ -509,6 +515,8 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8731_PWR, reg | 0x0040); break; case SND_SOC_BIAS_OFF: + if (wm8731->mclk) + clk_disable_unprepare(wm8731->mclk); snd_soc_write(codec, WM8731_PWR, 0xffff); regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies); @@ -667,6 +675,19 @@ static int wm8731_spi_probe(struct spi_device *spi) if (wm8731 == NULL) return -ENOMEM; + wm8731->mclk = devm_clk_get(&spi->dev, "mclk"); + if (IS_ERR(wm8731->mclk)) { + ret = PTR_ERR(wm8731->mclk); + if (ret == -ENOENT) { + wm8731->mclk = NULL; + dev_warn(&spi->dev, "Assuming static MCLK\n"); + } else { + dev_err(&spi->dev, "Failed to get MCLK: %d\n", + ret); + return ret; + } + } + mutex_init(&wm8731->lock); wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap); @@ -718,6 +739,19 @@ static int wm8731_i2c_probe(struct i2c_client *i2c, if (wm8731 == NULL) return -ENOMEM; + wm8731->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(wm8731->mclk)) { + ret = PTR_ERR(wm8731->mclk); + if (ret == -ENOENT) { + wm8731->mclk = NULL; + dev_warn(&i2c->dev, "Assuming static MCLK\n"); + } else { + dev_err(&i2c->dev, "Failed to get MCLK: %d\n", + ret); + return ret; + } + } + mutex_init(&wm8731->lock); wm8731->regmap = devm_regmap_init_i2c(i2c, &wm8731_regmap); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 31bb4801a005..9e71c768966f 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -123,7 +123,7 @@ static struct { }; static const unsigned int rates_11289[] = { - 44100, 88235, + 44100, 88200, }; static const struct snd_pcm_hw_constraint_list constraints_11289 = { @@ -150,7 +150,7 @@ static const struct snd_pcm_hw_constraint_list constraints_16384 = { }; static const unsigned int rates_16934[] = { - 44100, 88235, + 44100, 88200, }; static const struct snd_pcm_hw_constraint_list constraints_16934 = { @@ -168,7 +168,7 @@ static const struct snd_pcm_hw_constraint_list constraints_18432 = { }; static const unsigned int rates_22579[] = { - 44100, 88235, 1764000 + 44100, 88200, 176400 }; static const struct snd_pcm_hw_constraint_list constraints_22579 = { @@ -186,7 +186,7 @@ static const struct snd_pcm_hw_constraint_list constraints_24576 = { }; static const unsigned int rates_36864[] = { - 48000, 96000, 19200 + 48000, 96000, 192000 }; static const struct snd_pcm_hw_constraint_list constraints_36864 = { diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index 21ca3a94fc96..c50a5959345f 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -153,6 +153,7 @@ struct wm8753_priv { unsigned int hifi_fmt; int dai_func; + struct delayed_work charge_work; }; #define wm8753_reset(c) snd_soc_write(c, WM8753_RESET, 0) @@ -1326,9 +1327,19 @@ static int wm8753_mute(struct snd_soc_dai *dai, int mute) return 0; } +static void wm8753_charge_work(struct work_struct *work) +{ + struct wm8753_priv *wm8753 = + container_of(work, struct wm8753_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8753->regmap, WM8753_PWR1, 0x0180, 0x0100); +} + static int wm8753_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); u16 pwr_reg = snd_soc_read(codec, WM8753_PWR1) & 0xfe3e; switch (level) { @@ -1337,14 +1348,22 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); break; case SND_SOC_BIAS_PREPARE: - /* set vmid to 5k for quick power up */ - snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); + /* Wait until fully charged */ + flush_delayed_work(&wm8753->charge_work); break; case SND_SOC_BIAS_STANDBY: - /* mute dac and set vmid to 500k, enable VREF */ - snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141); + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + /* set vmid to 5k for quick power up */ + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); + schedule_delayed_work(&wm8753->charge_work, + msecs_to_jiffies(caps_charge)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_write(codec, WM8753_PWR1, pwr_reg | 0x0141); + } break; case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8753->charge_work); snd_soc_write(codec, WM8753_PWR1, 0x0001); break; } @@ -1428,38 +1447,12 @@ static struct snd_soc_dai_driver wm8753_dai[] = { }, }; -static void wm8753_work(struct work_struct *work) -{ - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, - delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - wm8753_set_bias_level(codec, dapm->bias_level); -} - -static int wm8753_suspend(struct snd_soc_codec *codec) -{ - wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - static int wm8753_resume(struct snd_soc_codec *codec) { struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); regcache_sync(wm8753->regmap); - wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge wm8753 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - queue_delayed_work(system_power_efficient_wq, - &codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); - } - return 0; } @@ -1468,7 +1461,7 @@ static int wm8753_probe(struct snd_soc_codec *codec) struct wm8753_priv *wm8753 = snd_soc_codec_get_drvdata(codec); int ret; - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8753_work); + INIT_DELAYED_WORK(&wm8753->charge_work, wm8753_charge_work); ret = wm8753_reset(codec); if (ret < 0) { @@ -1476,14 +1469,8 @@ static int wm8753_probe(struct snd_soc_codec *codec) return ret; } - wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm8753->dai_func = 0; - /* charge output caps */ - wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); - schedule_delayed_work(&codec->dapm.delayed_work, - msecs_to_jiffies(caps_charge)); - /* set the update bits */ snd_soc_update_bits(codec, WM8753_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8753_RDAC, 0x0100, 0x0100); @@ -1499,21 +1486,11 @@ static int wm8753_probe(struct snd_soc_codec *codec) return 0; } -/* power down chip */ -static int wm8753_remove(struct snd_soc_codec *codec) -{ - flush_delayed_work(&codec->dapm.delayed_work); - wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); - - return 0; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8753 = { .probe = wm8753_probe, - .remove = wm8753_remove, - .suspend = wm8753_suspend, .resume = wm8753_resume, .set_bias_level = wm8753_set_bias_level, + .suspend_bias_off = true, .controls = wm8753_snd_controls, .num_controls = ARRAY_SIZE(wm8753_snd_controls), diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c new file mode 100644 index 000000000000..6596f5f3a0c3 --- /dev/null +++ b/sound/soc/codecs/wm8804-i2c.c @@ -0,0 +1,65 @@ +/* + * wm8804-i2c.c -- WM8804 S/PDIF transceiver driver - I2C + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> + +#include "wm8804.h" + +static int wm8804_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&i2c->dev, regmap); +} + +static int wm8804_i2c_remove(struct i2c_client *i2c) +{ + wm8804_remove(&i2c->dev); + return 0; +} + +static const struct i2c_device_id wm8804_i2c_id[] = { + { "wm8804", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct i2c_driver wm8804_i2c_driver = { + .driver = { + .name = "wm8804", + .owner = THIS_MODULE, + .pm = &wm8804_pm, + .of_match_table = wm8804_of_match, + }, + .probe = wm8804_i2c_probe, + .remove = wm8804_i2c_remove, + .id_table = wm8804_i2c_id +}; + +module_i2c_driver(wm8804_i2c_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - I2C"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804-spi.c b/sound/soc/codecs/wm8804-spi.c new file mode 100644 index 000000000000..407a3cf391e5 --- /dev/null +++ b/sound/soc/codecs/wm8804-spi.c @@ -0,0 +1,57 @@ +/* + * wm8804-spi.c -- WM8804 S/PDIF transceiver driver - SPI + * + * Copyright 2015 Cirrus Logic Inc + * + * Author: Charles Keepax <ckeepax@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include "wm8804.h" + +static int wm8804_spi_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return wm8804_probe(&spi->dev, regmap); +} + +static int wm8804_spi_remove(struct spi_device *spi) +{ + wm8804_remove(&spi->dev); + return 0; +} + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct spi_driver wm8804_spi_driver = { + .driver = { + .name = "wm8804", + .owner = THIS_MODULE, + .pm = &wm8804_pm, + .of_match_table = wm8804_of_match, + }, + .probe = wm8804_spi_probe, + .remove = wm8804_spi_remove +}; + +module_spi_driver(wm8804_spi_driver); + +MODULE_DESCRIPTION("ASoC WM8804 driver - SPI"); +MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index b2b0e68f707e..1e403f67cf16 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -13,12 +13,11 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/i2c.h> +#include <linux/pm_runtime.h> #include <linux/of_device.h> -#include <linux/spi/spi.h> -#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> @@ -27,6 +26,7 @@ #include <sound/soc.h> #include <sound/initval.h> #include <sound/tlv.h> +#include <sound/soc-dapm.h> #include "wm8804.h" @@ -60,18 +60,23 @@ static const struct reg_default wm8804_reg_defaults[] = { }; struct wm8804_priv { + struct device *dev; struct regmap *regmap; struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; int mclk_div; -}; -static int txsrc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); + struct gpio_desc *reset; + + int aif_pwr; +}; static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +static int wm8804_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + /* * We can't use the same notifier block for more than one supply and * there's no way I can see to get from a callback to the caller @@ -93,26 +98,62 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); +static const SOC_ENUM_SINGLE_DECL(txsrc, WM8804_SPDTX4, 6, txsrc_text); -static const struct snd_kcontrol_new wm8804_snd_controls[] = { - SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), - SOC_SINGLE("TX Playback Switch", WM8804_PWRDN, 2, 1, 1), - SOC_SINGLE("AIF Playback Switch", WM8804_PWRDN, 4, 1, 1) +static const struct snd_kcontrol_new wm8804_tx_source_mux[] = { + SOC_DAPM_ENUM_EXT("Input Source", txsrc, + snd_soc_dapm_get_enum_double, txsrc_put), }; -static int txsrc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec; - unsigned int src; +static const struct snd_soc_dapm_widget wm8804_dapm_widgets[] = { +SND_SOC_DAPM_OUTPUT("SPDIF Out"), +SND_SOC_DAPM_INPUT("SPDIF In"), + +SND_SOC_DAPM_PGA("SPDIFTX", WM8804_PWRDN, 2, 1, NULL, 0), +SND_SOC_DAPM_PGA("SPDIFRX", WM8804_PWRDN, 1, 1, NULL, 0), + +SND_SOC_DAPM_MUX("Tx Source", SND_SOC_NOPM, 6, 0, wm8804_tx_source_mux), + +SND_SOC_DAPM_AIF_OUT_E("AIFTX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_AIF_IN_E("AIFRX", NULL, 0, SND_SOC_NOPM, 0, 0, wm8804_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route wm8804_dapm_routes[] = { + { "AIFRX", NULL, "Playback" }, + { "Tx Source", "AIF", "AIFRX" }, - codec = snd_soc_kcontrol_codec(kcontrol); - src = snd_soc_read(codec, WM8804_SPDTX4); - if (src & 0x40) - ucontrol->value.integer.value[0] = 1; - else - ucontrol->value.integer.value[0] = 0; + { "SPDIFRX", NULL, "SPDIF In" }, + { "Tx Source", "S/PDIF RX", "SPDIFRX" }, + + { "SPDIFTX", NULL, "Tx Source" }, + { "SPDIF Out", NULL, "SPDIFTX" }, + + { "AIFTX", NULL, "SPDIFRX" }, + { "Capture", NULL, "AIFTX" }, +}; + +static int wm8804_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* power up the aif */ + if (!wm8804->aif_pwr) + snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x0); + wm8804->aif_pwr++; + break; + case SND_SOC_DAPM_POST_PMD: + /* power down only both paths are disabled */ + wm8804->aif_pwr--; + if (!wm8804->aif_pwr) + snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0x10); + break; + } return 0; } @@ -120,48 +161,33 @@ static int txsrc_get(struct snd_kcontrol *kcontrol, static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec; - unsigned int src, txpwr; + struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol); + struct snd_soc_dapm_context *dapm = &codec->dapm; + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val = ucontrol->value.enumerated.item[0] << e->shift_l; + unsigned int mask = 1 << e->shift_l; + unsigned int txpwr; + + if (val != 0 && val != mask) + return -EINVAL; - codec = snd_soc_kcontrol_codec(kcontrol); + snd_soc_dapm_mutex_lock(dapm); - if (ucontrol->value.integer.value[0] != 0 - && ucontrol->value.integer.value[0] != 1) - return -EINVAL; + if (snd_soc_test_bits(codec, e->reg, mask, val)) { + /* save the current power state of the transmitter */ + txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; - src = snd_soc_read(codec, WM8804_SPDTX4); - switch ((src & 0x40) >> 6) { - case 0: - if (!ucontrol->value.integer.value[0]) - return 0; - break; - case 1: - if (ucontrol->value.integer.value[1]) - return 0; - break; - } + /* power down the transmitter */ + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); - /* save the current power state of the transmitter */ - txpwr = snd_soc_read(codec, WM8804_PWRDN) & 0x4; - /* power down the transmitter */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, 0x4); - /* set the tx source */ - snd_soc_update_bits(codec, WM8804_SPDTX4, 0x40, - ucontrol->value.integer.value[0] << 6); - - if (ucontrol->value.integer.value[0]) { - /* power down the receiver */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0x2); - /* power up the AIF */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x10, 0); - } else { - /* don't power down the AIF -- may be used as an output */ - /* power up the receiver */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x2, 0); + /* set the tx source */ + snd_soc_update_bits(codec, e->reg, mask, val); + + /* restore the transmitter's configuration */ + snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); } - /* restore the transmitter's configuration */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x4, txpwr); + snd_soc_dapm_mutex_unlock(dapm); return 0; } @@ -185,9 +211,9 @@ static bool wm8804_volatile(struct device *dev, unsigned int reg) } } -static int wm8804_reset(struct snd_soc_codec *codec) +static int wm8804_soft_reset(struct wm8804_priv *wm8804) { - return snd_soc_write(codec, WM8804_RST_DEVID1, 0x0); + return regmap_write(wm8804->regmap, WM8804_RST_DEVID1, 0x0); } static int wm8804_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) @@ -379,19 +405,19 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec; + struct snd_soc_codec *codec = dai->codec; + struct wm8804_priv *wm8804 = snd_soc_codec_get_drvdata(codec); + bool change; - codec = dai->codec; if (!freq_in || !freq_out) { /* disable the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); - return 0; + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (change) + pm_runtime_put(wm8804->dev); } else { int ret; struct pll_div pll_div; - struct wm8804_priv *wm8804; - - wm8804 = snd_soc_codec_get_drvdata(codec); ret = pll_factors(&pll_div, freq_out, freq_in, wm8804->mclk_div); @@ -399,7 +425,10 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, return ret; /* power down the PLL before reprogramming it */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); + regmap_update_bits_check(wm8804->regmap, WM8804_PWRDN, + 0x1, 0x1, &change); + if (!change) + pm_runtime_get_sync(wm8804->dev); /* set PLLN and PRESCALE */ snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, @@ -477,141 +506,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, return 0; } -static int wm8804_set_bias_level(struct snd_soc_codec *codec, - enum snd_soc_bias_level level) -{ - int ret; - struct wm8804_priv *wm8804; - - wm8804 = snd_soc_codec_get_drvdata(codec); - switch (level) { - case SND_SOC_BIAS_ON: - break; - case SND_SOC_BIAS_PREPARE: - /* power up the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0); - break; - case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, - "Failed to enable supplies: %d\n", - ret); - return ret; - } - regcache_sync(wm8804->regmap); - } - /* power down the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); - break; - case SND_SOC_BIAS_OFF: - /* power down the OSC and the PLL */ - snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); - regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - break; - } - - codec->dapm.bias_level = level; - return 0; -} - -static int wm8804_remove(struct snd_soc_codec *codec) -{ - struct wm8804_priv *wm8804; - int i; - - wm8804 = snd_soc_codec_get_drvdata(codec); - - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) - regulator_unregister_notifier(wm8804->supplies[i].consumer, - &wm8804->disable_nb[i]); - return 0; -} - -static int wm8804_probe(struct snd_soc_codec *codec) -{ - struct wm8804_priv *wm8804; - int i, id1, id2, ret; - - wm8804 = snd_soc_codec_get_drvdata(codec); - - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) - wm8804->supplies[i].supply = wm8804_supply_names[i]; - - ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, "Failed to request supplies: %d\n", ret); - return ret; - } - - wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; - wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; - - /* This should really be moved into the regulator core */ - for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { - ret = regulator_register_notifier(wm8804->supplies[i].consumer, - &wm8804->disable_nb[i]); - if (ret != 0) { - dev_err(codec->dev, - "Failed to register regulator notifier: %d\n", - ret); - } - } - - ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), - wm8804->supplies); - if (ret) { - dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); - return ret; - } - - id1 = snd_soc_read(codec, WM8804_RST_DEVID1); - if (id1 < 0) { - dev_err(codec->dev, "Failed to read device ID: %d\n", id1); - ret = id1; - goto err_reg_enable; - } - - id2 = snd_soc_read(codec, WM8804_DEVID2); - if (id2 < 0) { - dev_err(codec->dev, "Failed to read device ID: %d\n", id2); - ret = id2; - goto err_reg_enable; - } - - id2 = (id2 << 8) | id1; - - if (id2 != 0x8805) { - dev_err(codec->dev, "Invalid device ID: %#x\n", id2); - ret = -EINVAL; - goto err_reg_enable; - } - - ret = snd_soc_read(codec, WM8804_DEVREV); - if (ret < 0) { - dev_err(codec->dev, "Failed to read device revision: %d\n", - ret); - goto err_reg_enable; - } - dev_info(codec->dev, "revision %c\n", ret + 'A'); - - ret = wm8804_reset(codec); - if (ret < 0) { - dev_err(codec->dev, "Failed to issue reset: %d\n", ret); - goto err_reg_enable; - } - - return 0; - -err_reg_enable: - regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); - return ret; -} - static const struct snd_soc_dai_ops wm8804_dai_ops = { .hw_params = wm8804_hw_params, .set_fmt = wm8804_set_fmt, @@ -649,22 +543,15 @@ static struct snd_soc_dai_driver wm8804_dai = { }; static const struct snd_soc_codec_driver soc_codec_dev_wm8804 = { - .probe = wm8804_probe, - .remove = wm8804_remove, - .set_bias_level = wm8804_set_bias_level, .idle_bias_off = true, - .controls = wm8804_snd_controls, - .num_controls = ARRAY_SIZE(wm8804_snd_controls), -}; - -static const struct of_device_id wm8804_of_match[] = { - { .compatible = "wlf,wm8804", }, - { } + .dapm_widgets = wm8804_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets), + .dapm_routes = wm8804_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes), }; -MODULE_DEVICE_TABLE(of, wm8804_of_match); -static const struct regmap_config wm8804_regmap_config = { +const struct regmap_config wm8804_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -675,128 +562,169 @@ static const struct regmap_config wm8804_regmap_config = { .reg_defaults = wm8804_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), }; +EXPORT_SYMBOL_GPL(wm8804_regmap_config); -#if defined(CONFIG_SPI_MASTER) -static int wm8804_spi_probe(struct spi_device *spi) +int wm8804_probe(struct device *dev, struct regmap *regmap) { struct wm8804_priv *wm8804; - int ret; + unsigned int id1, id2; + int i, ret; - wm8804 = devm_kzalloc(&spi->dev, sizeof *wm8804, GFP_KERNEL); + wm8804 = devm_kzalloc(dev, sizeof(*wm8804), GFP_KERNEL); if (!wm8804) return -ENOMEM; - wm8804->regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); - if (IS_ERR(wm8804->regmap)) { - ret = PTR_ERR(wm8804->regmap); + dev_set_drvdata(dev, wm8804); + + wm8804->dev = dev; + wm8804->regmap = regmap; + + wm8804->reset = devm_gpiod_get_optional(dev, "wlf,reset", + GPIOD_OUT_LOW); + if (IS_ERR(wm8804->reset)) { + ret = PTR_ERR(wm8804->reset); + dev_err(dev, "Failed to get reset line: %d\n", ret); return ret; } - spi_set_drvdata(spi, wm8804); + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) + wm8804->supplies[i].supply = wm8804_supply_names[i]; - ret = snd_soc_register_codec(&spi->dev, - &soc_codec_dev_wm8804, &wm8804_dai, 1); + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to request supplies: %d\n", ret); + return ret; + } - return ret; -} + wm8804->disable_nb[0].notifier_call = wm8804_regulator_event_0; + wm8804->disable_nb[1].notifier_call = wm8804_regulator_event_1; -static int wm8804_spi_remove(struct spi_device *spi) -{ - snd_soc_unregister_codec(&spi->dev); - return 0; -} + /* This should really be moved into the regulator core */ + for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) { + struct regulator *regulator = wm8804->supplies[i].consumer; -static struct spi_driver wm8804_spi_driver = { - .driver = { - .name = "wm8804", - .owner = THIS_MODULE, - .of_match_table = wm8804_of_match, - }, - .probe = wm8804_spi_probe, - .remove = wm8804_spi_remove -}; -#endif + ret = devm_regulator_register_notifier(regulator, + &wm8804->disable_nb[i]); + if (ret != 0) { + dev_err(dev, + "Failed to register regulator notifier: %d\n", + ret); + return ret; + } + } -#if IS_ENABLED(CONFIG_I2C) -static int wm8804_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct wm8804_priv *wm8804; - int ret; + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + if (ret) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } - wm8804 = devm_kzalloc(&i2c->dev, sizeof *wm8804, GFP_KERNEL); - if (!wm8804) - return -ENOMEM; + if (wm8804->reset) + gpiod_set_value_cansleep(wm8804->reset, 1); - wm8804->regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); - if (IS_ERR(wm8804->regmap)) { - ret = PTR_ERR(wm8804->regmap); - return ret; + ret = regmap_read(regmap, WM8804_RST_DEVID1, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVID2, &id2); + if (ret < 0) { + dev_err(dev, "Failed to read device ID: %d\n", ret); + goto err_reg_enable; + } + + id2 = (id2 << 8) | id1; + + if (id2 != 0x8805) { + dev_err(dev, "Invalid device ID: %#x\n", id2); + ret = -EINVAL; + goto err_reg_enable; + } + + ret = regmap_read(regmap, WM8804_DEVREV, &id1); + if (ret < 0) { + dev_err(dev, "Failed to read device revision: %d\n", + ret); + goto err_reg_enable; } + dev_info(dev, "revision %c\n", id1 + 'A'); - i2c_set_clientdata(i2c, wm8804); + if (!wm8804->reset) { + ret = wm8804_soft_reset(wm8804); + if (ret < 0) { + dev_err(dev, "Failed to issue reset: %d\n", ret); + goto err_reg_enable; + } + } + + ret = snd_soc_register_codec(dev, &soc_codec_dev_wm8804, + &wm8804_dai, 1); + if (ret < 0) { + dev_err(dev, "Failed to register CODEC: %d\n", ret); + goto err_reg_enable; + } + + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_wm8804, &wm8804_dai, 1); + return 0; + +err_reg_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); return ret; } +EXPORT_SYMBOL_GPL(wm8804_probe); -static int wm8804_i2c_remove(struct i2c_client *i2c) +void wm8804_remove(struct device *dev) { - snd_soc_unregister_codec(&i2c->dev); - return 0; + pm_runtime_disable(dev); + snd_soc_unregister_codec(dev); } +EXPORT_SYMBOL_GPL(wm8804_remove); -static const struct i2c_device_id wm8804_i2c_id[] = { - { "wm8804", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); - -static struct i2c_driver wm8804_i2c_driver = { - .driver = { - .name = "wm8804", - .owner = THIS_MODULE, - .of_match_table = wm8804_of_match, - }, - .probe = wm8804_i2c_probe, - .remove = wm8804_i2c_remove, - .id_table = wm8804_i2c_id -}; -#endif - -static int __init wm8804_modinit(void) +#if IS_ENABLED(CONFIG_PM) +static int wm8804_runtime_resume(struct device *dev) { - int ret = 0; + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + int ret; -#if IS_ENABLED(CONFIG_I2C) - ret = i2c_add_driver(&wm8804_i2c_driver); + ret = regulator_bulk_enable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); if (ret) { - printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", - ret); - } -#endif -#if defined(CONFIG_SPI_MASTER) - ret = spi_register_driver(&wm8804_spi_driver); - if (ret != 0) { - printk(KERN_ERR "Failed to register wm8804 SPI driver: %d\n", - ret); + dev_err(wm8804->dev, "Failed to enable supplies: %d\n", ret); + return ret; } -#endif - return ret; + + regcache_sync(wm8804->regmap); + + /* Power up OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x0); + + return 0; } -module_init(wm8804_modinit); -static void __exit wm8804_exit(void) +static int wm8804_runtime_suspend(struct device *dev) { -#if IS_ENABLED(CONFIG_I2C) - i2c_del_driver(&wm8804_i2c_driver); -#endif -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&wm8804_spi_driver); -#endif + struct wm8804_priv *wm8804 = dev_get_drvdata(dev); + + /* Power down OSCCLK */ + regmap_update_bits(wm8804->regmap, WM8804_PWRDN, 0x8, 0x8); + + regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), + wm8804->supplies); + + return 0; } -module_exit(wm8804_exit); +#endif + +const struct dev_pm_ops wm8804_pm = { + SET_RUNTIME_PM_OPS(wm8804_runtime_suspend, wm8804_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(wm8804_pm); MODULE_DESCRIPTION("ASoC WM8804 driver"); MODULE_AUTHOR("Dimitris Papastamos <dp@opensource.wolfsonmicro.com>"); diff --git a/sound/soc/codecs/wm8804.h b/sound/soc/codecs/wm8804.h index e72d4f4ba6b1..aa72fa66c932 100644 --- a/sound/soc/codecs/wm8804.h +++ b/sound/soc/codecs/wm8804.h @@ -13,6 +13,8 @@ #ifndef _WM8804_H #define _WM8804_H +#include <linux/regmap.h> + /* * Register values. */ @@ -62,4 +64,10 @@ #define WM8804_MCLKDIV_256FS 0 #define WM8804_MCLKDIV_128FS 1 +extern const struct regmap_config wm8804_regmap_config; +extern const struct dev_pm_ops wm8804_pm; + +int wm8804_probe(struct device *dev, struct regmap *regmap); +void wm8804_remove(struct device *dev); + #endif /* _WM8804_H */ diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 39ddb9b8834c..f9cbabdc6238 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -31,11 +31,11 @@ #define WM8971_REG_COUNT 43 -static struct workqueue_struct *wm8971_workq = NULL; - /* codec private data */ struct wm8971_priv { unsigned int sysclk; + struct delayed_work charge_work; + struct regmap *regmap; }; /* @@ -552,9 +552,19 @@ static int wm8971_mute(struct snd_soc_dai *dai, int mute) return 0; } +static void wm8971_charge_work(struct work_struct *work) +{ + struct wm8971_priv *wm8971 = + container_of(work, struct wm8971_priv, charge_work.work); + + /* Set to 500k */ + regmap_update_bits(wm8971->regmap, WM8971_PWR1, 0x0180, 0x0100); +} + static int wm8971_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); u16 pwr_reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; switch (level) { @@ -563,15 +573,24 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec, snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x00c1); break; case SND_SOC_BIAS_PREPARE: + /* Wait until fully charged */ + flush_delayed_work(&wm8971->charge_work); break; case SND_SOC_BIAS_STANDBY: - if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { snd_soc_cache_sync(codec); + /* charge output caps - set vmid to 5k for quick power up */ + snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x01c0); + queue_delayed_work(system_power_efficient_wq, + &wm8971->charge_work, msecs_to_jiffies(1000)); + } else { + /* mute dac and set vmid to 500k, enable VREF */ + snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); + } - /* mute dac and set vmid to 500k, enable VREF */ - snd_soc_write(codec, WM8971_PWR1, pwr_reg | 0x0140); break; case SND_SOC_BIAS_OFF: + cancel_delayed_work_sync(&wm8971->charge_work); snd_soc_write(codec, WM8971_PWR1, 0x0001); break; } @@ -610,58 +629,14 @@ static struct snd_soc_dai_driver wm8971_dai = { .ops = &wm8971_dai_ops, }; -static void wm8971_work(struct work_struct *work) -{ - struct snd_soc_dapm_context *dapm = - container_of(work, struct snd_soc_dapm_context, - delayed_work.work); - struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); - wm8971_set_bias_level(codec, codec->dapm.bias_level); -} - -static int wm8971_suspend(struct snd_soc_codec *codec) -{ - wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); - return 0; -} - -static int wm8971_resume(struct snd_soc_codec *codec) -{ - u16 reg; - - wm8971_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* charge wm8971 caps */ - if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) { - reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; - snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->dapm.bias_level = SND_SOC_BIAS_ON; - queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, - msecs_to_jiffies(1000)); - } - - return 0; -} - static int wm8971_probe(struct snd_soc_codec *codec) { - int ret = 0; - u16 reg; + struct wm8971_priv *wm8971 = snd_soc_codec_get_drvdata(codec); - INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8971_work); - wm8971_workq = create_workqueue("wm8971"); - if (wm8971_workq == NULL) - return -ENOMEM; + INIT_DELAYED_WORK(&wm8971->charge_work, wm8971_charge_work); wm8971_reset(codec); - /* charge output caps - set vmid to 5k for quick power up */ - reg = snd_soc_read(codec, WM8971_PWR1) & 0xfe3e; - snd_soc_write(codec, WM8971_PWR1, reg | 0x01c0); - codec->dapm.bias_level = SND_SOC_BIAS_STANDBY; - queue_delayed_work(wm8971_workq, &codec->dapm.delayed_work, - msecs_to_jiffies(1000)); - /* set the update bits */ snd_soc_update_bits(codec, WM8971_LDAC, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8971_RDAC, 0x0100, 0x0100); @@ -672,26 +647,13 @@ static int wm8971_probe(struct snd_soc_codec *codec) snd_soc_update_bits(codec, WM8971_LINVOL, 0x0100, 0x0100); snd_soc_update_bits(codec, WM8971_RINVOL, 0x0100, 0x0100); - return ret; -} - - -/* power down chip */ -static int wm8971_remove(struct snd_soc_codec *codec) -{ - wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF); - - if (wm8971_workq) - destroy_workqueue(wm8971_workq); return 0; } static struct snd_soc_codec_driver soc_codec_dev_wm8971 = { .probe = wm8971_probe, - .remove = wm8971_remove, - .suspend = wm8971_suspend, - .resume = wm8971_resume, .set_bias_level = wm8971_set_bias_level, + .suspend_bias_off = true, .controls = wm8971_snd_controls, .num_controls = ARRAY_SIZE(wm8971_snd_controls), @@ -715,7 +677,6 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8971_priv *wm8971; - struct regmap *regmap; int ret; wm8971 = devm_kzalloc(&i2c->dev, sizeof(struct wm8971_priv), @@ -723,9 +684,9 @@ static int wm8971_i2c_probe(struct i2c_client *i2c, if (wm8971 == NULL) return -ENOMEM; - regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); + wm8971->regmap = devm_regmap_init_i2c(i2c, &wm8971_regmap); + if (IS_ERR(wm8971->regmap)) + return PTR_ERR(wm8971->regmap); i2c_set_clientdata(i2c, wm8971); diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index dc92d5e4e942..308748a022c5 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -2009,7 +2009,7 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); struct i2c_client *i2c = to_i2c_client(codec->dev); struct _fll_div fll_div; - unsigned long timeout; + unsigned long timeout, time_left; int ret, reg, retry; /* Any change? */ @@ -2110,13 +2110,15 @@ static int wm8996_set_fll(struct snd_soc_codec *codec, int fll_id, int source, if (i2c->irq) timeout *= 10; else - timeout /= 2; + /* ensure timeout of atleast 1 jiffies */ + timeout = timeout/2 ? : 1; for (retry = 0; retry < 10; retry++) { - ret = wait_for_completion_timeout(&wm8996->fll_lock, - timeout); - if (ret != 0) { + time_left = wait_for_completion_timeout(&wm8996->fll_lock, + timeout); + if (time_left != 0) { WARN_ON(!i2c->irq); + ret = 1; break; } diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index ff67b334065b..d01c2095452f 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -420,10 +420,9 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol, memcpy(ctl->cache, p, ctl->len); - if (!ctl->enabled) { - ctl->set = 1; + ctl->set = 1; + if (!ctl->enabled) return 0; - } return wm_coeff_write_control(kcontrol, p, ctl->len); } @@ -1185,7 +1184,6 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) int ret, pos, blocks, type, offset, reg; char *file; struct wm_adsp_buf *buf; - int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) @@ -1335,12 +1333,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) } } - tmp = le32_to_cpu(blk->len) % 4; - if (tmp) - pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); - else - pos += le32_to_cpu(blk->len) + sizeof(*blk); - + pos += (le32_to_cpu(blk->len) + sizeof(*blk) + 3) & ~0x03; blocks++; } diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 2b81ca418d2a..3736d9aabc56 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -1,14 +1,16 @@ config SND_DAVINCI_SOC - tristate "SoC Audio for TI DAVINCI" + tristate depends on ARCH_DAVINCI + select SND_EDMA_SOC config SND_EDMA_SOC - tristate "SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)" - depends on SOC_AM33XX || SOC_AM43XX + tristate "SoC Audio for Texas Instruments chips using eDMA" + depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI select SND_SOC_GENERIC_DMAENGINE_PCM help Say Y or M here if you want audio support for TI SoC which uses eDMA. The following line of SoCs are supported by this platform driver: + - daVinci devices - AM335x - AM437x/AM438x @@ -17,7 +19,7 @@ config SND_DAVINCI_SOC_I2S config SND_DAVINCI_SOC_MCASP tristate "Multichannel Audio Serial Port (McASP) support" - depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC + depends on SND_OMAP_SOC || SND_EDMA_SOC help Say Y or M here if you want to have support for McASP IP found in various Texas Instruments SoCs like: @@ -45,7 +47,7 @@ config SND_AM33XX_SOC_EVM config SND_DAVINCI_SOC_EVM tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" - depends on SND_DAVINCI_SOC && I2C + depends on SND_EDMA_SOC && I2C depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM select SND_DAVINCI_SOC_GENERIC_EVM help @@ -73,7 +75,7 @@ endchoice config SND_DM6467_SOC_EVM tristate "SoC Audio support for DaVinci DM6467 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DM6467_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM select SND_SOC_SPDIF @@ -82,7 +84,7 @@ config SND_DM6467_SOC_EVM config SND_DA830_SOC_EVM tristate "SoC Audio support for DA830/OMAP-L137 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA830_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM help @@ -91,7 +93,7 @@ config SND_DA830_SOC_EVM config SND_DA850_SOC_EVM tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM && I2C + depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C select SND_DAVINCI_SOC_GENERIC_EVM help Say Y if you want to add support for SoC audio on TI diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile index 09bf2ba92d38..f883933c1a19 100644 --- a/sound/soc/davinci/Makefile +++ b/sound/soc/davinci/Makefile @@ -1,11 +1,9 @@ # DAVINCI Platform Support -snd-soc-davinci-objs := davinci-pcm.o snd-soc-edma-objs := edma-pcm.o snd-soc-davinci-i2s-objs := davinci-i2s.o snd-soc-davinci-mcasp-objs:= davinci-mcasp.o snd-soc-davinci-vcif-objs:= davinci-vcif.o -obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index b6bb5947a8a8..731fb0d86c6a 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -117,7 +117,6 @@ static const struct snd_soc_dapm_route audio_map[] = { static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; - struct snd_soc_codec *codec = rtd->codec; struct device_node *np = card->dev->of_node; int ret; @@ -136,9 +135,9 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) } /* not connected */ - snd_soc_dapm_nc_pin(&codec->dapm, "MONO_LOUT"); - snd_soc_dapm_nc_pin(&codec->dapm, "HPLCOM"); - snd_soc_dapm_nc_pin(&codec->dapm, "HPRCOM"); + snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT"); + snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM"); + snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM"); return 0; } @@ -425,18 +424,8 @@ static int davinci_evm_probe(struct platform_device *pdev) return ret; } -static int davinci_evm_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static struct platform_driver davinci_evm_driver = { .probe = davinci_evm_probe, - .remove = davinci_evm_remove, .driver = { .name = "davinci_evm", .pm = &snd_soc_pm_ops, diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 15fb28fc8e1b..56cb4d95637d 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -23,8 +23,9 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> -#include "davinci-pcm.h" +#include "edma-pcm.h" #include "davinci-i2s.h" @@ -122,7 +123,8 @@ static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { struct davinci_mcbsp_dev { struct device *dev; - struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; void __iomem *base; #define MOD_DSP_A 0 #define MOD_DSP_B 1 @@ -419,8 +421,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - struct davinci_pcm_dma_params *dma_params = - &dev->dma_params[substream->stream]; struct snd_interval *i = NULL; int mcbsp_word_length, master; unsigned int rcr, xcr, srgr, clk_div, freq, framesize; @@ -532,8 +532,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } } - dma_params->acnt = dma_params->data_type = data_type[fmt]; - dma_params->fifo_level = 0; mcbsp_word_length = asp_word_length[fmt]; switch (master) { @@ -600,15 +598,6 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -static int davinci_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); - return 0; -} - static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -620,7 +609,6 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .startup = davinci_i2s_startup, .shutdown = davinci_i2s_shutdown, .prepare = davinci_i2s_prepare, .trigger = davinci_i2s_trigger, @@ -630,7 +618,18 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { }; +static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + static struct snd_soc_dai_driver davinci_i2s_dai = { + .probe = davinci_i2s_dai_probe, .playback = { .channels_min = 2, .channels_max = 2, @@ -651,11 +650,9 @@ static const struct snd_soc_component_driver davinci_i2s_component = { static int davinci_i2s_probe(struct platform_device *pdev) { - struct snd_platform_data *pdata = pdev->dev.platform_data; struct davinci_mcbsp_dev *dev; struct resource *mem, *ioarea, *res; - enum dma_event_q asp_chan_q = EVENTQ_0; - enum dma_event_q ram_chan_q = EVENTQ_1; + int *dma; int ret; mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -676,22 +673,6 @@ static int davinci_i2s_probe(struct platform_device *pdev) GFP_KERNEL); if (!dev) return -ENOMEM; - if (pdata) { - dev->enable_channel_combine = pdata->enable_channel_combine; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = - pdata->sram_size_playback; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = - pdata->sram_size_capture; - dev->clk_input_pin = pdata->clk_input_pin; - dev->i2s_accurate_sck = pdata->i2s_accurate_sck; - asp_chan_q = pdata->asp_chan_q; - ram_chan_q = pdata->ram_chan_q; - } - - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].asp_chan_q = asp_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].ram_chan_q = ram_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].asp_chan_q = asp_chan_q; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].ram_chan_q = ram_chan_q; dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) @@ -705,10 +686,10 @@ static int davinci_i2s_probe(struct platform_device *pdev) goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = + dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = + dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ @@ -718,7 +699,9 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; + dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; + *dma = res->start; + dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = dma; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { @@ -726,9 +709,11 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_release_clk; } - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; - dev->dev = &pdev->dev; + dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; + *dma = res->start; + dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = dma; + dev->dev = &pdev->dev; dev_set_drvdata(&pdev->dev, dev); ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component, @@ -736,7 +721,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (ret != 0) goto err_release_clk; - ret = davinci_soc_platform_register(&pdev->dev); + ret = edma_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); goto err_unregister_component; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index de3b155a5011..bb4b78eada58 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -26,6 +26,8 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_device.h> +#include <linux/platform_data/davinci_asp.h> +#include <linux/math64.h> #include <sound/asoundef.h> #include <sound/core.h> @@ -36,7 +38,6 @@ #include <sound/dmaengine_pcm.h> #include <sound/omap-pcm.h> -#include "davinci-pcm.h" #include "edma-pcm.h" #include "davinci-mcasp.h" @@ -62,10 +63,15 @@ struct davinci_mcasp_context { u32 config_regs[ARRAY_SIZE(context_regs)]; u32 afifo_regs[2]; /* for read/write fifo control registers */ u32 *xrsr_regs; /* for serializer configuration */ + bool pm_state; +}; + +struct davinci_mcasp_ruledata { + struct davinci_mcasp *mcasp; + int serializers; }; struct davinci_mcasp { - struct davinci_pcm_dma_params dma_params[2]; struct snd_dmaengine_dai_dma_data dma_data[2]; void __iomem *base; u32 fifo_base; @@ -82,6 +88,7 @@ struct davinci_mcasp { u16 bclk_lrclk_ratio; int streams; u32 irq_request[2]; + int dma_request[2]; int sysclk_freq; bool bclk_master; @@ -98,6 +105,8 @@ struct davinci_mcasp { #ifdef CONFIG_PM_SLEEP struct davinci_mcasp_context context; #endif + + struct davinci_mcasp_ruledata ruledata[2]; }; static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, @@ -441,6 +450,18 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); mcasp->bclk_master = 1; break; + case SND_SOC_DAIFMT_CBS_CFM: + /* codec is clock slave and frame master */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + mcasp->bclk_master = 1; + break; case SND_SOC_DAIFMT_CBM_CFS: /* codec is clock master and frame slave */ mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); @@ -507,7 +528,7 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); } out: - pm_runtime_put_sync(mcasp->dev); + pm_runtime_put(mcasp->dev); return ret; } @@ -516,6 +537,7 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + pm_runtime_get_sync(mcasp->dev); switch (div_id) { case 0: /* MCLK divider */ mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, @@ -541,6 +563,7 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, return -EINVAL; } + pm_runtime_put(mcasp->dev); return 0; } @@ -555,6 +578,7 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + pm_runtime_get_sync(mcasp->dev); if (dir == SND_SOC_CLOCK_OUT) { mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); @@ -567,6 +591,7 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, mcasp->sysclk_freq = freq; + pm_runtime_put(mcasp->dev); return 0; } @@ -631,7 +656,6 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, int period_words, int channels) { - struct davinci_pcm_dma_params *dma_params = &mcasp->dma_params[stream]; struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream]; int i; u8 tx_ser = 0; @@ -699,10 +723,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, * For example if three serializers are enabled the DMA * need to transfer three words per DMA request. */ - dma_params->fifo_level = active_serializers; dma_data->maxburst = active_serializers; } else { - dma_params->fifo_level = 0; dma_data->maxburst = 0; } return 0; @@ -734,7 +756,6 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, /* Configure the burst size for platform drivers */ if (numevt == 1) numevt = 0; - dma_params->fifo_level = numevt; dma_data->maxburst = numevt; return 0; @@ -855,13 +876,35 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, return 0; } +static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, + unsigned int bclk_freq, + int *error_ppm) +{ + int div = mcasp->sysclk_freq / bclk_freq; + int rem = mcasp->sysclk_freq % bclk_freq; + + if (rem != 0) { + if (div == 0 || + ((mcasp->sysclk_freq / div) - bclk_freq) > + (bclk_freq - (mcasp->sysclk_freq / (div+1)))) { + div++; + rem = rem - bclk_freq; + } + } + if (error_ppm) + *error_ppm = + (div*1000000 + (int)div64_long(1000000LL*rem, + (int)bclk_freq)) + /div - 1000000; + + return div; +} + static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - struct davinci_pcm_dma_params *dma_params = - &mcasp->dma_params[substream->stream]; int word_length; int channels = params_channels(params); int period_size = params_period_size(params); @@ -872,16 +915,20 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, * the machine driver, we need to calculate the ratio. */ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - unsigned int bclk_freq = snd_soc_params_to_bclk(params); - unsigned int div = mcasp->sysclk_freq / bclk_freq; - if (mcasp->sysclk_freq % bclk_freq != 0) { - if (((mcasp->sysclk_freq / div) - bclk_freq) > - (bclk_freq - (mcasp->sysclk_freq / (div+1)))) - div++; - dev_warn(mcasp->dev, - "Inaccurate BCLK: %u Hz / %u != %u Hz\n", - mcasp->sysclk_freq, div, bclk_freq); - } + int channels = params_channels(params); + int rate = params_rate(params); + int sbits = params_width(params); + int ppm, div; + + if (channels > mcasp->tdm_slots) + channels = mcasp->tdm_slots; + + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*channels, + &ppm); + if (ppm) + dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", + ppm); + __davinci_mcasp_set_clkdiv(cpu_dai, 1, div, 0); } @@ -902,31 +949,26 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; word_length = 8; break; case SNDRV_PCM_FORMAT_U16_LE: case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; word_length = 16; break; case SNDRV_PCM_FORMAT_U24_3LE: case SNDRV_PCM_FORMAT_S24_3LE: - dma_params->data_type = 3; word_length = 24; break; case SNDRV_PCM_FORMAT_U24_LE: case SNDRV_PCM_FORMAT_S24_LE: - dma_params->data_type = 4; word_length = 24; break; case SNDRV_PCM_FORMAT_U32_LE: case SNDRV_PCM_FORMAT_S32_LE: - dma_params->data_type = 4; word_length = 32; break; @@ -935,11 +977,6 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - if (mcasp->version == MCASP_VERSION_2 && !dma_params->fifo_level) - dma_params->acnt = 4; - else - dma_params->acnt = dma_params->data_type; - davinci_config_channel_size(mcasp, word_length); if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) @@ -973,10 +1010,126 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; } +static const unsigned int davinci_mcasp_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, +}; + +#define DAVINCI_MAX_RATE_ERROR_PPM 1000 + +static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + struct snd_interval *ri = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int sbits = params_width(params); + int channels = params_channels(params); + unsigned int list[ARRAY_SIZE(davinci_mcasp_dai_rates)]; + int i, count = 0; + + if (channels > rd->mcasp->tdm_slots) + channels = rd->mcasp->tdm_slots; + + for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) { + if (ri->min <= davinci_mcasp_dai_rates[i] && + ri->max >= davinci_mcasp_dai_rates[i]) { + uint bclk_freq = sbits*channels* + davinci_mcasp_dai_rates[i]; + int ppm; + + davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) + list[count++] = davinci_mcasp_dai_rates[i]; + } + } + dev_dbg(rd->mcasp->dev, + "%d frequencies (%d-%d) for %d sbits and %d channels\n", + count, ri->min, ri->max, sbits, channels); + + return snd_interval_list(hw_param_interval(params, rule->var), + count, list, 0); +} + +static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask nfmt; + int rate = params_rate(params); + int channels = params_channels(params); + int i, count = 0; + + snd_mask_none(&nfmt); + + if (channels > rd->mcasp->tdm_slots) + channels = rd->mcasp->tdm_slots; + + for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + if (snd_mask_test(fmt, i)) { + uint bclk_freq = snd_pcm_format_width(i)*channels*rate; + int ppm; + + davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + snd_mask_set(&nfmt, i); + count++; + } + } + } + dev_dbg(rd->mcasp->dev, + "%d possible sample format for %d Hz and %d channels\n", + count, rate, channels); + + return snd_mask_refine(fmt, &nfmt); +} + +static int davinci_mcasp_hw_rule_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + struct snd_interval *ci = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + int sbits = params_width(params); + int rate = params_rate(params); + int max_chan_per_wire = rd->mcasp->tdm_slots < ci->max ? + rd->mcasp->tdm_slots : ci->max; + unsigned int list[ci->max - ci->min + 1]; + int c1, c, count = 0; + + for (c1 = ci->min; c1 <= max_chan_per_wire; c1++) { + uint bclk_freq = c1*sbits*rate; + int ppm; + + davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + /* If we can use all tdm_slots, we can put any + amount of channels to remaining wires as + long as they fit in. */ + if (c1 == rd->mcasp->tdm_slots) { + for (c = c1; c <= rd->serializers*c1 && + c <= ci->max; c++) + list[count++] = c; + } else { + list[count++] = c1; + } + } + } + dev_dbg(rd->mcasp->dev, + "%d possible channel counts (%d-%d) for %d Hz and %d sbits\n", + count, ci->min, ci->max, rate, sbits); + + return snd_interval_list(hw_param_interval(params, rule->var), + count, list, 0); +} + static int davinci_mcasp_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + struct davinci_mcasp_ruledata *ruledata = + &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir; @@ -998,6 +1151,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, if (mcasp->serial_dir[i] == dir) max_channels++; } + ruledata->serializers = max_channels; max_channels *= mcasp->tdm_slots; /* * If the already active stream has less channels than the calculated @@ -1012,6 +1166,42 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels); + + /* + * If we rely on implicit BCLK divider setting we should + * set constraints based on what we can provide. + */ + if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + int ret; + + ruledata->mcasp = mcasp; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + davinci_mcasp_hw_rule_rate, + ruledata, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) + return ret; + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + davinci_mcasp_hw_rule_format, + ruledata, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) + return ret; + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + davinci_mcasp_hw_rule_channels, + ruledata, + SNDRV_PCM_HW_PARAM_RATE, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) + return ret; + } + return 0; } @@ -1043,17 +1233,8 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) { struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - if (mcasp->version >= MCASP_VERSION_3) { - /* Using dmaengine PCM */ - dai->playback_dma_data = - &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = - &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - } else { - /* Using davinci-pcm */ - dai->playback_dma_data = mcasp->dma_params; - dai->capture_dma_data = mcasp->dma_params; - } + dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; return 0; } @@ -1066,6 +1247,10 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) u32 reg; int i; + context->pm_state = pm_runtime_enabled(mcasp->dev); + if (!context->pm_state) + pm_runtime_get_sync(mcasp->dev); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]); @@ -1082,6 +1267,8 @@ static int davinci_mcasp_suspend(struct snd_soc_dai *dai) context->xrsr_regs[i] = mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i)); + pm_runtime_put_sync(mcasp->dev); + return 0; } @@ -1092,6 +1279,8 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) u32 reg; int i; + pm_runtime_get_sync(mcasp->dev); + for (i = 0; i < ARRAY_SIZE(context_regs); i++) mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]); @@ -1108,6 +1297,9 @@ static int davinci_mcasp_resume(struct snd_soc_dai *dai) mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), context->xrsr_regs[i]); + if (!context->pm_state) + pm_runtime_put_sync(mcasp->dev); + return 0; } #else @@ -1172,28 +1364,24 @@ static const struct snd_soc_component_driver davinci_mcasp_component = { static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { .tx_dma_offset = 0x400, .rx_dma_offset = 0x400, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_1, }; static struct davinci_mcasp_pdata da830_mcasp_pdata = { .tx_dma_offset = 0x2000, .rx_dma_offset = 0x2000, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, }; static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { .tx_dma_offset = 0, .rx_dma_offset = 0, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_3, }; static struct davinci_mcasp_pdata dra7_mcasp_pdata = { .tx_dma_offset = 0x200, .rx_dma_offset = 0x284, - .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_4, }; @@ -1370,12 +1558,12 @@ nodata: static int davinci_mcasp_probe(struct platform_device *pdev) { - struct davinci_pcm_dma_params *dma_params; struct snd_dmaengine_dai_dma_data *dma_data; struct resource *mem, *ioarea, *res, *dat; struct davinci_mcasp_pdata *pdata; struct davinci_mcasp *mcasp; char *irq_name; + int *dma; int irq; int ret; @@ -1415,13 +1603,6 @@ static int davinci_mcasp_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); - ret = pm_runtime_get_sync(&pdev->dev); - if (IS_ERR_VALUE(ret)) { - dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n"); - pm_runtime_disable(&pdev->dev); - return ret; - } - mcasp->base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); if (!mcasp->base) { dev_err(&pdev->dev, "ioremap failed\n"); @@ -1509,59 +1690,45 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (dat) mcasp->dat_port = true; - dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_PLAYBACK]; dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dma_params->asp_chan_q = pdata->asp_chan_q; - dma_params->ram_chan_q = pdata->ram_chan_q; - dma_params->sram_pool = pdata->sram_pool; - dma_params->sram_size = pdata->sram_size_playback; if (dat) - dma_params->dma_addr = dat->start; + dma_data->addr = dat->start; else - dma_params->dma_addr = mem->start + pdata->tx_dma_offset; - - /* Unconditional dmaengine stuff */ - dma_data->addr = dma_params->dma_addr; + dma_data->addr = mem->start + pdata->tx_dma_offset; + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res) - dma_params->channel = res->start; + *dma = res->start; else - dma_params->channel = pdata->tx_dma_channel; + *dma = pdata->tx_dma_channel; /* dmaengine filter data for DT and non-DT boot */ if (pdev->dev.of_node) dma_data->filter_data = "tx"; else - dma_data->filter_data = &dma_params->channel; + dma_data->filter_data = dma; /* RX is not valid in DIT mode */ if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - dma_params = &mcasp->dma_params[SNDRV_PCM_STREAM_CAPTURE]; dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - dma_params->asp_chan_q = pdata->asp_chan_q; - dma_params->ram_chan_q = pdata->ram_chan_q; - dma_params->sram_pool = pdata->sram_pool; - dma_params->sram_size = pdata->sram_size_capture; if (dat) - dma_params->dma_addr = dat->start; + dma_data->addr = dat->start; else - dma_params->dma_addr = mem->start + pdata->rx_dma_offset; - - /* Unconditional dmaengine stuff */ - dma_data->addr = dma_params->dma_addr; + dma_data->addr = mem->start + pdata->rx_dma_offset; + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE]; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (res) - dma_params->channel = res->start; + *dma = res->start; else - dma_params->channel = pdata->rx_dma_channel; + *dma = pdata->rx_dma_channel; /* dmaengine filter data for DT and non-DT boot */ if (pdev->dev.of_node) dma_data->filter_data = "rx"; else - dma_data->filter_data = &dma_params->channel; + dma_data->filter_data = dma; } if (mcasp->version < MCASP_VERSION_3) { @@ -1584,17 +1751,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) goto err; switch (mcasp->version) { -#if IS_BUILTIN(CONFIG_SND_DAVINCI_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_DAVINCI_SOC)) - case MCASP_VERSION_1: - case MCASP_VERSION_2: - ret = davinci_soc_platform_register(&pdev->dev); - break; -#endif #if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ IS_MODULE(CONFIG_SND_EDMA_SOC)) + case MCASP_VERSION_1: + case MCASP_VERSION_2: case MCASP_VERSION_3: ret = edma_pcm_platform_register(&pdev->dev); break; @@ -1621,14 +1782,12 @@ static int davinci_mcasp_probe(struct platform_device *pdev) return 0; err: - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return ret; } static int davinci_mcasp_remove(struct platform_device *pdev) { - pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); return 0; diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c deleted file mode 100644 index 7809e9d935fc..000000000000 --- a/sound/soc/davinci/davinci-pcm.c +++ /dev/null @@ -1,861 +0,0 @@ -/* - * ALSA PCM interface for the TI DAVINCI processor - * - * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> - * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> - * added SRAM ping/pong (C) 2008 Troy Kisky <troy.kisky@boundarydevices.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/dma-mapping.h> -#include <linux/kernel.h> -#include <linux/genalloc.h> -#include <linux/platform_data/edma.h> - -#include <sound/core.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> -#include <sound/soc.h> - -#include <asm/dma.h> - -#include "davinci-pcm.h" - -#ifdef DEBUG -static void print_buf_info(int slot, char *name) -{ - struct edmacc_param p; - if (slot < 0) - return; - edma_read_slot(slot, &p); - printk(KERN_DEBUG "%s: 0x%x, opt=%x, src=%x, a_b_cnt=%x dst=%x\n", - name, slot, p.opt, p.src, p.a_b_cnt, p.dst); - printk(KERN_DEBUG " src_dst_bidx=%x link_bcntrld=%x src_dst_cidx=%x ccnt=%x\n", - p.src_dst_bidx, p.link_bcntrld, p.src_dst_cidx, p.ccnt); -} -#else -static void print_buf_info(int slot, char *name) -{ -} -#endif - -static struct snd_pcm_hardware pcm_hardware_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| - SNDRV_PCM_INFO_BATCH), - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -static struct snd_pcm_hardware pcm_hardware_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_BATCH), - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 8 * 1024, - .periods_min = 16, - .periods_max = 255, - .fifo_size = 0, -}; - -/* - * How ping/pong works.... - * - * Playback: - * ram_params - copys 2*ping_size from start of SDRAM to iram, - * links to ram_link2 - * ram_link2 - copys rest of SDRAM to iram in ping_size units, - * links to ram_link - * ram_link - copys entire SDRAM to iram in ping_size uints, - * links to self - * - * asp_params - same as asp_link[0] - * asp_link[0] - copys from lower half of iram to asp port - * links to asp_link[1], triggers iram copy event on completion - * asp_link[1] - copys from upper half of iram to asp port - * links to asp_link[0], triggers iram copy event on completion - * triggers interrupt only needed to let upper SOC levels update position - * in stream on completion - * - * When playback is started: - * ram_params started - * asp_params started - * - * Capture: - * ram_params - same as ram_link, - * links to ram_link - * ram_link - same as playback - * links to self - * - * asp_params - same as playback - * asp_link[0] - same as playback - * asp_link[1] - same as playback - * - * When capture is started: - * asp_params started - */ -struct davinci_runtime_data { - spinlock_t lock; - int period; /* current DMA period */ - int asp_channel; /* Master DMA channel */ - int asp_link[2]; /* asp parameter link channel, ping/pong */ - struct davinci_pcm_dma_params *params; /* DMA params */ - int ram_channel; - int ram_link; - int ram_link2; - struct edmacc_param asp_params; - struct edmacc_param ram_params; -}; - -static void davinci_pcm_period_elapsed(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - - prtd->period++; - if (unlikely(prtd->period >= runtime->periods)) - prtd->period = 0; -} - -static void davinci_pcm_period_reset(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - prtd->period = 0; -} -/* - * Not used with ping/pong - */ -static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct snd_pcm_runtime *runtime = substream->runtime; - unsigned int period_size; - unsigned int dma_offset; - dma_addr_t dma_pos; - dma_addr_t src, dst; - unsigned short src_bidx, dst_bidx; - unsigned short src_cidx, dst_cidx; - unsigned int data_type; - unsigned short acnt; - unsigned int count; - unsigned int fifo_level; - - period_size = snd_pcm_lib_period_bytes(substream); - dma_offset = prtd->period * period_size; - dma_pos = runtime->dma_addr + dma_offset; - fifo_level = prtd->params->fifo_level; - - pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " - "dma_ptr = %x period_size=%x\n", prtd->asp_link[0], dma_pos, - period_size); - - data_type = prtd->params->data_type; - count = period_size / data_type; - if (fifo_level) - count /= fifo_level; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - src = dma_pos; - dst = prtd->params->dma_addr; - src_bidx = data_type; - dst_bidx = 4; - src_cidx = data_type * fifo_level; - dst_cidx = 0; - } else { - src = prtd->params->dma_addr; - dst = dma_pos; - src_bidx = 0; - dst_bidx = data_type; - src_cidx = 0; - dst_cidx = data_type * fifo_level; - } - - acnt = prtd->params->acnt; - edma_set_src(prtd->asp_link[0], src, INCR, W8BIT); - edma_set_dest(prtd->asp_link[0], dst, INCR, W8BIT); - - edma_set_src_index(prtd->asp_link[0], src_bidx, src_cidx); - edma_set_dest_index(prtd->asp_link[0], dst_bidx, dst_cidx); - - if (!fifo_level) - edma_set_transfer_params(prtd->asp_link[0], acnt, count, 1, 0, - ASYNC); - else - edma_set_transfer_params(prtd->asp_link[0], acnt, - fifo_level, - count, fifo_level, - ABSYNC); -} - -static void davinci_pcm_dma_irq(unsigned link, u16 ch_status, void *data) -{ - struct snd_pcm_substream *substream = data; - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - print_buf_info(prtd->ram_channel, "i ram_channel"); - pr_debug("davinci_pcm: link=%d, status=0x%x\n", link, ch_status); - - if (unlikely(ch_status != EDMA_DMA_COMPLETE)) - return; - - if (snd_pcm_running(substream)) { - spin_lock(&prtd->lock); - if (prtd->ram_channel < 0) { - /* No ping/pong must fix up link dma data*/ - davinci_pcm_enqueue_dma(substream); - } - davinci_pcm_period_elapsed(substream); - spin_unlock(&prtd->lock); - snd_pcm_period_elapsed(substream); - } -} - -#ifdef CONFIG_GENERIC_ALLOCATOR -static int allocate_sram(struct snd_pcm_substream *substream, - struct gen_pool *sram_pool, unsigned size, - struct snd_pcm_hardware *ppcm) -{ - struct snd_dma_buffer *buf = &substream->dma_buffer; - struct snd_dma_buffer *iram_dma = NULL; - dma_addr_t iram_phys = 0; - void *iram_virt = NULL; - - if (buf->private_data || !size) - return 0; - - ppcm->period_bytes_max = size; - iram_virt = gen_pool_dma_alloc(sram_pool, size, &iram_phys); - if (!iram_virt) - goto exit1; - iram_dma = kzalloc(sizeof(*iram_dma), GFP_KERNEL); - if (!iram_dma) - goto exit2; - iram_dma->area = iram_virt; - iram_dma->addr = iram_phys; - memset(iram_dma->area, 0, size); - iram_dma->bytes = size; - buf->private_data = iram_dma; - return 0; -exit2: - if (iram_virt) - gen_pool_free(sram_pool, (unsigned)iram_virt, size); -exit1: - return -ENOMEM; -} - -static void davinci_free_sram(struct snd_pcm_substream *substream, - struct snd_dma_buffer *iram_dma) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct gen_pool *sram_pool = prtd->params->sram_pool; - - gen_pool_free(sram_pool, (unsigned) iram_dma->area, iram_dma->bytes); -} -#else -static int allocate_sram(struct snd_pcm_substream *substream, - struct gen_pool *sram_pool, unsigned size, - struct snd_pcm_hardware *ppcm) -{ - return 0; -} - -static void davinci_free_sram(struct snd_pcm_substream *substream, - struct snd_dma_buffer *iram_dma) -{ -} -#endif - -/* - * Only used with ping/pong. - * This is called after runtime->dma_addr, period_bytes and data_type are valid - */ -static int ping_pong_dma_setup(struct snd_pcm_substream *substream) -{ - unsigned short ram_src_cidx, ram_dst_cidx; - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - struct snd_dma_buffer *iram_dma = - (struct snd_dma_buffer *)substream->dma_buffer.private_data; - struct davinci_pcm_dma_params *params = prtd->params; - unsigned int data_type = params->data_type; - unsigned int acnt = params->acnt; - /* divide by 2 for ping/pong */ - unsigned int ping_size = snd_pcm_lib_period_bytes(substream) >> 1; - unsigned int fifo_level = prtd->params->fifo_level; - unsigned int count; - if ((data_type == 0) || (data_type > 4)) { - printk(KERN_ERR "%s: data_type=%i\n", __func__, data_type); - return -EINVAL; - } - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - dma_addr_t asp_src_pong = iram_dma->addr + ping_size; - ram_src_cidx = ping_size; - ram_dst_cidx = -ping_size; - edma_set_src(prtd->asp_link[1], asp_src_pong, INCR, W8BIT); - - edma_set_src_index(prtd->asp_link[0], data_type, - data_type * fifo_level); - edma_set_src_index(prtd->asp_link[1], data_type, - data_type * fifo_level); - - edma_set_src(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); - } else { - dma_addr_t asp_dst_pong = iram_dma->addr + ping_size; - ram_src_cidx = -ping_size; - ram_dst_cidx = ping_size; - edma_set_dest(prtd->asp_link[1], asp_dst_pong, INCR, W8BIT); - - edma_set_dest_index(prtd->asp_link[0], data_type, - data_type * fifo_level); - edma_set_dest_index(prtd->asp_link[1], data_type, - data_type * fifo_level); - - edma_set_dest(prtd->ram_link, runtime->dma_addr, INCR, W32BIT); - } - - if (!fifo_level) { - count = ping_size / data_type; - edma_set_transfer_params(prtd->asp_link[0], acnt, count, - 1, 0, ASYNC); - edma_set_transfer_params(prtd->asp_link[1], acnt, count, - 1, 0, ASYNC); - } else { - count = ping_size / (data_type * fifo_level); - edma_set_transfer_params(prtd->asp_link[0], acnt, fifo_level, - count, fifo_level, ABSYNC); - edma_set_transfer_params(prtd->asp_link[1], acnt, fifo_level, - count, fifo_level, ABSYNC); - } - - edma_set_src_index(prtd->ram_link, ping_size, ram_src_cidx); - edma_set_dest_index(prtd->ram_link, ping_size, ram_dst_cidx); - edma_set_transfer_params(prtd->ram_link, ping_size, 2, - runtime->periods, 2, ASYNC); - - /* init master params */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - edma_read_slot(prtd->ram_link, &prtd->ram_params); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - struct edmacc_param p_ram; - /* Copy entire iram buffer before playback started */ - prtd->ram_params.a_b_cnt = (1 << 16) | (ping_size << 1); - /* 0 dst_bidx */ - prtd->ram_params.src_dst_bidx = (ping_size << 1); - /* 0 dst_cidx */ - prtd->ram_params.src_dst_cidx = (ping_size << 1); - prtd->ram_params.ccnt = 1; - - /* Skip 1st period */ - edma_read_slot(prtd->ram_link, &p_ram); - p_ram.src += (ping_size << 1); - p_ram.ccnt -= 1; - edma_write_slot(prtd->ram_link2, &p_ram); - /* - * When 1st started, ram -> iram dma channel will fill the - * entire iram. Then, whenever a ping/pong asp buffer finishes, - * 1/2 iram will be filled. - */ - prtd->ram_params.link_bcntrld = - EDMA_CHAN_SLOT(prtd->ram_link2) << 5; - } - return 0; -} - -/* 1 asp tx or rx channel using 2 parameter channels - * 1 ram to/from iram channel using 1 parameter channel - * - * Playback - * ram copy channel kicks off first, - * 1st ram copy of entire iram buffer completion kicks off asp channel - * asp tcc always kicks off ram copy of 1/2 iram buffer - * - * Record - * asp channel starts, tcc kicks off ram copy - */ -static int request_ping_pong(struct snd_pcm_substream *substream, - struct davinci_runtime_data *prtd, - struct snd_dma_buffer *iram_dma) -{ - dma_addr_t asp_src_ping; - dma_addr_t asp_dst_ping; - int ret; - struct davinci_pcm_dma_params *params = prtd->params; - - /* Request ram master channel */ - ret = prtd->ram_channel = edma_alloc_channel(EDMA_CHANNEL_ANY, - davinci_pcm_dma_irq, substream, - prtd->params->ram_chan_q); - if (ret < 0) - goto exit1; - - /* Request ram link channel */ - ret = prtd->ram_link = edma_alloc_slot( - EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit2; - - ret = prtd->asp_link[1] = edma_alloc_slot( - EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit3; - - prtd->ram_link2 = -1; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = prtd->ram_link2 = edma_alloc_slot( - EDMA_CTLR(prtd->ram_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit4; - } - /* circle ping-pong buffers */ - edma_link(prtd->asp_link[0], prtd->asp_link[1]); - edma_link(prtd->asp_link[1], prtd->asp_link[0]); - /* circle ram buffers */ - edma_link(prtd->ram_link, prtd->ram_link); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - asp_src_ping = iram_dma->addr; - asp_dst_ping = params->dma_addr; /* fifo */ - } else { - asp_src_ping = params->dma_addr; /* fifo */ - asp_dst_ping = iram_dma->addr; - } - /* ping */ - edma_set_src(prtd->asp_link[0], asp_src_ping, INCR, W16BIT); - edma_set_dest(prtd->asp_link[0], asp_dst_ping, INCR, W16BIT); - edma_set_src_index(prtd->asp_link[0], 0, 0); - edma_set_dest_index(prtd->asp_link[0], 0, 0); - - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f) | TCINTEN); - prtd->asp_params.opt |= TCCHEN | - EDMA_TCC(prtd->ram_channel & 0x3f); - edma_write_slot(prtd->asp_link[0], &prtd->asp_params); - - /* pong */ - edma_set_src(prtd->asp_link[1], asp_src_ping, INCR, W16BIT); - edma_set_dest(prtd->asp_link[1], asp_dst_ping, INCR, W16BIT); - edma_set_src_index(prtd->asp_link[1], 0, 0); - edma_set_dest_index(prtd->asp_link[1], 0, 0); - - edma_read_slot(prtd->asp_link[1], &prtd->asp_params); - prtd->asp_params.opt &= ~(TCCMODE | EDMA_TCC(0x3f)); - /* interrupt after every pong completion */ - prtd->asp_params.opt |= TCINTEN | TCCHEN | - EDMA_TCC(prtd->ram_channel & 0x3f); - edma_write_slot(prtd->asp_link[1], &prtd->asp_params); - - /* ram */ - edma_set_src(prtd->ram_link, iram_dma->addr, INCR, W32BIT); - edma_set_dest(prtd->ram_link, iram_dma->addr, INCR, W32BIT); - pr_debug("%s: audio dma channels/slots in use for ram:%u %u %u," - "for asp:%u %u %u\n", __func__, - prtd->ram_channel, prtd->ram_link, prtd->ram_link2, - prtd->asp_channel, prtd->asp_link[0], - prtd->asp_link[1]); - return 0; -exit4: - edma_free_channel(prtd->asp_link[1]); - prtd->asp_link[1] = -1; -exit3: - edma_free_channel(prtd->ram_link); - prtd->ram_link = -1; -exit2: - edma_free_channel(prtd->ram_channel); - prtd->ram_channel = -1; -exit1: - return ret; -} - -static int davinci_pcm_dma_request(struct snd_pcm_substream *substream) -{ - struct snd_dma_buffer *iram_dma; - struct davinci_runtime_data *prtd = substream->runtime->private_data; - struct davinci_pcm_dma_params *params = prtd->params; - int ret; - - if (!params) - return -ENODEV; - - /* Request asp master DMA channel */ - ret = prtd->asp_channel = edma_alloc_channel(params->channel, - davinci_pcm_dma_irq, substream, - prtd->params->asp_chan_q); - if (ret < 0) - goto exit1; - - /* Request asp link channels */ - ret = prtd->asp_link[0] = edma_alloc_slot( - EDMA_CTLR(prtd->asp_channel), EDMA_SLOT_ANY); - if (ret < 0) - goto exit2; - - iram_dma = (struct snd_dma_buffer *)substream->dma_buffer.private_data; - if (iram_dma) { - if (request_ping_pong(substream, prtd, iram_dma) == 0) - return 0; - printk(KERN_WARNING "%s: dma channel allocation failed," - "not using sram\n", __func__); - } - - /* Issue transfer completion IRQ when the channel completes a - * transfer, then always reload from the same slot (by a kind - * of loopback link). The completion IRQ handler will update - * the reload slot with a new buffer. - * - * REVISIT save p_ram here after setting up everything except - * the buffer and its length (ccnt) ... use it as a template - * so davinci_pcm_enqueue_dma() takes less time in IRQ. - */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - prtd->asp_params.opt |= TCINTEN | - EDMA_TCC(EDMA_CHAN_SLOT(prtd->asp_channel)); - prtd->asp_params.link_bcntrld = EDMA_CHAN_SLOT(prtd->asp_link[0]) << 5; - edma_write_slot(prtd->asp_link[0], &prtd->asp_params); - return 0; -exit2: - edma_free_channel(prtd->asp_channel); - prtd->asp_channel = -1; -exit1: - return ret; -} - -static int davinci_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - int ret = 0; - - spin_lock(&prtd->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - edma_start(prtd->asp_channel); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && - prtd->ram_channel >= 0) { - /* copy 1st iram buffer */ - edma_start(prtd->ram_channel); - } - break; - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - edma_resume(prtd->asp_channel); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - edma_pause(prtd->asp_channel); - break; - default: - ret = -EINVAL; - break; - } - - spin_unlock(&prtd->lock); - - return ret; -} - -static int davinci_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct davinci_runtime_data *prtd = substream->runtime->private_data; - - davinci_pcm_period_reset(substream); - if (prtd->ram_channel >= 0) { - int ret = ping_pong_dma_setup(substream); - if (ret < 0) - return ret; - - edma_write_slot(prtd->ram_channel, &prtd->ram_params); - edma_write_slot(prtd->asp_channel, &prtd->asp_params); - - print_buf_info(prtd->ram_channel, "ram_channel"); - print_buf_info(prtd->ram_link, "ram_link"); - print_buf_info(prtd->ram_link2, "ram_link2"); - print_buf_info(prtd->asp_channel, "asp_channel"); - print_buf_info(prtd->asp_link[0], "asp_link[0]"); - print_buf_info(prtd->asp_link[1], "asp_link[1]"); - - /* - * There is a phase offset of 2 periods between the position - * used by dma setup and the position reported in the pointer - * function. - * - * The phase offset, when not using ping-pong buffers, is due to - * the two consecutive calls to davinci_pcm_enqueue_dma() below. - * - * Whereas here, with ping-pong buffers, the phase is due to - * there being an entire buffer transfer complete before the - * first dma completion event triggers davinci_pcm_dma_irq(). - */ - davinci_pcm_period_elapsed(substream); - davinci_pcm_period_elapsed(substream); - - return 0; - } - davinci_pcm_enqueue_dma(substream); - davinci_pcm_period_elapsed(substream); - - /* Copy self-linked parameter RAM entry into master channel */ - edma_read_slot(prtd->asp_link[0], &prtd->asp_params); - edma_write_slot(prtd->asp_channel, &prtd->asp_params); - davinci_pcm_enqueue_dma(substream); - davinci_pcm_period_elapsed(substream); - - return 0; -} - -static snd_pcm_uframes_t -davinci_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - unsigned int offset; - int asp_count; - unsigned int period_size = snd_pcm_lib_period_bytes(substream); - - /* - * There is a phase offset of 2 periods between the position used by dma - * setup and the position reported in the pointer function. Either +2 in - * the dma setup or -2 here in the pointer function (with wrapping, - * both) accounts for this offset -- choose the latter since it makes - * the first-time setup clearer. - */ - spin_lock(&prtd->lock); - asp_count = prtd->period - 2; - spin_unlock(&prtd->lock); - - if (asp_count < 0) - asp_count += runtime->periods; - asp_count *= period_size; - - offset = bytes_to_frames(runtime, asp_count); - if (offset >= runtime->buffer_size) - offset = 0; - - return offset; -} - -static int davinci_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd; - struct snd_pcm_hardware *ppcm; - int ret = 0; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_pcm_dma_params *pa; - struct davinci_pcm_dma_params *params; - - pa = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - if (!pa) - return -ENODEV; - params = &pa[substream->stream]; - - ppcm = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? - &pcm_hardware_playback : &pcm_hardware_capture; - allocate_sram(substream, params->sram_pool, params->sram_size, ppcm); - snd_soc_set_runtime_hwparams(substream, ppcm); - /* ensure that buffer size is a multiple of period size */ - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - - prtd = kzalloc(sizeof(struct davinci_runtime_data), GFP_KERNEL); - if (prtd == NULL) - return -ENOMEM; - - spin_lock_init(&prtd->lock); - prtd->params = params; - prtd->asp_channel = -1; - prtd->asp_link[0] = prtd->asp_link[1] = -1; - prtd->ram_channel = -1; - prtd->ram_link = -1; - prtd->ram_link2 = -1; - - runtime->private_data = prtd; - - ret = davinci_pcm_dma_request(substream); - if (ret) { - printk(KERN_ERR "davinci_pcm: Failed to get dma channels\n"); - kfree(prtd); - } - - return ret; -} - -static int davinci_pcm_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct davinci_runtime_data *prtd = runtime->private_data; - - if (prtd->ram_channel >= 0) - edma_stop(prtd->ram_channel); - if (prtd->asp_channel >= 0) - edma_stop(prtd->asp_channel); - if (prtd->asp_link[0] >= 0) - edma_unlink(prtd->asp_link[0]); - if (prtd->asp_link[1] >= 0) - edma_unlink(prtd->asp_link[1]); - if (prtd->ram_link >= 0) - edma_unlink(prtd->ram_link); - - if (prtd->asp_link[0] >= 0) - edma_free_slot(prtd->asp_link[0]); - if (prtd->asp_link[1] >= 0) - edma_free_slot(prtd->asp_link[1]); - if (prtd->asp_channel >= 0) - edma_free_channel(prtd->asp_channel); - if (prtd->ram_link >= 0) - edma_free_slot(prtd->ram_link); - if (prtd->ram_link2 >= 0) - edma_free_slot(prtd->ram_link2); - if (prtd->ram_channel >= 0) - edma_free_channel(prtd->ram_channel); - - kfree(prtd); - - return 0; -} - -static int davinci_pcm_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 davinci_pcm_hw_free(struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - -static int davinci_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - - return dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); -} - -static struct snd_pcm_ops davinci_pcm_ops = { - .open = davinci_pcm_open, - .close = davinci_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = davinci_pcm_hw_params, - .hw_free = davinci_pcm_hw_free, - .prepare = davinci_pcm_prepare, - .trigger = davinci_pcm_trigger, - .pointer = davinci_pcm_pointer, - .mmap = davinci_pcm_mmap, -}; - -static int davinci_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream, - size_t size) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - - pr_debug("davinci_pcm: preallocate_dma_buffer: area=%p, addr=%p, " - "size=%d\n", (void *) buf->area, (void *) buf->addr, size); - - if (!buf->area) - return -ENOMEM; - - buf->bytes = size; - return 0; -} - -static void davinci_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - struct snd_dma_buffer *iram_dma; - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - iram_dma = buf->private_data; - if (iram_dma) { - davinci_free_sram(substream, iram_dma); - kfree(iram_dma); - } - } -} - -static int davinci_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret; - - ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK, - pcm_hardware_playback.buffer_bytes_max); - if (ret) - return ret; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = davinci_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE, - pcm_hardware_capture.buffer_bytes_max); - if (ret) - return ret; - } - - return 0; -} - -static struct snd_soc_platform_driver davinci_soc_platform = { - .ops = &davinci_pcm_ops, - .pcm_new = davinci_pcm_new, - .pcm_free = davinci_pcm_free, -}; - -int davinci_soc_platform_register(struct device *dev) -{ - return devm_snd_soc_register_platform(dev, &davinci_soc_platform); -} -EXPORT_SYMBOL_GPL(davinci_soc_platform_register); - -MODULE_AUTHOR("Vladimir Barinov"); -MODULE_DESCRIPTION("TI DAVINCI PCM DMA module"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h deleted file mode 100644 index 0fe2346a9aa2..000000000000 --- a/sound/soc/davinci/davinci-pcm.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * ALSA PCM interface for the TI DAVINCI processor - * - * Author: Vladimir Barinov, <vbarinov@embeddedalley.com> - * Copyright: (C) 2007 MontaVista Software, Inc., <source@mvista.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _DAVINCI_PCM_H -#define _DAVINCI_PCM_H - -#include <linux/genalloc.h> -#include <linux/platform_data/davinci_asp.h> -#include <linux/platform_data/edma.h> - -struct davinci_pcm_dma_params { - int channel; /* sync dma channel ID */ - unsigned short acnt; - dma_addr_t dma_addr; /* device physical address for DMA */ - unsigned sram_size; - struct gen_pool *sram_pool; /* SRAM gen_pool for ping pong */ - enum dma_event_q asp_chan_q; /* event queue number for ASP channel */ - enum dma_event_q ram_chan_q; /* event queue number for RAM channel */ - unsigned char data_type; /* xfer data type */ - unsigned char convert_mono_stereo; - unsigned int fifo_level; -}; - -#if IS_ENABLED(CONFIG_SND_DAVINCI_SOC) -int davinci_soc_platform_register(struct device *dev); -#else -static inline int davinci_soc_platform_register(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_SND_DAVINCI_SOC */ - -#endif diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 5bee04279ebe..fabd05f24aeb 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -33,8 +33,9 @@ #include <sound/pcm_params.h> #include <sound/initval.h> #include <sound/soc.h> +#include <sound/dmaengine_pcm.h> -#include "davinci-pcm.h" +#include "edma-pcm.h" #include "davinci-i2s.h" #define MOD_REG_BIT(val, mask, set) do { \ @@ -47,7 +48,8 @@ struct davinci_vcif_dev { struct davinci_vc *davinci_vc; - struct davinci_pcm_dma_params dma_params[2]; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; }; static void davinci_vcif_start(struct snd_pcm_substream *substream) @@ -93,8 +95,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, { struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai); struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; - struct davinci_pcm_dma_params *dma_params = - &davinci_vcif_dev->dma_params[substream->stream]; u32 w; /* Restart the codec before setup */ @@ -113,16 +113,12 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, /* Determine xfer data type */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_U8: - dma_params->data_type = 0; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_RD_UNSIGNED | DAVINCI_VC_CTRL_WD_BITS_8 | DAVINCI_VC_CTRL_WD_UNSIGNED, 1); break; case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_WD_BITS_8, 1); @@ -130,8 +126,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, DAVINCI_VC_CTRL_WD_UNSIGNED, 0); break; case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | DAVINCI_VC_CTRL_RD_UNSIGNED | DAVINCI_VC_CTRL_WD_BITS_8 | @@ -142,8 +136,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - dma_params->acnt = dma_params->data_type; - writel(w, davinci_vc->base + DAVINCI_VC_CTRL); return 0; @@ -172,24 +164,25 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } -static int davinci_vcif_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); - - snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); - return 0; -} - #define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 static const struct snd_soc_dai_ops davinci_vcif_dai_ops = { - .startup = davinci_vcif_startup, .trigger = davinci_vcif_trigger, .hw_params = davinci_vcif_hw_params, }; +static int davinci_vcif_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + static struct snd_soc_dai_driver davinci_vcif_dai = { + .probe = davinci_vcif_dai_probe, .playback = { .channels_min = 1, .channels_max = 2, @@ -225,16 +218,16 @@ static int davinci_vcif_probe(struct platform_device *pdev) /* DMA tx params */ davinci_vcif_dev->davinci_vc = davinci_vc; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = - davinci_vc->davinci_vcif.dma_tx_channel; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = - davinci_vc->davinci_vcif.dma_tx_addr; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = + &davinci_vc->davinci_vcif.dma_tx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = + davinci_vc->davinci_vcif.dma_tx_addr; /* DMA rx params */ - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = - davinci_vc->davinci_vcif.dma_rx_channel; - davinci_vcif_dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = - davinci_vc->davinci_vcif.dma_rx_addr; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = + &davinci_vc->davinci_vcif.dma_rx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = + davinci_vc->davinci_vcif.dma_rx_addr; dev_set_drvdata(&pdev->dev, davinci_vcif_dev); @@ -245,7 +238,7 @@ static int davinci_vcif_probe(struct platform_device *pdev) return ret; } - ret = davinci_soc_platform_register(&pdev->dev); + ret = edma_pcm_platform_register(&pdev->dev); if (ret) { dev_err(&pdev->dev, "register PCM failed: %d\n", ret); snd_soc_unregister_component(&pdev->dev); diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 081e406b3713..19c302b0d763 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -24,7 +24,7 @@ config SND_SOC_FSL_SAI in-tree drivers select it automatically. config SND_SOC_FSL_SSI - tristate "Synchronous Serial Interface module support" + tristate "Synchronous Serial Interface module (SSI) support" select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) select REGMAP_MMIO @@ -35,7 +35,7 @@ config SND_SOC_FSL_SSI in-tree drivers select it automatically. config SND_SOC_FSL_SPDIF - tristate "Sony/Philips Digital Interface module support" + tristate "Sony/Philips Digital Interface (S/PDIF) module support" select REGMAP_MMIO select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC) diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 3f6959c8e2f7..de438871040b 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -512,6 +512,12 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) memcpy(priv->dai_link, fsl_asoc_card_dai, sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link)); + ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing"); + if (ret) { + dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret); + goto asrc_fail; + } + /* Normal DAI Link */ priv->dai_link[0].cpu_of_node = cpu_np; priv->dai_link[0].codec_of_node = codec_np; diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 6b0c8f717ec2..e8bb8eef1d16 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c @@ -1288,7 +1288,7 @@ static int fsl_ssi_probe(struct platform_device *pdev) const struct of_device_id *of_id; const char *p, *sprop; const uint32_t *iprop; - struct resource res; + struct resource *res; void __iomem *iomem; char name[64]; @@ -1335,19 +1335,11 @@ static int fsl_ssi_probe(struct platform_device *pdev) } ssi_private->cpu_dai_drv.name = dev_name(&pdev->dev); - /* Get the addresses and IRQ */ - ret = of_address_to_resource(np, 0, &res); - if (ret) { - dev_err(&pdev->dev, "could not determine device resources\n"); - return ret; - } - ssi_private->ssi_phys = res.start; - - iomem = devm_ioremap(&pdev->dev, res.start, resource_size(&res)); - if (!iomem) { - dev_err(&pdev->dev, "could not map device resources\n"); - return -ENOMEM; - } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iomem = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + ssi_private->ssi_phys = res->start; ret = of_property_match_string(np, "clock-names", "ipg"); if (ret < 0) { @@ -1393,8 +1385,8 @@ static int fsl_ssi_probe(struct platform_device *pdev) return ret; } - ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component, - &ssi_private->cpu_dai_drv, 1); + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_ssi_component, + &ssi_private->cpu_dai_drv, 1); if (ret) { dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); goto error_asoc_register; @@ -1407,13 +1399,13 @@ static int fsl_ssi_probe(struct platform_device *pdev) if (ret < 0) { dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq); - goto error_irq; + goto error_asoc_register; } } ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev); if (ret) - goto error_irq; + goto error_asoc_register; /* * If codec-handle property is missing from SSI node, we assume @@ -1454,9 +1446,6 @@ done: error_sound_card: fsl_ssi_debugfs_remove(&ssi_private->dbg_stats); -error_irq: - snd_soc_unregister_component(&pdev->dev); - error_asoc_register: if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); @@ -1472,7 +1461,6 @@ static int fsl_ssi_remove(struct platform_device *pdev) if (ssi_private->pdev) platform_device_unregister(ssi_private->pdev); - snd_soc_unregister_component(&pdev->dev); if (ssi_private->soc->imx) fsl_ssi_imx_clean(pdev, ssi_private); diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index f8cf10e16ce9..20e7400e2611 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -53,9 +53,9 @@ static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) /* Headphone jack detection */ if (gpio_is_valid(data->jack_gpio)) { - ret = snd_soc_jack_new(rtd->codec, "Headphone", - SND_JACK_HEADPHONE | SND_JACK_BTN_0, - &headset_jack); + ret = snd_soc_card_jack_new(rtd->card, "Headphone", + SND_JACK_HEADPHONE | SND_JACK_BTN_0, + &headset_jack, NULL, 0); if (ret) return ret; diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c index 08d2a8069b0a..0bab76051fd8 100644 --- a/sound/soc/fsl/mpc5200_psc_ac97.c +++ b/sound/soc/fsl/mpc5200_psc_ac97.c @@ -326,7 +326,7 @@ static int psc_ac97_of_remove(struct platform_device *op) } /* Match table for of_platform binding */ -static struct of_device_id psc_ac97_match[] = { +static const struct of_device_id psc_ac97_match[] = { { .compatible = "fsl,mpc5200-psc-ac97", }, { .compatible = "fsl,mpc5200b-psc-ac97", }, {} diff --git a/sound/soc/fsl/mpc5200_psc_i2s.c b/sound/soc/fsl/mpc5200_psc_i2s.c index 51fb0c00fe73..d8232943ccb6 100644 --- a/sound/soc/fsl/mpc5200_psc_i2s.c +++ b/sound/soc/fsl/mpc5200_psc_i2s.c @@ -217,7 +217,7 @@ static int psc_i2s_of_remove(struct platform_device *op) } /* Match table for of_platform binding */ -static struct of_device_id psc_i2s_match[] = { +static const struct of_device_id psc_i2s_match[] = { { .compatible = "fsl,mpc5200-psc-i2s", }, { .compatible = "fsl,mpc5200b-psc-i2s", }, {} diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c index c44459d24c50..ec731223cab3 100644 --- a/sound/soc/fsl/pcm030-audio-fabric.c +++ b/sound/soc/fsl/pcm030-audio-fabric.c @@ -113,7 +113,7 @@ static int pcm030_fabric_remove(struct platform_device *op) return ret; } -static struct of_device_id pcm030_audio_match[] = { +static const struct of_device_id pcm030_audio_match[] = { { .compatible = "phytec,pcm030-audio-fabric", }, {} }; diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c index a958937ab405..b454972dce35 100644 --- a/sound/soc/fsl/wm1133-ev1.c +++ b/sound/soc/fsl/wm1133-ev1.c @@ -202,23 +202,20 @@ static struct snd_soc_jack_pin mic_jack_pins[] = { static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; /* Headphone jack detection */ - snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack); - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE); /* Microphone jack detection */ - snd_soc_jack_new(codec, "Microphone", - SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Microphone", + SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack, + mic_jack_pins, ARRAY_SIZE(mic_jack_pins)); wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE, SND_JACK_BTN_0); - snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); return 0; } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fb550b5869d2..33feee9ca8c3 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -125,14 +125,6 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, { int ret; - if (set->fmt) { - ret = snd_soc_dai_set_fmt(dai, set->fmt); - if (ret && ret != -ENOTSUPP) { - dev_err(dai->dev, "simple-card: set_fmt error\n"); - goto err; - } - } - if (set->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); if (ret && ret != -ENOTSUPP) { @@ -176,11 +168,11 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return ret; if (gpio_is_valid(priv->gpio_hp_det)) { - snd_soc_jack_new(codec->codec, "Headphones", SND_JACK_HEADPHONE, - &simple_card_hp_jack); - snd_soc_jack_add_pins(&simple_card_hp_jack, - ARRAY_SIZE(simple_card_hp_jack_pins), - simple_card_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", + SND_JACK_HEADPHONE, + &simple_card_hp_jack, + simple_card_hp_jack_pins, + ARRAY_SIZE(simple_card_hp_jack_pins)); simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det; simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert; @@ -189,11 +181,11 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) } if (gpio_is_valid(priv->gpio_mic_det)) { - snd_soc_jack_new(codec->codec, "Mic Jack", SND_JACK_MICROPHONE, - &simple_card_mic_jack); - snd_soc_jack_add_pins(&simple_card_mic_jack, - ARRAY_SIZE(simple_card_mic_jack_pins), - simple_card_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + &simple_card_mic_jack, + simple_card_mic_jack_pins, + ARRAY_SIZE(simple_card_mic_jack_pins)); simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det; simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert; snd_soc_jack_add_gpios(&simple_card_mic_jack, 1, @@ -269,12 +261,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, struct device_node *codec, char *prefix, int idx) { + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct device *dev = simple_priv_to_dev(priv); struct device_node *bitclkmaster = NULL; struct device_node *framemaster = NULL; - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; unsigned int daifmt; daifmt = snd_soc_of_parse_daifmt(node, prefix, @@ -289,8 +279,7 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, */ dev_dbg(dev, "Revert to legacy daifmt parsing\n"); - cpu_dai->fmt = codec_dai->fmt = - snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | + daifmt = snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) | (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK); } else { if (codec == bitclkmaster) @@ -299,11 +288,10 @@ static int asoc_simple_card_parse_daifmt(struct device_node *node, else daifmt |= (codec == framemaster) ? SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; - - cpu_dai->fmt = daifmt; - codec_dai->fmt = daifmt; } + dai_link->dai_fmt = daifmt; + of_node_put(bitclkmaster); of_node_put(framemaster); @@ -384,13 +372,12 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, dai_link->init = asoc_simple_card_dai_init; dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); - dev_dbg(dev, "\tcpu : %s / %04x / %d\n", + dev_dbg(dev, "\tformat : %04x\n", dai_link->dai_fmt); + dev_dbg(dev, "\tcpu : %s / %d\n", dai_link->cpu_dai_name, - dai_props->cpu_dai.fmt, dai_props->cpu_dai.sysclk); - dev_dbg(dev, "\tcodec : %s / %04x / %d\n", + dev_dbg(dev, "\tcodec : %s / %d\n", dai_link->codec_dai_name, - dai_props->codec_dai.fmt, dai_props->codec_dai.sysclk); /* @@ -577,14 +564,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->codec_name = cinfo->codec; dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->codec_dai_name = cinfo->codec_dai.name; + dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, sizeof(priv->dai_props->cpu_dai)); memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, sizeof(priv->dai_props->codec_dai)); - priv->dai_props->cpu_dai.fmt |= cinfo->daifmt; - priv->dai_props->codec_dai.fmt |= cinfo->daifmt; } snd_soc_card_set_drvdata(&priv->snd_card, priv); diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index a8e53c45c6b6..cd9aee9871a3 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -1,42 +1,10 @@ # Core support -snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o -snd-soc-sst-acpi-objs := sst-acpi.o - -snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ - sst-mfld-platform-compress.o sst-atom-controls.o -snd-soc-mfld-machine-objs := mfld_machine.o - -obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o -obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o - -obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o -obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o +obj-$(CONFIG_SND_SOC_INTEL_SST) += common/ # Platform Support -snd-soc-sst-haswell-pcm-objs := \ - sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o -snd-soc-sst-baytrail-pcm-objs := \ - sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o - -obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o -obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o +obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += atom/ # Machine support -snd-soc-sst-haswell-objs := haswell.o -snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o -snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o -snd-soc-sst-broadwell-objs := broadwell.o -snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o -snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o -snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o - -obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o -obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o -obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o -obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o -obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o -obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o -obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o - -# DSP driver -obj-$(CONFIG_SND_SST_IPC) += sst/ +obj-$(CONFIG_SND_SOC_INTEL_SST) += boards/ diff --git a/sound/soc/intel/atom/Makefile b/sound/soc/intel/atom/Makefile new file mode 100644 index 000000000000..ce8074fa6d66 --- /dev/null +++ b/sound/soc/intel/atom/Makefile @@ -0,0 +1,7 @@ +snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o \ + sst-mfld-platform-compress.o sst-atom-controls.o + +obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o + +# DSP driver +obj-$(CONFIG_SND_SST_IPC) += sst/ diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index 90aa5c0476f3..90aa5c0476f3 100644 --- a/sound/soc/intel/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h index daecc58f28af..daecc58f28af 100644 --- a/sound/soc/intel/sst-atom-controls.h +++ b/sound/soc/intel/atom/sst-atom-controls.h diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/atom/sst-mfld-dsp.h index 4257263157cd..4257263157cd 100644 --- a/sound/soc/intel/sst-mfld-dsp.h +++ b/sound/soc/intel/atom/sst-mfld-dsp.h diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c index 395168986462..395168986462 100644 --- a/sound/soc/intel/sst-mfld-platform-compress.c +++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index 7523cbef8780..2fbaf2c75d17 100644 --- a/sound/soc/intel/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -594,11 +594,13 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, ret_val = stream->ops->stream_drop(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: dev_dbg(rtd->dev, "sst: in pause\n"); status = SST_PLATFORM_PAUSED; ret_val = stream->ops->stream_pause(sst->dev, str_id); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: dev_dbg(rtd->dev, "sst: in pause release\n"); status = SST_PLATFORM_RUNNING; ret_val = stream->ops->stream_pause_release(sst->dev, str_id); @@ -665,6 +667,9 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd) static int sst_soc_probe(struct snd_soc_platform *platform) { + struct sst_data *drv = dev_get_drvdata(platform->dev); + + drv->soc_card = platform->component.card; return sst_dsp_init_v2_dpcm(platform); } @@ -727,9 +732,64 @@ static int sst_platform_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP + +static int sst_soc_prepare(struct device *dev) +{ + struct sst_data *drv = dev_get_drvdata(dev); + int i; + + /* suspend all pcms first */ + snd_soc_suspend(drv->soc_card->dev); + snd_soc_poweroff(drv->soc_card->dev); + + /* set the SSPs to idle */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + send_ssp_cmd(dai, dai->name, 0); + sst_handle_vb_timer(dai, false); + } + } + + return 0; +} + +static void sst_soc_complete(struct device *dev) +{ + struct sst_data *drv = dev_get_drvdata(dev); + int i; + + /* restart SSPs */ + for (i = 0; i < drv->soc_card->num_rtd; i++) { + struct snd_soc_dai *dai = drv->soc_card->rtd[i].cpu_dai; + + if (dai->active) { + sst_handle_vb_timer(dai, true); + send_ssp_cmd(dai, dai->name, 1); + } + } + snd_soc_resume(drv->soc_card->dev); +} + +#else + +#define sst_soc_prepare NULL +#define sst_soc_complete NULL + +#endif + + +static const struct dev_pm_ops sst_platform_pm = { + .prepare = sst_soc_prepare, + .complete = sst_soc_complete, +}; + static struct platform_driver sst_platform_driver = { .driver = { .name = "sst-mfld-platform", + .pm = &sst_platform_pm, }, .probe = sst_platform_probe, .remove = sst_platform_remove, diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index 79c8d1246a8f..9094314be2b0 100644 --- a/sound/soc/intel/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -174,6 +174,7 @@ struct sst_data { struct sst_platform_data *pdata; struct snd_sst_bytes_v2 *byte_stream; struct mutex lock; + struct snd_soc_card *soc_card; }; int sst_register_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst); diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/atom/sst/Makefile index fd21726361b5..fd21726361b5 100644 --- a/sound/soc/intel/sst/Makefile +++ b/sound/soc/intel/atom/sst/Makefile diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 11c578651c1c..96c2e420cce6 100644 --- a/sound/soc/intel/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>"); MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>"); @@ -423,23 +423,135 @@ static int intel_sst_runtime_suspend(struct device *dev) return ret; } -static int intel_sst_runtime_resume(struct device *dev) +static int intel_sst_suspend(struct device *dev) { - int ret = 0; struct intel_sst_drv *ctx = dev_get_drvdata(dev); + struct sst_fw_save *fw_save; + int i, ret = 0; - if (ctx->sst_state == SST_RESET) { - ret = sst_load_fw(ctx); - if (ret) { - dev_err(dev, "FW download fail %d\n", ret); - sst_set_fw_state_locked(ctx, SST_RESET); + /* check first if we are already in SW reset */ + if (ctx->sst_state == SST_RESET) + return 0; + + /* + * check if any stream is active and running + * they should already by suspend by soc_suspend + */ + for (i = 1; i <= ctx->info.max_streams; i++) { + struct stream_info *stream = &ctx->streams[i]; + + if (stream->status == STREAM_RUNNING) { + dev_err(dev, "stream %d is running, cant susupend, abort\n", i); + return -EBUSY; } } + synchronize_irq(ctx->irq_num); + flush_workqueue(ctx->post_msg_wq); + + /* Move the SST state to Reset */ + sst_set_fw_state_locked(ctx, SST_RESET); + + /* tell DSP we are suspending */ + if (ctx->ops->save_dsp_context(ctx)) + return -EBUSY; + + /* save the memories */ + fw_save = kzalloc(sizeof(*fw_save), GFP_KERNEL); + if (!fw_save) + return -ENOMEM; + fw_save->iram = kzalloc(ctx->iram_end - ctx->iram_base, GFP_KERNEL); + if (!fw_save->iram) { + ret = -ENOMEM; + goto iram; + } + fw_save->dram = kzalloc(ctx->dram_end - ctx->dram_base, GFP_KERNEL); + if (!fw_save->dram) { + ret = -ENOMEM; + goto dram; + } + fw_save->sram = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); + if (!fw_save->sram) { + ret = -ENOMEM; + goto sram; + } + + fw_save->ddr = kzalloc(ctx->ddr_end - ctx->ddr_base, GFP_KERNEL); + if (!fw_save->ddr) { + ret = -ENOMEM; + goto ddr; + } + + memcpy32_fromio(fw_save->iram, ctx->iram, ctx->iram_end - ctx->iram_base); + memcpy32_fromio(fw_save->dram, ctx->dram, ctx->dram_end - ctx->dram_base); + memcpy32_fromio(fw_save->sram, ctx->mailbox, SST_MAILBOX_SIZE); + memcpy32_fromio(fw_save->ddr, ctx->ddr, ctx->ddr_end - ctx->ddr_base); + + ctx->fw_save = fw_save; + ctx->ops->reset(ctx); + return 0; +ddr: + kfree(fw_save->sram); +sram: + kfree(fw_save->dram); +dram: + kfree(fw_save->iram); +iram: + kfree(fw_save); + return ret; +} + +static int intel_sst_resume(struct device *dev) +{ + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + struct sst_fw_save *fw_save = ctx->fw_save; + int ret = 0; + struct sst_block *block; + + if (!fw_save) + return 0; + + sst_set_fw_state_locked(ctx, SST_FW_LOADING); + + /* we have to restore the memory saved */ + ctx->ops->reset(ctx); + + ctx->fw_save = NULL; + + memcpy32_toio(ctx->iram, fw_save->iram, ctx->iram_end - ctx->iram_base); + memcpy32_toio(ctx->dram, fw_save->dram, ctx->dram_end - ctx->dram_base); + memcpy32_toio(ctx->mailbox, fw_save->sram, SST_MAILBOX_SIZE); + memcpy32_toio(ctx->ddr, fw_save->ddr, ctx->ddr_end - ctx->ddr_base); + + kfree(fw_save->sram); + kfree(fw_save->dram); + kfree(fw_save->iram); + kfree(fw_save->ddr); + kfree(fw_save); + + block = sst_create_block(ctx, 0, FW_DWNL_ID); + if (block == NULL) + return -ENOMEM; + + + /* start and wait for ack */ + ctx->ops->start(ctx); + ret = sst_wait_timeout(ctx, block); + if (ret) { + dev_err(ctx->dev, "fw download failed %d\n", ret); + /* FW download failed due to timeout */ + ret = -EBUSY; + + } else { + sst_set_fw_state_locked(ctx, SST_FW_RUNNING); + } + + sst_free_block(ctx, block); return ret; } const struct dev_pm_ops intel_sst_pm = { + .suspend = intel_sst_suspend, + .resume = intel_sst_resume, .runtime_suspend = intel_sst_runtime_suspend, - .runtime_resume = intel_sst_runtime_resume, }; EXPORT_SYMBOL_GPL(intel_sst_pm); diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 562bc483d6b7..3f493862e98d 100644 --- a/sound/soc/intel/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -337,6 +337,13 @@ struct sst_shim_regs64 { u64 csr2; }; +struct sst_fw_save { + void *iram; + void *dram; + void *sram; + void *ddr; +}; + /** * struct intel_sst_drv - driver ops * @@ -428,6 +435,8 @@ struct intel_sst_drv { * persistent till worker thread gets called */ char firmware_name[FW_NAME_SIZE]; + + struct sst_fw_save *fw_save; }; /* misc definitions */ @@ -544,4 +553,7 @@ int sst_alloc_drv_context(struct intel_sst_drv **ctx, int sst_context_init(struct intel_sst_drv *ctx); void sst_context_cleanup(struct intel_sst_drv *ctx); void sst_configure_runtime_pm(struct intel_sst_drv *ctx); +void memcpy32_toio(void __iomem *dst, const void *src, int count); +void memcpy32_fromio(void *dst, const void __iomem *src, int count); + #endif diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index b782dfdcdbba..05f693083911 100644 --- a/sound/soc/intel/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -39,7 +39,7 @@ #include <acpi/actypes.h> #include <acpi/acpi_bus.h> #include "../sst-mfld-platform.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" #include "sst.h" struct sst_machines { @@ -309,7 +309,7 @@ static int sst_acpi_probe(struct platform_device *pdev) ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64), GFP_KERNEL); if (!ctx->shim_regs64) { - return -ENOMEM; + ret = -ENOMEM; goto do_sst_cleanup; } diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 5f75ef3cdd22..7b50a9d17ec1 100644 --- a/sound/soc/intel/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" @@ -138,12 +138,36 @@ int sst_get_stream(struct intel_sst_drv *ctx, static int sst_power_control(struct device *dev, bool state) { struct intel_sst_drv *ctx = dev_get_drvdata(dev); - - dev_dbg(ctx->dev, "state:%d", state); - if (state == true) - return pm_runtime_get_sync(dev); - else + int ret = 0; + int usage_count = 0; + +#ifdef CONFIG_PM + usage_count = atomic_read(&dev->power.usage_count); +#else + usage_count = 1; +#endif + + if (state == true) { + ret = pm_runtime_get_sync(dev); + + dev_dbg(ctx->dev, "Enable: pm usage count: %d\n", usage_count); + if (ret < 0) { + dev_err(ctx->dev, "Runtime get failed with err: %d\n", ret); + return ret; + } + if ((ctx->sst_state == SST_RESET) && (usage_count == 1)) { + ret = sst_load_fw(ctx); + if (ret) { + dev_err(dev, "FW download fail %d\n", ret); + sst_set_fw_state_locked(ctx, SST_RESET); + ret = sst_pm_runtime_put(ctx); + } + } + } else { + dev_dbg(ctx->dev, "Disable: pm usage count: %d\n", usage_count); return sst_pm_runtime_put(ctx); + } + return ret; } /* @@ -357,7 +381,7 @@ static int sst_cdev_tstamp(struct device *dev, unsigned int str_id, tstamp->copied_total = fw_tstamp.ring_buffer_counter; tstamp->pcm_frames = fw_tstamp.frames_decoded; tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter, - (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24))); + (u64)stream->num_ch * SST_GET_BYTES_PER_SAMPLE(24)); tstamp->sampling_rate = fw_tstamp.sampling_frequency; dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames); @@ -572,6 +596,35 @@ static int sst_stream_drop(struct device *dev, int str_id) return sst_drop_stream(ctx, str_id); } +static int sst_stream_pause(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + + return sst_pause_stream(ctx, str_id); +} + +static int sst_stream_resume(struct device *dev, int str_id) +{ + struct stream_info *str_info; + struct intel_sst_drv *ctx = dev_get_drvdata(dev); + + if (ctx->sst_state != SST_FW_RUNNING) + return 0; + + str_info = get_stream_info(ctx, str_id); + if (!str_info) + return -EINVAL; + return sst_resume_stream(ctx, str_id); +} + static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info) { int str_id = 0; @@ -633,6 +686,8 @@ static struct sst_ops pcm_ops = { .stream_init = sst_stream_init, .stream_start = sst_stream_start, .stream_drop = sst_stream_drop, + .stream_pause = sst_stream_pause, + .stream_pause_release = sst_stream_resume, .stream_read_tstamp = sst_read_timestamp, .send_byte_stream = sst_send_byte_stream, .close = sst_close_pcm_stream, diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index 484e60978477..5a278618466c 100644 --- a/sound/soc/intel/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -32,7 +32,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" struct sst_block *sst_create_block(struct intel_sst_drv *ctx, u32 msg_id, u32 drv_id) diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 7888cd707853..33917146d9c4 100644 --- a/sound/soc/intel/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -37,9 +37,17 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" -static inline void memcpy32_toio(void __iomem *dst, const void *src, int count) +void memcpy32_toio(void __iomem *dst, const void *src, int count) +{ + /* __iowrite32_copy uses 32-bit count values so divide by 4 for + * right count in words + */ + __iowrite32_copy(dst, src, count/4); +} + +void memcpy32_fromio(void *dst, const void __iomem *src, int count) { /* __iowrite32_copy uses 32-bit count values so divide by 4 for * right count in words diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c index 3a0b3bf0af97..3a0b3bf0af97 100644 --- a/sound/soc/intel/sst/sst_pci.c +++ b/sound/soc/intel/atom/sst/sst_pci.c diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 4b7720864492..adb32fefd693 100644 --- a/sound/soc/intel/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -34,7 +34,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" int sst_shim_write(void __iomem *addr, int offset, int value) { @@ -111,30 +111,6 @@ int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, } -unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr) -{ - unsigned long long val = 0; - - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - val = sst_shim_read64(sst->shim, addr); - break; - } - return val; -} - -void write_shim_data(struct intel_sst_drv *sst, int addr, - unsigned long long data) -{ - switch (sst->dev_id) { - case SST_MRFLD_PCI_ID: - case SST_BYT_ACPI_ID: - sst_shim_write64(sst->shim, addr, (u64) data); - break; - } -} - /* * sst_wait_timeout - wait on event for timeout * diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index dae2a41997aa..a74c64c7053c 100644 --- a/sound/soc/intel/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -31,7 +31,7 @@ #include <asm/platform_sst_audio.h> #include "../sst-mfld-platform.h" #include "sst.h" -#include "../sst-dsp.h" +#include "../../common/sst-dsp.h" int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) { diff --git a/sound/soc/intel/baytrail/Makefile b/sound/soc/intel/baytrail/Makefile new file mode 100644 index 000000000000..488408cadf6d --- /dev/null +++ b/sound/soc/intel/baytrail/Makefile @@ -0,0 +1,4 @@ +snd-soc-sst-baytrail-pcm-objs := \ + sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/baytrail/sst-baytrail-dsp.c index 5a9e56700f31..01d023cc05dd 100644 --- a/sound/soc/intel/sst-baytrail-dsp.c +++ b/sound/soc/intel/baytrail/sst-baytrail-dsp.c @@ -22,8 +22,8 @@ #include <linux/platform_device.h> #include <linux/firmware.h> -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" #include "sst-baytrail-ipc.h" #define SST_BYT_FW_SIGNATURE_SIZE 4 diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c index b4ad98c43e5c..1efb33b36303 100644 --- a/sound/soc/intel/sst-baytrail-ipc.c +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c @@ -29,8 +29,9 @@ #include <asm/div64.h> #include "sst-baytrail-ipc.h" -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-ipc.h" /* IPC message timeout */ #define IPC_TIMEOUT_MSECS 300 @@ -142,23 +143,6 @@ struct sst_byt_fw_init { u8 debug_info; } __packed; -/* driver internal IPC message structure */ -struct ipc_message { - struct list_head list; - u64 header; - - /* direction wrt host CPU */ - char tx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; - size_t tx_size; - char rx_data[SST_BYT_IPC_MAX_PAYLOAD_SIZE]; - size_t rx_size; - - wait_queue_head_t waitq; - bool complete; - bool wait; - int errno; -}; - struct sst_byt_stream; struct sst_byt; @@ -195,14 +179,7 @@ struct sst_byt { struct sst_fw *fw; /* IPC messaging */ - struct list_head tx_list; - struct list_head rx_list; - struct list_head empty_list; - wait_queue_head_t wait_txq; - struct task_struct *tx_thread; - struct kthread_worker kworker; - struct kthread_work kwork; - struct ipc_message *msg; + struct sst_generic_ipc ipc; }; static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) @@ -246,209 +223,6 @@ static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, return NULL; } -static void sst_byt_ipc_shim_dbg(struct sst_byt *byt, const char *text) -{ - struct sst_dsp *sst = byt->dsp; - u64 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); - - dev_err(byt->dev, - "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", - text, ipcx, isr, ipcd, imrx); -} - -/* locks held by caller */ -static struct ipc_message *sst_byt_msg_get_empty(struct sst_byt *byt) -{ - struct ipc_message *msg = NULL; - - if (!list_empty(&byt->empty_list)) { - msg = list_first_entry(&byt->empty_list, - struct ipc_message, list); - list_del(&msg->list); - } - - return msg; -} - -static void sst_byt_ipc_tx_msgs(struct kthread_work *work) -{ - struct sst_byt *byt = - container_of(work, struct sst_byt, kwork); - struct ipc_message *msg; - u64 ipcx; - unsigned long flags; - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - if (list_empty(&byt->tx_list)) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return; - } - - /* if the DSP is busy we will TX messages after IRQ */ - ipcx = sst_dsp_shim_read64_unlocked(byt->dsp, SST_IPCX); - if (ipcx & SST_BYT_IPCX_BUSY) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return; - } - - msg = list_first_entry(&byt->tx_list, struct ipc_message, list); - - list_move(&msg->list, &byt->rx_list); - - /* send the message */ - if (msg->header & IPC_HEADER_LARGE(true)) - sst_dsp_outbox_write(byt->dsp, msg->tx_data, msg->tx_size); - sst_dsp_shim_write64_unlocked(byt->dsp, SST_IPCX, msg->header); - - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); -} - -static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt, - struct ipc_message *msg) -{ - msg->complete = true; - - if (!msg->wait) - list_add_tail(&msg->list, &byt->empty_list); - else - wake_up(&msg->waitq); -} - -static void sst_byt_drop_all(struct sst_byt *byt) -{ - struct ipc_message *msg, *tmp; - unsigned long flags; - - /* drop all TX and Rx messages before we stall + reset DSP */ - spin_lock_irqsave(&byt->dsp->spinlock, flags); - list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) { - list_move(&msg->list, &byt->empty_list); - } - - list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) { - list_move(&msg->list, &byt->empty_list); - } - - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); -} - -static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg, - void *rx_data) -{ - unsigned long flags; - int ret; - - /* wait for DSP completion */ - ret = wait_event_timeout(msg->waitq, msg->complete, - msecs_to_jiffies(IPC_TIMEOUT_MSECS)); - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - if (ret == 0) { - list_del(&msg->list); - sst_byt_ipc_shim_dbg(byt, "message timeout"); - - ret = -ETIMEDOUT; - } else { - - /* copy the data returned from DSP */ - if (msg->rx_size) - memcpy(rx_data, msg->rx_data, msg->rx_size); - ret = msg->errno; - } - - list_add_tail(&msg->list, &byt->empty_list); - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return ret; -} - -static int sst_byt_ipc_tx_message(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes, - void *rx_data, size_t rx_bytes, int wait) -{ - unsigned long flags; - struct ipc_message *msg; - - spin_lock_irqsave(&byt->dsp->spinlock, flags); - - msg = sst_byt_msg_get_empty(byt); - if (msg == NULL) { - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - return -EBUSY; - } - - msg->header = header; - msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; - msg->wait = wait; - msg->errno = 0; - msg->complete = false; - - if (tx_bytes) { - /* msg content = lower 32-bit of the header + data */ - *(u32 *)msg->tx_data = (u32)(header & (u32)-1); - memcpy(msg->tx_data + sizeof(u32), tx_data, tx_bytes); - msg->tx_size += sizeof(u32); - } - - list_add_tail(&msg->list, &byt->tx_list); - spin_unlock_irqrestore(&byt->dsp->spinlock, flags); - - queue_kthread_work(&byt->kworker, &byt->kwork); - - if (wait) - return sst_byt_tx_wait_done(byt, msg, rx_data); - else - return 0; -} - -static inline int sst_byt_ipc_tx_msg_wait(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes, - void *rx_data, size_t rx_bytes) -{ - return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, - rx_data, rx_bytes, 1); -} - -static inline int sst_byt_ipc_tx_msg_nowait(struct sst_byt *byt, u64 header, - void *tx_data, size_t tx_bytes) -{ - return sst_byt_ipc_tx_message(byt, header, tx_data, tx_bytes, - NULL, 0, 0); -} - -static struct ipc_message *sst_byt_reply_find_msg(struct sst_byt *byt, - u64 header) -{ - struct ipc_message *msg = NULL, *_msg; - u64 mask; - - /* match reply to message sent based on msg and stream IDs */ - mask = IPC_HEADER_MSG_ID_MASK | - IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; - header &= mask; - - if (list_empty(&byt->rx_list)) { - dev_err(byt->dev, - "ipc: rx list is empty but received 0x%llx\n", header); - goto out; - } - - list_for_each_entry(_msg, &byt->rx_list, list) { - if ((_msg->header & mask) == header) { - msg = _msg; - break; - } - } - -out: - return msg; -} - static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) { struct sst_byt_stream *stream; @@ -477,7 +251,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) { struct ipc_message *msg; - msg = sst_byt_reply_find_msg(byt, header); + msg = sst_ipc_reply_find_msg(&byt->ipc, header); if (msg == NULL) return 1; @@ -491,7 +265,7 @@ static int sst_byt_process_reply(struct sst_byt *byt, u64 header) list_del(&msg->list); /* wake up */ - sst_byt_tx_msg_reply_complete(byt, msg); + sst_ipc_tx_msg_reply_complete(&byt->ipc, msg); return 1; } @@ -538,6 +312,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) { struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_byt *byt = sst_dsp_get_thread_context(sst); + struct sst_generic_ipc *ipc = &byt->ipc; u64 header; unsigned long flags; @@ -569,7 +344,7 @@ static irqreturn_t sst_byt_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&byt->kworker, &byt->kwork); + queue_kthread_work(&ipc->kworker, &ipc->kwork); return IRQ_HANDLED; } @@ -656,7 +431,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) header = sst_byt_header(IPC_IA_ALLOC_STREAM, sizeof(*str_req) + sizeof(u32), true, stream->str_id); - ret = sst_byt_ipc_tx_msg_wait(byt, header, str_req, sizeof(*str_req), + ret = sst_ipc_tx_message_wait(&byt->ipc, header, str_req, + sizeof(*str_req), reply, sizeof(*reply)); if (ret < 0) { dev_err(byt->dev, "ipc: error stream commit failed\n"); @@ -679,7 +455,7 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) goto out; header = sst_byt_header(IPC_IA_FREE_STREAM, 0, false, stream->str_id); - ret = sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); + ret = sst_ipc_tx_message_wait(&byt->ipc, header, NULL, 0, NULL, 0); if (ret < 0) { dev_err(byt->dev, "ipc: free stream %d failed\n", stream->str_id); @@ -703,9 +479,11 @@ static int sst_byt_stream_operations(struct sst_byt *byt, int type, header = sst_byt_header(type, 0, false, stream_id); if (wait) - return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0); + return sst_ipc_tx_message_wait(&byt->ipc, header, NULL, + 0, NULL, 0); else - return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0); + return sst_ipc_tx_message_nowait(&byt->ipc, header, + NULL, 0); } /* stream ALSA trigger operations */ @@ -725,7 +503,7 @@ int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, tx_msg = &start_stream; size = sizeof(start_stream); - ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); + ret = sst_ipc_tx_message_nowait(&byt->ipc, header, tx_msg, size); if (ret < 0) dev_err(byt->dev, "ipc: error failed to start stream %d\n", stream->str_id); @@ -790,23 +568,6 @@ int sst_byt_get_dsp_position(struct sst_byt *byt, return do_div(fw_tstamp.ring_buffer_counter, buffer_size); } -static int msg_empty_list_init(struct sst_byt *byt) -{ - struct ipc_message *msg; - int i; - - byt->msg = kzalloc(sizeof(*msg) * IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (byt->msg == NULL) - return -ENOMEM; - - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - init_waitqueue_head(&byt->msg[i].waitq); - list_add(&byt->msg[i].list, &byt->empty_list); - } - - return 0; -} - struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) { return byt->dsp; @@ -823,7 +584,7 @@ int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) dev_dbg(byt->dev, "dsp reset\n"); sst_dsp_reset(byt->dsp); - sst_byt_drop_all(byt); + sst_ipc_drop_all(&byt->ipc); dev_dbg(byt->dev, "dsp in reset\n"); dev_dbg(byt->dev, "free all blocks and unload fw\n"); @@ -876,9 +637,52 @@ int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) } EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); +static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) +{ + if (msg->header & IPC_HEADER_LARGE(true)) + sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + + sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->header); +} + +static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text) +{ + struct sst_dsp *sst = ipc->dsp; + u64 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); + + dev_err(ipc->dev, + "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", + text, ipcx, isr, ipcd, imrx); +} + +static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data, + size_t tx_size) +{ + /* msg content = lower 32-bit of the header + data */ + *(u32 *)msg->tx_data = (u32)(msg->header & (u32)-1); + memcpy(msg->tx_data + sizeof(u32), tx_data, tx_size); + msg->tx_size += sizeof(u32); +} + +static u64 byt_reply_msg_match(u64 header, u64 *mask) +{ + /* match reply to message sent based on msg and stream IDs */ + *mask = IPC_HEADER_MSG_ID_MASK | + IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; + header &= *mask; + + return header; +} + int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_byt *byt; + struct sst_generic_ipc *ipc; struct sst_fw *byt_sst_fw; struct sst_byt_fw_init init; int err; @@ -889,39 +693,30 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) if (byt == NULL) return -ENOMEM; - byt->dev = dev; - INIT_LIST_HEAD(&byt->stream_list); - INIT_LIST_HEAD(&byt->tx_list); - INIT_LIST_HEAD(&byt->rx_list); - INIT_LIST_HEAD(&byt->empty_list); - init_waitqueue_head(&byt->boot_wait); - init_waitqueue_head(&byt->wait_txq); + ipc = &byt->ipc; + ipc->dev = dev; + ipc->ops.tx_msg = byt_tx_msg; + ipc->ops.shim_dbg = byt_shim_dbg; + ipc->ops.tx_data_copy = byt_tx_data_copy; + ipc->ops.reply_msg_match = byt_reply_msg_match; - err = msg_empty_list_init(byt); - if (err < 0) - return -ENOMEM; - - /* start the IPC message thread */ - init_kthread_worker(&byt->kworker); - byt->tx_thread = kthread_run(kthread_worker_fn, - &byt->kworker, "%s", - dev_name(byt->dev)); - if (IS_ERR(byt->tx_thread)) { - err = PTR_ERR(byt->tx_thread); - dev_err(byt->dev, "error failed to create message TX task\n"); - goto err_free_msg; - } - init_kthread_work(&byt->kwork, sst_byt_ipc_tx_msgs); + err = sst_ipc_init(ipc); + if (err != 0) + goto ipc_init_err; + INIT_LIST_HEAD(&byt->stream_list); + init_waitqueue_head(&byt->boot_wait); byt_dev.thread_context = byt; /* init SST shim */ byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); if (byt->dsp == NULL) { err = -ENODEV; - goto dsp_err; + goto dsp_new_err; } + ipc->dsp = byt->dsp; + /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(byt->dsp); @@ -961,10 +756,10 @@ boot_err: sst_fw_free(byt_sst_fw); fw_err: sst_dsp_free(byt->dsp); -dsp_err: - kthread_stop(byt->tx_thread); -err_free_msg: - kfree(byt->msg); +dsp_new_err: + sst_ipc_fini(ipc); +ipc_init_err: + kfree(byt); return err; } @@ -977,7 +772,6 @@ void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) sst_dsp_reset(byt->dsp); sst_fw_free_all(byt->dsp); sst_dsp_free(byt->dsp); - kthread_stop(byt->tx_thread); - kfree(byt->msg); + sst_ipc_fini(&byt->ipc); } EXPORT_SYMBOL_GPL(sst_byt_dsp_free); diff --git a/sound/soc/intel/sst-baytrail-ipc.h b/sound/soc/intel/baytrail/sst-baytrail-ipc.h index 8faff6dcf25d..8faff6dcf25d 100644 --- a/sound/soc/intel/sst-baytrail-ipc.h +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.h diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c index 224c49c9f135..79547bec558b 100644 --- a/sound/soc/intel/sst-baytrail-pcm.c +++ b/sound/soc/intel/baytrail/sst-baytrail-pcm.c @@ -20,8 +20,8 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include "sst-baytrail-ipc.h" -#include "sst-dsp-priv.h" -#include "sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-dsp.h" #define BYT_PCM_COUNT 2 diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile new file mode 100644 index 000000000000..f8237f0044eb --- /dev/null +++ b/sound/soc/intel/boards/Makefile @@ -0,0 +1,15 @@ +snd-soc-sst-haswell-objs := haswell.o +snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o +snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o +snd-soc-sst-broadwell-objs := broadwell.o +snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o +snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o +snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o +obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o +obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o +obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/boards/broadwell.c index 9cf7d01479ad..8bafaf6ceab1 100644 --- a/sound/soc/intel/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -22,10 +22,10 @@ #include <sound/jack.h> #include <sound/pcm_params.h> -#include "sst-dsp.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" -#include "../codecs/rt286.h" +#include "../../codecs/rt286.h" static struct snd_soc_jack broadwell_headset; /* Headset jack detection DAPM pins */ @@ -80,15 +80,9 @@ static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; int ret = 0; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset); - - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&broadwell_headset, - ARRAY_SIZE(broadwell_headset_pins), - broadwell_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset, + broadwell_headset_pins, ARRAY_SIZE(broadwell_headset_pins)); if (ret) return ret; @@ -110,9 +104,7 @@ static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } @@ -227,6 +219,32 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { }, }; +static int broadwell_suspend(struct snd_soc_card *card){ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-INT343A:00")) { + dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); + rt286_mic_detect(codec, NULL); + break; + } + } + return 0; +} + +static int broadwell_resume(struct snd_soc_card *card){ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-INT343A:00")) { + dev_dbg(codec->dev, "enabling jack detect for resume.\n"); + rt286_mic_detect(codec, &broadwell_headset); + break; + } + } + return 0; +} + /* broadwell audio machine driver for WPT + RT286S */ static struct snd_soc_card broadwell_rt286 = { .name = "broadwell-rt286", @@ -240,6 +258,8 @@ static struct snd_soc_card broadwell_rt286 = { .dapm_routes = broadwell_rt286_map, .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map), .fully_routed = true, + .suspend_pre = broadwell_suspend, + .resume_post = broadwell_resume, }; static int broadwell_audio_probe(struct platform_device *pdev) diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c index 9832afe7d22c..7ab8cc9fbfd5 100644 --- a/sound/soc/intel/byt-max98090.c +++ b/sound/soc/intel/boards/byt-max98090.c @@ -24,7 +24,7 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/max98090.h" +#include "../../codecs/max98090.h" struct byt_max98090_private { struct snd_soc_jack jack; @@ -84,7 +84,6 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = { static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) { int ret; - struct snd_soc_codec *codec = runtime->codec; struct snd_soc_card *card = runtime->card; struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card); struct snd_soc_jack *jack = &drv->jack; @@ -100,13 +99,9 @@ static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) } /* Enable jack detection */ - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET, jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_LINEOUT | SND_JACK_HEADSET, jack, + hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c index 354eaad886e1..ae89b9b966d9 100644 --- a/sound/soc/intel/byt-rt5640.c +++ b/sound/soc/intel/boards/byt-rt5640.c @@ -23,9 +23,9 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5640.h" +#include "../../codecs/rt5640.h" -#include "sst-dsp.h" +#include "../common/sst-dsp.h" static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 59308629043e..7f55d59024a8 100644 --- a/sound/soc/intel/bytcr_dpcm_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -26,8 +26,8 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include "../codecs/rt5640.h" -#include "sst-atom-controls.h" +#include "../../codecs/rt5640.h" +#include "../atom/sst-atom-controls.h" static const struct snd_soc_dapm_widget byt_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone", NULL), @@ -113,9 +113,7 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index bd29617a9ab9..20a28b22e30f 100644 --- a/sound/soc/intel/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -27,8 +27,8 @@ #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/jack.h> -#include "../codecs/rt5645.h" -#include "sst-atom-controls.h" +#include "../../codecs/rt5645.h" +#include "../atom/sst-atom-controls.h" #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5645-aif1" @@ -169,17 +169,17 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) return ret; } - ret = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, - &ctx->hp_jack); + ret = snd_soc_card_jack_new(runtime->card, "Headphone Jack", + SND_JACK_HEADPHONE, &ctx->hp_jack, + NULL, 0); if (ret) { dev_err(runtime->dev, "HP jack creation failed %d\n", ret); return ret; } - ret = snd_soc_jack_new(codec, "Mic Jack", - SND_JACK_MICROPHONE, - &ctx->mic_jack); + ret = snd_soc_card_jack_new(runtime->card, "Mic Jack", + SND_JACK_MICROPHONE, &ctx->mic_jack, + NULL, 0); if (ret) { dev_err(runtime->dev, "Mic jack creation failed %d\n", ret); return ret; @@ -203,9 +203,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index ff016621583a..2c9cc5be439e 100644 --- a/sound/soc/intel/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -22,13 +22,28 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include "../codecs/rt5670.h" -#include "sst-atom-controls.h" +#include <sound/jack.h> +#include "../../codecs/rt5670.h" +#include "../atom/sst-atom-controls.h" /* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ #define CHT_PLAT_CLK_3_HZ 19200000 #define CHT_CODEC_DAI "rt5670-aif1" +static struct snd_soc_jack cht_bsw_headset; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin cht_bsw_headset_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, +}; + static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card) { int i; @@ -50,6 +65,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, struct snd_soc_dapm_context *dapm = w->dapm; struct snd_soc_card *card = dapm->card; struct snd_soc_dai *codec_dai; + int ret; codec_dai = cht_get_codec_dai(card); if (!codec_dai) { @@ -57,17 +73,31 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w, return -EIO; } - if (!SND_SOC_DAPM_EVENT_OFF(event)) - return 0; - - /* Set codec sysclk source to its internal clock because codec PLL will - * be off when idle and MCLK will also be off by ACPI when codec is - * runtime suspended. Codec needs clock for jack detection and button - * press. - */ - snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, - 0, SND_SOC_CLOCK_IN); - + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* set codec PLL source to the 19.2MHz platform clock (MCLK) */ + ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, + CHT_PLAT_CLK_3_HZ, 48000 * 512); + if (ret < 0) { + dev_err(card->dev, "can't set codec pll: %d\n", ret); + return ret; + } + + /* set codec sysclk source to PLL */ + ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, + 48000 * 512, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "can't set codec sysclk: %d\n", ret); + return ret; + } + } else { + /* Set codec sysclk source to its internal clock because codec + * PLL will be off when idle and MCLK will also be off by ACPI + * when codec is runtime suspended. Codec needs clock for jack + * detection and button press. + */ + snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, + 48000 * 512, SND_SOC_CLOCK_IN); + } return 0; } @@ -77,7 +107,8 @@ static const struct snd_soc_dapm_widget cht_dapm_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), SND_SOC_DAPM_SPK("Ext Spk", NULL), SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, - platform_clock_control, SND_SOC_DAPM_POST_PMD), + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), }; static const struct snd_soc_dapm_route cht_audio_map[] = { @@ -162,6 +193,15 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) | RT5670_AD_MONO_L_FILTER | RT5670_AD_MONO_R_FILTER, RT5670_CLK_SEL_I2S1_ASRC); + + ret = snd_soc_card_jack_new(runtime->card, "Headset", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2, &cht_bsw_headset, + cht_bsw_headset_pins, ARRAY_SIZE(cht_bsw_headset_pins)); + if (ret) + return ret; + + rt5670_set_jack_detect(codec, &cht_bsw_headset); return 0; } @@ -178,9 +218,7 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP2 to 24-bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S24_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S24_LE); return 0; } @@ -217,7 +255,7 @@ static struct snd_soc_dai_link cht_dailink[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", .platform_name = "sst-mfld-platform", - .ignore_suspend = 1, + .nonatomic = true, .dynamic = 1, .dpcm_playback = 1, .dpcm_capture = 1, @@ -240,19 +278,48 @@ static struct snd_soc_dai_link cht_dailink[] = { .cpu_dai_name = "ssp2-port", .platform_name = "sst-mfld-platform", .no_pcm = 1, + .nonatomic = true, .codec_dai_name = "rt5670-aif1", .codec_name = "i2c-10EC5670:00", .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_CBS_CFS, .init = cht_codec_init, .be_hw_params_fixup = cht_codec_fixup, - .ignore_suspend = 1, .dpcm_playback = 1, .dpcm_capture = 1, .ops = &cht_be_ssp2_ops, }, }; +static int cht_suspend_pre(struct snd_soc_card *card) +{ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { + dev_dbg(codec->dev, "disabling jack detect before going to suspend.\n"); + rt5670_jack_suspend(codec); + break; + } + } + return 0; +} + +static int cht_resume_post(struct snd_soc_card *card) +{ + struct snd_soc_codec *codec; + + list_for_each_entry(codec, &card->codec_dev_list, card_list) { + if (!strcmp(codec->component.name, "i2c-10EC5670:00")) { + dev_dbg(codec->dev, "enabling jack detect for resume.\n"); + rt5670_jack_resume(codec); + break; + } + } + + return 0; +} + /* SoC card */ static struct snd_soc_card snd_soc_card_cht = { .name = "cherrytrailcraudio", @@ -264,6 +331,8 @@ static struct snd_soc_card snd_soc_card_cht = { .num_dapm_routes = ARRAY_SIZE(cht_audio_map), .controls = cht_mc_controls, .num_controls = ARRAY_SIZE(cht_mc_controls), + .suspend_pre = cht_suspend_pre, + .resume_post = cht_resume_post, }; static int snd_cht_mc_probe(struct platform_device *pdev) @@ -285,7 +354,6 @@ static int snd_cht_mc_probe(struct platform_device *pdev) static struct platform_driver snd_cht_mc_driver = { .driver = { .name = "cht-bsw-rt5672", - .pm = &snd_soc_pm_ops, }, .probe = snd_cht_mc_probe, }; diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/boards/haswell.c index 35edf51a52aa..22558572cb9c 100644 --- a/sound/soc/intel/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -21,10 +21,10 @@ #include <sound/soc.h> #include <sound/pcm_params.h> -#include "sst-dsp.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" -#include "../codecs/rt5640.h" +#include "../../codecs/rt5640.h" /* Haswell ULT platforms have a Headphone and Mic jack */ static const struct snd_soc_dapm_widget haswell_widgets[] = { @@ -56,9 +56,7 @@ static int haswell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = channels->max = 2; /* set SSP0 to 16 bit */ - snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - - SNDRV_PCM_HW_PARAM_FIRST_MASK], - SNDRV_PCM_FORMAT_S16_LE); + params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); return 0; } diff --git a/sound/soc/intel/mfld_machine.c b/sound/soc/intel/boards/mfld_machine.c index 90b7a57713a0..49c09a0add79 100644 --- a/sound/soc/intel/mfld_machine.c +++ b/sound/soc/intel/boards/mfld_machine.c @@ -228,10 +228,13 @@ static void mfld_jack_check(unsigned int intr_status) { struct mfld_jack_data jack_data; + if (!mfld_codec) + return; + jack_data.mfld_jack = &mfld_jack; jack_data.intr_id = intr_status; - sn95031_jack_detection(&jack_data); + sn95031_jack_detection(mfld_codec, &jack_data); /* TODO: add american headset detection post gpiolib support */ } @@ -240,8 +243,6 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) struct snd_soc_dapm_context *dapm = &runtime->card->dapm; int ret_val; - mfld_codec = runtime->codec; - /* default is earpiece pin, userspace sets it explcitly */ snd_soc_dapm_disable_pin(dapm, "Headphones"); /* default is lineout NC, userspace sets it explcitly */ @@ -254,20 +255,15 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) snd_soc_dapm_disable_pin(dapm, "LINEINR"); /* Headset and button jack detection */ - ret_val = snd_soc_jack_new(mfld_codec, "Intel(R) MID Audio Jack", - SND_JACK_HEADSET | SND_JACK_BTN_0 | - SND_JACK_BTN_1, &mfld_jack); + ret_val = snd_soc_card_jack_new(runtime->card, + "Intel(R) MID Audio Jack", SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1, &mfld_jack, + mfld_jack_pins, ARRAY_SIZE(mfld_jack_pins)); if (ret_val) { pr_err("jack creation failed\n"); return ret_val; } - ret_val = snd_soc_jack_add_pins(&mfld_jack, - ARRAY_SIZE(mfld_jack_pins), mfld_jack_pins); - if (ret_val) { - pr_err("adding jack pins failed\n"); - return ret_val; - } ret_val = snd_soc_jack_add_zones(&mfld_jack, ARRAY_SIZE(mfld_zones), mfld_zones); if (ret_val) { @@ -275,6 +271,8 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime) return ret_val; } + mfld_codec = runtime->codec; + /* we want to check if anything is inserted at boot, * so send a fake event to codec and it will read adc * to find if anything is there or not */ @@ -359,8 +357,6 @@ static irqreturn_t snd_mfld_jack_detection(int irq, void *data) { struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data; - if (mfld_jack.codec == NULL) - return IRQ_HANDLED; mfld_jack_check(mc_drv_ctx->interrupt_status); return IRQ_HANDLED; diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile new file mode 100644 index 000000000000..f24154ca4e98 --- /dev/null +++ b/sound/soc/intel/common/Makefile @@ -0,0 +1,7 @@ +snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o +snd-soc-sst-acpi-objs := sst-acpi.o +snd-soc-sst-ipc-objs := sst-ipc.o + +obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o +obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o + diff --git a/sound/soc/intel/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c index b3d84560fbb5..42f293f9c6e2 100644 --- a/sound/soc/intel/sst-acpi.c +++ b/sound/soc/intel/common/sst-acpi.c @@ -142,6 +142,7 @@ static int sst_acpi_probe(struct platform_device *pdev) sst_acpi->desc = desc; sst_acpi->mach = mach; + sst_pdata->resindex_dma_base = desc->resindex_dma_base; if (desc->resindex_dma_base >= 0) { sst_pdata->dma_engine = desc->dma_engine; sst_pdata->dma_base = desc->resindex_dma_base; diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index b9da030e312d..396d54510350 100644 --- a/sound/soc/intel/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -173,6 +173,16 @@ struct sst_module_runtime_context { }; /* + * Audio DSP Module State + */ +enum sst_module_state { + SST_MODULE_STATE_UNLOADED = 0, /* default state */ + SST_MODULE_STATE_LOADED, + SST_MODULE_STATE_INITIALIZED, /* and inactive */ + SST_MODULE_STATE_ACTIVE, +}; + +/* * Audio DSP Generic Module. * * Each Firmware file can consist of 1..N modules. A module can span multiple @@ -203,6 +213,9 @@ struct sst_module { struct list_head list; /* DSP list of modules */ struct list_head list_fw; /* FW list of modules */ struct list_head runtime_list; /* list of runtime module objects*/ + + /* state */ + enum sst_module_state state; }; /* diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index 64e94212d2d2..64e94212d2d2 100644 --- a/sound/soc/intel/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index f291e32f0077..96aeb2556ad4 100644 --- a/sound/soc/intel/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -28,7 +28,6 @@ /* Supported SST DMA Devices */ #define SST_DMA_TYPE_DW 1 -#define SST_DMA_TYPE_MID 2 /* autosuspend delay 5s*/ #define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) @@ -206,6 +205,7 @@ struct sst_pdata { const struct firmware *fw; /* DMA */ + int resindex_dma_base; /* other fields invalid if equals to -1 */ u32 dma_base; u32 dma_size; int dma_engine; diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 5f71ef607a57..ebcca6dc48d1 100644 --- a/sound/soc/intel/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -221,8 +221,6 @@ int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) dma_cap_mask_t mask; int ret; - /* The Intel MID DMA engine driver needs the slave config set but - * Synopsis DMA engine driver safely ignores the slave config */ dma_cap_zero(mask); dma_cap_set(DMA_SLAVE, mask); dma_cap_set(DMA_MEMCPY, mask); @@ -271,15 +269,16 @@ int sst_dma_new(struct sst_dsp *sst) const char *dma_dev_name; int ret = 0; + if (sst->pdata->resindex_dma_base == -1) + /* DMA is not used, return and squelsh error messages */ + return 0; + /* configure the correct platform data for whatever DMA engine * is attached to the ADSP IP. */ switch (sst->pdata->dma_engine) { case SST_DMA_TYPE_DW: dma_dev_name = "dw_dmac"; break; - case SST_DMA_TYPE_MID: - dma_dev_name = "Intel MID DMA"; - break; default: dev_err(sst->dev, "error: invalid DMA engine %d\n", sst->pdata->dma_engine); @@ -498,6 +497,7 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw, sst_module->scratch_size = template->scratch_size; sst_module->persistent_size = template->persistent_size; sst_module->entry = template->entry; + sst_module->state = SST_MODULE_STATE_UNLOADED; INIT_LIST_HEAD(&sst_module->block_list); INIT_LIST_HEAD(&sst_module->runtime_list); diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c new file mode 100644 index 000000000000..4b62a553823c --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.c @@ -0,0 +1,294 @@ +/* + * Intel SST generic IPC Support + * + * Copyright (C) 2015, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/kthread.h> +#include <sound/asound.h> + +#include "sst-dsp.h" +#include "sst-dsp-priv.h" +#include "sst-ipc.h" + +/* IPC message timeout (msecs) */ +#define IPC_TIMEOUT_MSECS 300 + +#define IPC_EMPTY_LIST_SIZE 8 + +/* locks held by caller */ +static struct ipc_message *msg_get_empty(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg = NULL; + + if (!list_empty(&ipc->empty_list)) { + msg = list_first_entry(&ipc->empty_list, struct ipc_message, + list); + list_del(&msg->list); + } + + return msg; +} + +static int tx_wait_done(struct sst_generic_ipc *ipc, + struct ipc_message *msg, void *rx_data) +{ + unsigned long flags; + int ret; + + /* wait for DSP completion (in all cases atm inc pending) */ + ret = wait_event_timeout(msg->waitq, msg->complete, + msecs_to_jiffies(IPC_TIMEOUT_MSECS)); + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + if (ret == 0) { + if (ipc->ops.shim_dbg != NULL) + ipc->ops.shim_dbg(ipc, "message timeout"); + + list_del(&msg->list); + ret = -ETIMEDOUT; + } else { + + /* copy the data returned from DSP */ + if (msg->rx_size) + memcpy(rx_data, msg->rx_data, msg->rx_size); + ret = msg->errno; + } + + list_add_tail(&msg->list, &ipc->empty_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return ret; +} + +static int ipc_tx_message(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, + size_t rx_bytes, int wait) +{ + struct ipc_message *msg; + unsigned long flags; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + msg = msg_get_empty(ipc); + if (msg == NULL) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return -EBUSY; + } + + msg->header = header; + msg->tx_size = tx_bytes; + msg->rx_size = rx_bytes; + msg->wait = wait; + msg->errno = 0; + msg->pending = false; + msg->complete = false; + + if ((tx_bytes) && (ipc->ops.tx_data_copy != NULL)) + ipc->ops.tx_data_copy(msg, tx_data, tx_bytes); + + list_add_tail(&msg->list, &ipc->tx_list); + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + queue_kthread_work(&ipc->kworker, &ipc->kwork); + + if (wait) + return tx_wait_done(ipc, msg, rx_data); + else + return 0; +} + +static int msg_empty_list_init(struct sst_generic_ipc *ipc) +{ + int i; + + ipc->msg = kzalloc(sizeof(struct ipc_message) * + IPC_EMPTY_LIST_SIZE, GFP_KERNEL); + if (ipc->msg == NULL) + return -ENOMEM; + + for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { + init_waitqueue_head(&ipc->msg[i].waitq); + list_add(&ipc->msg[i].list, &ipc->empty_list); + } + + return 0; +} + +static void ipc_tx_msgs(struct kthread_work *work) +{ + struct sst_generic_ipc *ipc = + container_of(work, struct sst_generic_ipc, kwork); + struct ipc_message *msg; + unsigned long flags; + u64 ipcx; + + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + if (list_empty(&ipc->tx_list) || ipc->pending) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + /* if the DSP is busy, we will TX messages after IRQ. + * also postpone if we are in the middle of procesing completion irq*/ + ipcx = sst_dsp_shim_read_unlocked(ipc->dsp, SST_IPCX); + if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) { + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + return; + } + + msg = list_first_entry(&ipc->tx_list, struct ipc_message, list); + list_move(&msg->list, &ipc->rx_list); + + if (ipc->ops.tx_msg != NULL) + ipc->ops.tx_msg(ipc, msg); + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); +} + +int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + rx_data, rx_bytes, 1); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_wait); + +int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes) +{ + return ipc_tx_message(ipc, header, tx_data, tx_bytes, + NULL, 0, 0); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_message_nowait); + +struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, + u64 header) +{ + struct ipc_message *msg; + u64 mask; + + if (ipc->ops.reply_msg_match != NULL) + header = ipc->ops.reply_msg_match(header, &mask); + + if (list_empty(&ipc->rx_list)) { + dev_err(ipc->dev, "error: rx list empty but received 0x%llx\n", + header); + return NULL; + } + + list_for_each_entry(msg, &ipc->rx_list, list) { + if ((msg->header & mask) == header) + return msg; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(sst_ipc_reply_find_msg); + +/* locks held by caller */ +void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, + struct ipc_message *msg) +{ + msg->complete = true; + + if (!msg->wait) + list_add_tail(&msg->list, &ipc->empty_list); + else + wake_up(&msg->waitq); +} +EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete); + +void sst_ipc_drop_all(struct sst_generic_ipc *ipc) +{ + struct ipc_message *msg, *tmp; + unsigned long flags; + int tx_drop_cnt = 0, rx_drop_cnt = 0; + + /* drop all TX and Rx messages before we stall + reset DSP */ + spin_lock_irqsave(&ipc->dsp->spinlock, flags); + + list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { + list_move(&msg->list, &ipc->empty_list); + tx_drop_cnt++; + } + + list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) { + list_move(&msg->list, &ipc->empty_list); + rx_drop_cnt++; + } + + spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); + + if (tx_drop_cnt || rx_drop_cnt) + dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n", + tx_drop_cnt, rx_drop_cnt); +} +EXPORT_SYMBOL_GPL(sst_ipc_drop_all); + +int sst_ipc_init(struct sst_generic_ipc *ipc) +{ + int ret; + + INIT_LIST_HEAD(&ipc->tx_list); + INIT_LIST_HEAD(&ipc->rx_list); + INIT_LIST_HEAD(&ipc->empty_list); + init_waitqueue_head(&ipc->wait_txq); + + ret = msg_empty_list_init(ipc); + if (ret < 0) + return -ENOMEM; + + /* start the IPC message thread */ + init_kthread_worker(&ipc->kworker); + ipc->tx_thread = kthread_run(kthread_worker_fn, + &ipc->kworker, "%s", + dev_name(ipc->dev)); + if (IS_ERR(ipc->tx_thread)) { + dev_err(ipc->dev, "error: failed to create message TX task\n"); + ret = PTR_ERR(ipc->tx_thread); + kfree(ipc->msg); + return ret; + } + + init_kthread_work(&ipc->kwork, ipc_tx_msgs); + return 0; +} +EXPORT_SYMBOL_GPL(sst_ipc_init); + +void sst_ipc_fini(struct sst_generic_ipc *ipc) +{ + if (ipc->tx_thread) + kthread_stop(ipc->tx_thread); + + if (ipc->msg) + kfree(ipc->msg); +} +EXPORT_SYMBOL_GPL(sst_ipc_fini); + +/* Module information */ +MODULE_AUTHOR("Jin Yao"); +MODULE_DESCRIPTION("Intel SST IPC generic"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h new file mode 100644 index 000000000000..125ea451a373 --- /dev/null +++ b/sound/soc/intel/common/sst-ipc.h @@ -0,0 +1,91 @@ +/* + * Intel SST generic IPC Support + * + * Copyright (C) 2015, Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version + * 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SST_GENERIC_IPC_H +#define __SST_GENERIC_IPC_H + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <linux/sched.h> +#include <linux/kthread.h> + +#define IPC_MAX_MAILBOX_BYTES 256 + +struct ipc_message { + struct list_head list; + u64 header; + + /* direction wrt host CPU */ + char tx_data[IPC_MAX_MAILBOX_BYTES]; + size_t tx_size; + char rx_data[IPC_MAX_MAILBOX_BYTES]; + size_t rx_size; + + wait_queue_head_t waitq; + bool pending; + bool complete; + bool wait; + int errno; +}; + +struct sst_generic_ipc; + +struct sst_plat_ipc_ops { + void (*tx_msg)(struct sst_generic_ipc *, struct ipc_message *); + void (*shim_dbg)(struct sst_generic_ipc *, const char *); + void (*tx_data_copy)(struct ipc_message *, char *, size_t); + u64 (*reply_msg_match)(u64 header, u64 *mask); +}; + +/* SST generic IPC data */ +struct sst_generic_ipc { + struct device *dev; + struct sst_dsp *dsp; + + /* IPC messaging */ + struct list_head tx_list; + struct list_head rx_list; + struct list_head empty_list; + wait_queue_head_t wait_txq; + struct task_struct *tx_thread; + struct kthread_worker kworker; + struct kthread_work kwork; + bool pending; + struct ipc_message *msg; + + struct sst_plat_ipc_ops ops; +}; + +int sst_ipc_tx_message_wait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes); + +int sst_ipc_tx_message_nowait(struct sst_generic_ipc *ipc, u64 header, + void *tx_data, size_t tx_bytes); + +struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, + u64 header); + +void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, + struct ipc_message *msg); + +void sst_ipc_drop_all(struct sst_generic_ipc *ipc); +int sst_ipc_init(struct sst_generic_ipc *ipc); +void sst_ipc_fini(struct sst_generic_ipc *ipc); + +#endif diff --git a/sound/soc/intel/haswell/Makefile b/sound/soc/intel/haswell/Makefile new file mode 100644 index 000000000000..9c1723112d22 --- /dev/null +++ b/sound/soc/intel/haswell/Makefile @@ -0,0 +1,4 @@ +snd-soc-sst-haswell-pcm-objs := \ + sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o + +obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c index 402b728c0a06..7f94920c8a4d 100644 --- a/sound/soc/intel/sst-haswell-dsp.c +++ b/sound/soc/intel/haswell/sst-haswell-dsp.c @@ -28,9 +28,9 @@ #include <linux/firmware.h> #include <linux/pm_runtime.h> -#include "sst-dsp.h" -#include "sst-dsp-priv.h" -#include "sst-haswell-ipc.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../haswell/sst-haswell-ipc.h" #include <trace/events/hswadsp.h> @@ -100,6 +100,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, && module->type != SST_HSW_MODULE_PCM && module->type != SST_HSW_MODULE_PCM_REFERENCE && module->type != SST_HSW_MODULE_PCM_CAPTURE + && module->type != SST_HSW_MODULE_WAVES && module->type != SST_HSW_MODULE_LPAL) return 0; @@ -139,6 +140,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, mod->type = SST_MEM_IRAM; break; case SST_HSW_DRAM: + case SST_HSW_REGS: ram = dsp->addr.lpe; mod->offset = block->ram_offset; mod->type = SST_MEM_DRAM; @@ -169,6 +171,7 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, block = (void *)block + sizeof(*block) + block->size; } + mod->state = SST_MODULE_STATE_LOADED; return 0; } diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 863a9ca34b8e..344a1e9bbce5 100644 --- a/sound/soc/intel/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -34,8 +34,9 @@ #include <sound/asound.h> #include "sst-haswell-ipc.h" -#include "sst-dsp.h" -#include "sst-dsp-priv.h" +#include "../common/sst-dsp.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-ipc.h" /* Global Message - Generic */ #define IPC_GLB_TYPE_SHIFT 24 @@ -79,6 +80,15 @@ #define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) #define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) +/* Module Message */ +#define IPC_MODULE_OPERATION_SHIFT 20 +#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT) +#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT) + +#define IPC_MODULE_ID_SHIFT 16 +#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT) +#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT) + /* IPC message timeout (msecs) */ #define IPC_TIMEOUT_MSECS 300 #define IPC_BOOT_MSECS 200 @@ -115,6 +125,7 @@ enum ipc_glb_type { IPC_GLB_ENTER_DX_STATE = 12, IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ + IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */ IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ }; @@ -133,6 +144,16 @@ enum ipc_glb_reply { IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ }; +enum ipc_module_operation { + IPC_MODULE_NOTIFICATION = 0, + IPC_MODULE_ENABLE = 1, + IPC_MODULE_DISABLE = 2, + IPC_MODULE_GET_PARAMETER = 3, + IPC_MODULE_SET_PARAMETER = 4, + IPC_MODULE_GET_INFO = 5, + IPC_MODULE_MAX_MESSAGE +}; + /* Stream Message - Types */ enum ipc_str_operation { IPC_STR_RESET = 0, @@ -190,23 +211,6 @@ struct sst_hsw_ipc_fw_ready { u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; } __attribute__((packed)); -struct ipc_message { - struct list_head list; - u32 header; - - /* direction wrt host CPU */ - char tx_data[IPC_MAX_MAILBOX_BYTES]; - size_t tx_size; - char rx_data[IPC_MAX_MAILBOX_BYTES]; - size_t rx_size; - - wait_queue_head_t waitq; - bool pending; - bool complete; - bool wait; - int errno; -}; - struct sst_hsw_stream; struct sst_hsw; @@ -305,18 +309,19 @@ struct sst_hsw { bool shutdown; /* IPC messaging */ - struct list_head tx_list; - struct list_head rx_list; - struct list_head empty_list; - wait_queue_head_t wait_txq; - struct task_struct *tx_thread; - struct kthread_worker kworker; - struct kthread_work kwork; - bool pending; - struct ipc_message *msg; + struct sst_generic_ipc ipc; /* FW log stream */ struct sst_hsw_log_stream log_stream; + + /* flags bit field to track module state when resume from RTD3, + * each bit represent state (enabled/disabled) of single module */ + u32 enabled_modules_rtd3; + + /* buffer to store parameter lines */ + u32 param_idx_w; /* write index */ + u32 param_idx_r; /* read index */ + u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT]; }; #define CREATE_TRACE_POINTS @@ -352,6 +357,16 @@ static inline u32 msg_get_notify_reason(u32 msg) return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; } +static inline u32 msg_get_module_operation(u32 msg) +{ + return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT; +} + +static inline u32 msg_get_module_id(u32 msg) +{ + return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT; +} + u32 create_channel_map(enum sst_hsw_channel_config config) { switch (config) { @@ -417,159 +432,6 @@ static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, return NULL; } -static void ipc_shim_dbg(struct sst_hsw *hsw, const char *text) -{ - struct sst_dsp *sst = hsw->dsp; - u32 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); - - dev_err(hsw->dev, "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", - text, ipcx, isr, ipcd, imrx); -} - -/* locks held by caller */ -static struct ipc_message *msg_get_empty(struct sst_hsw *hsw) -{ - struct ipc_message *msg = NULL; - - if (!list_empty(&hsw->empty_list)) { - msg = list_first_entry(&hsw->empty_list, struct ipc_message, - list); - list_del(&msg->list); - } - - return msg; -} - -static void ipc_tx_msgs(struct kthread_work *work) -{ - struct sst_hsw *hsw = - container_of(work, struct sst_hsw, kwork); - struct ipc_message *msg; - unsigned long flags; - u32 ipcx; - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - if (list_empty(&hsw->tx_list) || hsw->pending) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return; - } - - /* if the DSP is busy, we will TX messages after IRQ. - * also postpone if we are in the middle of procesing completion irq*/ - ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX); - if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return; - } - - msg = list_first_entry(&hsw->tx_list, struct ipc_message, list); - - list_move(&msg->list, &hsw->rx_list); - - /* send the message */ - sst_dsp_outbox_write(hsw->dsp, msg->tx_data, msg->tx_size); - sst_dsp_ipc_msg_tx(hsw->dsp, msg->header | SST_IPCX_BUSY); - - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); -} - -/* locks held by caller */ -static void tx_msg_reply_complete(struct sst_hsw *hsw, struct ipc_message *msg) -{ - msg->complete = true; - trace_ipc_reply("completed", msg->header); - - if (!msg->wait) - list_add_tail(&msg->list, &hsw->empty_list); - else - wake_up(&msg->waitq); -} - -static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg, - void *rx_data) -{ - unsigned long flags; - int ret; - - /* wait for DSP completion (in all cases atm inc pending) */ - ret = wait_event_timeout(msg->waitq, msg->complete, - msecs_to_jiffies(IPC_TIMEOUT_MSECS)); - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - if (ret == 0) { - ipc_shim_dbg(hsw, "message timeout"); - - trace_ipc_error("error message timeout for", msg->header); - list_del(&msg->list); - ret = -ETIMEDOUT; - } else { - - /* copy the data returned from DSP */ - if (msg->rx_size) - memcpy(rx_data, msg->rx_data, msg->rx_size); - ret = msg->errno; - } - - list_add_tail(&msg->list, &hsw->empty_list); - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return ret; -} - -static int ipc_tx_message(struct sst_hsw *hsw, u32 header, void *tx_data, - size_t tx_bytes, void *rx_data, size_t rx_bytes, int wait) -{ - struct ipc_message *msg; - unsigned long flags; - - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - msg = msg_get_empty(hsw); - if (msg == NULL) { - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - return -EBUSY; - } - - if (tx_bytes) - memcpy(msg->tx_data, tx_data, tx_bytes); - - msg->header = header; - msg->tx_size = tx_bytes; - msg->rx_size = rx_bytes; - msg->wait = wait; - msg->errno = 0; - msg->pending = false; - msg->complete = false; - - list_add_tail(&msg->list, &hsw->tx_list); - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - - queue_kthread_work(&hsw->kworker, &hsw->kwork); - - if (wait) - return tx_wait_done(hsw, msg, rx_data); - else - return 0; -} - -static inline int ipc_tx_message_wait(struct sst_hsw *hsw, u32 header, - void *tx_data, size_t tx_bytes, void *rx_data, size_t rx_bytes) -{ - return ipc_tx_message(hsw, header, tx_data, tx_bytes, rx_data, - rx_bytes, 1); -} - -static inline int ipc_tx_message_nowait(struct sst_hsw *hsw, u32 header, - void *tx_data, size_t tx_bytes) -{ - return ipc_tx_message(hsw, header, tx_data, tx_bytes, NULL, 0, 0); -} - static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) { struct sst_hsw_ipc_fw_ready fw_ready; @@ -604,7 +466,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) /* log the FW version info got from the mailbox here. */ memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size); pinfo = &fw_info[0]; - for (i = 0; i < sizeof(tmp) / sizeof(char *); i++) + for (i = 0; i < ARRAY_SIZE(tmp); i++) tmp[i] = strsep(&pinfo, " "); dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - " "version: %s.%s, build %s, source commit id: %s\n", @@ -657,27 +519,6 @@ static void hsw_notification_work(struct work_struct *work) sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); } -static struct ipc_message *reply_find_msg(struct sst_hsw *hsw, u32 header) -{ - struct ipc_message *msg; - - /* clear reply bits & status bits */ - header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); - - if (list_empty(&hsw->rx_list)) { - dev_err(hsw->dev, "error: rx list empty but received 0x%x\n", - header); - return NULL; - } - - list_for_each_entry(msg, &hsw->rx_list, list) { - if (msg->header == header) - return msg; - } - - return NULL; -} - static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) { struct sst_hsw_stream *stream; @@ -716,7 +557,7 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) trace_ipc_reply("processing -->", header); - msg = reply_find_msg(hsw, header); + msg = sst_ipc_reply_find_msg(&hsw->ipc, header); if (msg == NULL) { trace_ipc_error("error: can't find message header", header); return -EIO; @@ -727,14 +568,14 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) case IPC_GLB_REPLY_PENDING: trace_ipc_pending_reply("received", header); msg->pending = true; - hsw->pending = true; + hsw->ipc.pending = true; return 1; case IPC_GLB_REPLY_SUCCESS: if (msg->pending) { trace_ipc_pending_reply("completed", header); sst_dsp_inbox_read(hsw->dsp, msg->rx_data, msg->rx_size); - hsw->pending = false; + hsw->ipc.pending = false; } else { /* copy data from the DSP */ sst_dsp_outbox_read(hsw->dsp, msg->rx_data, @@ -790,11 +631,36 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) /* wake up and return the error if we have waiters on this message ? */ list_del(&msg->list); - tx_msg_reply_complete(hsw, msg); + sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg); return 1; } +static int hsw_module_message(struct sst_hsw *hsw, u32 header) +{ + u32 operation, module_id; + int handled = 0; + + operation = msg_get_module_operation(header); + module_id = msg_get_module_id(header); + dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n", + header); + dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n", + operation, module_id); + + switch (operation) { + case IPC_MODULE_NOTIFICATION: + dev_dbg(hsw->dev, "module notification received"); + handled = 1; + break; + default: + handled = hsw_process_reply(hsw, header); + break; + } + + return handled; +} + static int hsw_stream_message(struct sst_hsw *hsw, u32 header) { u32 stream_msg, stream_id, stage_type; @@ -890,6 +756,9 @@ static int hsw_process_notification(struct sst_hsw *hsw) case IPC_GLB_DEBUG_LOG_MESSAGE: handled = hsw_log_message(hsw, header); break; + case IPC_GLB_MODULE_OPERATION: + handled = hsw_module_message(hsw, header); + break; default: dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", type, header); @@ -903,6 +772,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) { struct sst_dsp *sst = (struct sst_dsp *) context; struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); + struct sst_generic_ipc *ipc = &hsw->ipc; u32 ipcx, ipcd; int handled; unsigned long flags; @@ -949,7 +819,7 @@ static irqreturn_t hsw_irq_thread(int irq, void *context) spin_unlock_irqrestore(&sst->spinlock, flags); /* continue to send any remaining messages... */ - queue_kthread_work(&hsw->kworker, &hsw->kwork); + queue_kthread_work(&ipc->kworker, &ipc->kwork); return IRQ_HANDLED; } @@ -959,7 +829,8 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw, { int ret; - ret = ipc_tx_message_wait(hsw, IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), + ret = sst_ipc_tx_message_wait(&hsw->ipc, + IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION), NULL, 0, version, sizeof(*version)); if (ret < 0) dev_err(hsw->dev, "error: get version failed\n"); @@ -1023,7 +894,8 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw, req->channel = channel; } - ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, req, + sizeof(*req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: set stream volume failed\n"); return ret; @@ -1088,7 +960,8 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, req.curve_type = hsw->curve_type; req.target_volume = volume; - ret = ipc_tx_message_wait(hsw, header, &req, sizeof(req), NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &req, + sizeof(req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: set mixer volume failed\n"); return ret; @@ -1146,7 +1019,7 @@ int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) stream->free_req.stream_id = stream->reply.stream_hw_id; header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); - ret = ipc_tx_message_wait(hsw, header, &stream->free_req, + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &stream->free_req, sizeof(stream->free_req), NULL, 0); if (ret < 0) { dev_err(hsw->dev, "error: free stream %d failed\n", @@ -1338,8 +1211,8 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); - ret = ipc_tx_message_wait(hsw, header, str_req, sizeof(*str_req), - reply, sizeof(*reply)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, str_req, + sizeof(*str_req), reply, sizeof(*reply)); if (ret < 0) { dev_err(hsw->dev, "error: stream commit failed\n"); return ret; @@ -1388,7 +1261,8 @@ int sst_hsw_mixer_get_info(struct sst_hsw *hsw) trace_ipc_request("get global mixer info", 0); - ret = ipc_tx_message_wait(hsw, header, NULL, 0, reply, sizeof(*reply)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, + reply, sizeof(*reply)); if (ret < 0) { dev_err(hsw->dev, "error: get stream info failed\n"); return ret; @@ -1409,9 +1283,10 @@ static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, header |= (stream_id << IPC_STR_ID_SHIFT); if (wait) - return ipc_tx_message_wait(hsw, header, NULL, 0, NULL, 0); + return sst_ipc_tx_message_wait(&hsw->ipc, header, + NULL, 0, NULL, 0); else - return ipc_tx_message_nowait(hsw, header, NULL, 0); + return sst_ipc_tx_message_nowait(&hsw->ipc, header, NULL, 0); } /* Stream ALSA trigger operations */ @@ -1538,8 +1413,8 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw, header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); - ret = ipc_tx_message_wait(hsw, header, &config, sizeof(config), - NULL, 0); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &config, + sizeof(config), NULL, 0); if (ret < 0) dev_err(hsw->dev, "error: set device formats failed\n"); @@ -1559,8 +1434,8 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, trace_ipc_request("PM enter Dx state", state); - ret = ipc_tx_message_wait(hsw, header, &state_, sizeof(state_), - dx, sizeof(*dx)); + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, &state_, + sizeof(state_), dx, sizeof(*dx)); if (ret < 0) { dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); return ret; @@ -1703,32 +1578,6 @@ static int sst_hsw_dx_state_restore(struct sst_hsw *hsw) return 0; } -static void sst_hsw_drop_all(struct sst_hsw *hsw) -{ - struct ipc_message *msg, *tmp; - unsigned long flags; - int tx_drop_cnt = 0, rx_drop_cnt = 0; - - /* drop all TX and Rx messages before we stall + reset DSP */ - spin_lock_irqsave(&hsw->dsp->spinlock, flags); - - list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) { - list_move(&msg->list, &hsw->empty_list); - tx_drop_cnt++; - } - - list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) { - list_move(&msg->list, &hsw->empty_list); - rx_drop_cnt++; - } - - spin_unlock_irqrestore(&hsw->dsp->spinlock, flags); - - if (tx_drop_cnt || rx_drop_cnt) - dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n", - tx_drop_cnt, rx_drop_cnt); -} - int sst_hsw_dsp_load(struct sst_hsw *hsw) { struct sst_dsp *dsp = hsw->dsp; @@ -1808,7 +1657,7 @@ int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw) if (ret < 0) return ret; - sst_hsw_drop_all(hsw); + sst_ipc_drop_all(&hsw->ipc); return 0; } @@ -1844,6 +1693,8 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) if (ret < 0) dev_err(dev, "error: audio DSP boot failure\n"); + sst_hsw_init_module_state(hsw); + ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, msecs_to_jiffies(IPC_BOOT_MSECS)); if (ret == 0) { @@ -1864,26 +1715,345 @@ int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) } #endif -static int msg_empty_list_init(struct sst_hsw *hsw) +struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) { - int i; + return hsw->dsp; +} - hsw->msg = kzalloc(sizeof(struct ipc_message) * - IPC_EMPTY_LIST_SIZE, GFP_KERNEL); - if (hsw->msg == NULL) - return -ENOMEM; +void sst_hsw_init_module_state(struct sst_hsw *hsw) +{ + struct sst_module *module; + enum sst_hsw_module_id id; + + /* the base fw contains several modules */ + for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) { + module = sst_module_get_from_id(hsw->dsp, id); + if (module) { + /* module waves is active only after being enabled */ + if (id == SST_HSW_MODULE_WAVES) + module->state = SST_MODULE_STATE_INITIALIZED; + else + module->state = SST_MODULE_STATE_ACTIVE; + } + } +} + +bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED) + return false; + else + return true; +} + +bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id) +{ + struct sst_module *module; + + module = sst_module_get_from_id(hsw->dsp, module_id); + if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE) + return true; + else + return false; +} - for (i = 0; i < IPC_EMPTY_LIST_SIZE; i++) { - init_waitqueue_head(&hsw->msg[i].waitq); - list_add(&hsw->msg[i].list, &hsw->empty_list); +void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 |= (1 << module_id); +} + +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + hsw->enabled_modules_rtd3 &= ~(1 << module_id); +} + +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) +{ + return hsw->enabled_modules_rtd3 & (1 << module_id); +} + +void sst_hsw_reset_param_buf(struct sst_hsw *hsw) +{ + hsw->param_idx_w = 0; + hsw->param_idx_r = 0; + memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf)); +} + +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf) +{ + /* save line to the first available position of param buffer */ + if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) { + dev_warn(hsw->dev, "warning: param buffer overflow!\n"); + return -EPERM; + } + memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT); + hsw->param_idx_w++; + return 0; +} + +int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf) +{ + u8 id = 0; + + /* read the first matching line from param buffer */ + while (hsw->param_idx_r < WAVES_PARAM_LINES) { + id = hsw->param_buf[hsw->param_idx_r][0]; + hsw->param_idx_r++; + if (buf[0] == id) { + memcpy(buf, hsw->param_buf[hsw->param_idx_r], + WAVES_PARAM_COUNT); + break; + } } + if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) { + dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n"); + hsw->param_idx_r = 0; + return 0; + } + return 0; +} + +int sst_hsw_launch_param_buf(struct sst_hsw *hsw) +{ + int ret, idx; + if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { + dev_dbg(hsw->dev, "module waves is not active\n"); + return 0; + } + + /* put all param lines to DSP through ipc */ + for (idx = 0; idx < hsw->param_idx_w; idx++) { + ret = sst_hsw_module_set_param(hsw, + SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0], + WAVES_PARAM_COUNT, hsw->param_buf[idx]); + if (ret < 0) + return ret; + } return 0; } -struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name) { - return hsw->dsp; + int ret = 0; + const struct firmware *fw = NULL; + struct sst_fw *hsw_sst_fw; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name); + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + /* loading for the first time */ + if (module_id == SST_HSW_MODULE_BASE_FW) { + /* for base module: use fw requested in acpi probe */ + fw = dsp->pdata->fw; + if (!fw) { + dev_err(dev, "request Base fw failed\n"); + return -ENODEV; + } + } else { + /* try and load any other optional modules if they are + * available. Use dev_info instead of dev_err in case + * request firmware failed */ + ret = request_firmware(&fw, name, dev); + if (ret) { + dev_info(dev, "fw image %s not available(%d)\n", + name, ret); + return ret; + } + } + hsw_sst_fw = sst_fw_new(dsp, fw, hsw); + if (hsw_sst_fw == NULL) { + dev_err(dev, "error: failed to load firmware\n"); + ret = -ENOMEM; + goto out; + } + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "error: no module %d in firmware %s\n", + module_id, name); + } + } else + dev_info(dev, "module %d (%s) already loaded\n", + module_id, name); +out: + /* release fw, but base fw should be released by acpi driver */ + if (fw && module_id != SST_HSW_MODULE_BASE_FW) + release_firmware(fw); + + return ret; +} + +int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header = 0; + struct sst_hsw_ipc_module_config config; + struct sst_module *module; + struct sst_module_runtime *runtime; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already enabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + runtime = sst_module_runtime_get_from_id(module, module_id); + if (runtime == NULL) { + dev_err(dev, "runtime %d not valid", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "module enable header: %x\n", header); + + config.map.module_entries_count = 1; + config.map.module_entries[0].module_id = module->id; + config.map.module_entries[0].entry_point = module->entry; + + config.persistent_mem.offset = + sst_dsp_get_offset(dsp, + runtime->persistent_offset, SST_MEM_DRAM); + config.persistent_mem.size = module->persistent_size; + + config.scratch_mem.offset = + sst_dsp_get_offset(dsp, + dsp->scratch_offset, SST_MEM_DRAM); + config.scratch_mem.size = module->scratch_size; + dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x", + config.map.module_entries[0].module_id, + config.persistent_mem.size, + config.persistent_mem.offset, + config.scratch_mem.size, config.scratch_mem.offset, + config.map.module_entries[0].entry_point); + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, + &config, sizeof(config), NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module enable failed - %d\n", ret); + else + module->state = SST_MODULE_STATE_ACTIVE; + + return ret; +} + +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id) +{ + int ret; + u32 header; + struct sst_module *module; + struct device *dev = hsw->dev; + struct sst_dsp *dsp = hsw->dsp; + + if (!sst_hsw_is_module_loaded(hsw, module_id)) { + dev_dbg(dev, "module %d not loaded\n", module_id); + return 0; + } + + if (!sst_hsw_is_module_active(hsw, module_id)) { + dev_info(dev, "module %d already disabled\n", module_id); + return 0; + } + + module = sst_module_get_from_id(dsp, module_id); + if (module == NULL) { + dev_err(dev, "module %d not valid\n", module_id); + return -ENXIO; + } + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | + IPC_MODULE_ID(module_id); + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, NULL, 0, NULL, 0); + if (ret < 0) + dev_err(dev, "module disable failed - %d\n", ret); + else + module->state = SST_MODULE_STATE_INITIALIZED; + + return ret; +} + +int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param) +{ + int ret; + unsigned char *data = NULL; + u32 header = 0; + u32 payload_size = 0, transfer_parameter_size = 0; + dma_addr_t dma_addr = 0; + struct sst_hsw_transfer_parameter *parameter; + struct device *dev = hsw->dev; + + header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | + IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) | + IPC_MODULE_ID(module_id); + dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", header); + + payload_size = param_size + + sizeof(struct sst_hsw_transfer_parameter) - + sizeof(struct sst_hsw_transfer_list); + dev_dbg(dev, "parameter size : %d\n", param_size); + dev_dbg(dev, "payload size : %d\n", payload_size); + + if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) { + /* short parameter, mailbox can contain data */ + dev_dbg(dev, "transfer parameter size : %d\n", + transfer_parameter_size); + + transfer_parameter_size = ALIGN(payload_size, 4); + dev_dbg(dev, "transfer parameter aligned size : %d\n", + transfer_parameter_size); + + parameter = kzalloc(transfer_parameter_size, GFP_KERNEL); + if (parameter == NULL) + return -ENOMEM; + + memcpy(parameter->data, param, param_size); + } else { + dev_warn(dev, "transfer parameter size too large!"); + return 0; + } + + parameter->parameter_id = parameter_id; + parameter->data_size = param_size; + + ret = sst_ipc_tx_message_wait(&hsw->ipc, header, + parameter, transfer_parameter_size , NULL, 0); + if (ret < 0) + dev_err(dev, "ipc: module set parameter failed - %d\n", ret); + + kfree(parameter); + + if (data) + dma_free_coherent(hsw->dsp->dma_dev, + param_size, (void *)data, dma_addr); + + return ret; } static struct sst_dsp_device hsw_dev = { @@ -1891,10 +2061,48 @@ static struct sst_dsp_device hsw_dev = { .ops = &haswell_ops, }; +static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) +{ + /* send the message */ + sst_dsp_outbox_write(ipc->dsp, msg->tx_data, msg->tx_size); + sst_dsp_ipc_msg_tx(ipc->dsp, msg->header); +} + +static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text) +{ + struct sst_dsp *sst = ipc->dsp; + u32 isr, ipcd, imrx, ipcx; + + ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); + isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); + ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); + imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); + + dev_err(ipc->dev, + "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", + text, ipcx, isr, ipcd, imrx); +} + +static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data, + size_t tx_size) +{ + memcpy(msg->tx_data, tx_data, tx_size); +} + +static u64 hsw_reply_msg_match(u64 header, u64 *mask) +{ + /* clear reply bits & status bits */ + header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); + *mask = (u64)-1; + + return header; +} + int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) { struct sst_hsw_ipc_fw_version version; struct sst_hsw *hsw; + struct sst_generic_ipc *ipc; int ret; dev_dbg(dev, "initialising Audio DSP IPC\n"); @@ -1903,39 +2111,30 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) if (hsw == NULL) return -ENOMEM; - hsw->dev = dev; - INIT_LIST_HEAD(&hsw->stream_list); - INIT_LIST_HEAD(&hsw->tx_list); - INIT_LIST_HEAD(&hsw->rx_list); - INIT_LIST_HEAD(&hsw->empty_list); - init_waitqueue_head(&hsw->boot_wait); - init_waitqueue_head(&hsw->wait_txq); - - ret = msg_empty_list_init(hsw); - if (ret < 0) - return -ENOMEM; + ipc = &hsw->ipc; + ipc->dev = dev; + ipc->ops.tx_msg = hsw_tx_msg; + ipc->ops.shim_dbg = hsw_shim_dbg; + ipc->ops.tx_data_copy = hsw_tx_data_copy; + ipc->ops.reply_msg_match = hsw_reply_msg_match; - /* start the IPC message thread */ - init_kthread_worker(&hsw->kworker); - hsw->tx_thread = kthread_run(kthread_worker_fn, - &hsw->kworker, "%s", - dev_name(hsw->dev)); - if (IS_ERR(hsw->tx_thread)) { - ret = PTR_ERR(hsw->tx_thread); - dev_err(hsw->dev, "error: failed to create message TX task\n"); - goto err_free_msg; - } - init_kthread_work(&hsw->kwork, ipc_tx_msgs); + ret = sst_ipc_init(ipc); + if (ret != 0) + goto ipc_init_err; + INIT_LIST_HEAD(&hsw->stream_list); + init_waitqueue_head(&hsw->boot_wait); hsw_dev.thread_context = hsw; /* init SST shim */ hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); if (hsw->dsp == NULL) { ret = -ENODEV; - goto dsp_err; + goto dsp_new_err; } + ipc->dsp = hsw->dsp; + /* allocate DMA buffer for context storage */ hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL); @@ -1947,18 +2146,22 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) /* keep the DSP in reset state for base FW loading */ sst_dsp_reset(hsw->dsp); - hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw); - if (hsw->sst_fw == NULL) { - ret = -ENODEV; - dev_err(dev, "error: failed to load firmware\n"); + /* load base module and other modules in base firmware image */ + ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base"); + if (ret < 0) goto fw_err; - } + + /* try to load module waves */ + sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin"); /* allocate scratch mem regions */ ret = sst_block_alloc_scratch(hsw->dsp); if (ret < 0) goto boot_err; + /* init param buffer */ + sst_hsw_reset_param_buf(hsw); + /* wait for DSP boot completion */ sst_dsp_boot(hsw->dsp); ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, @@ -1971,6 +2174,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) goto boot_err; } + /* init module state after boot */ + sst_hsw_init_module_state(hsw); + /* get the FW version */ sst_hsw_fw_get_version(hsw, &version); @@ -1986,17 +2192,16 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) boot_err: sst_dsp_reset(hsw->dsp); - sst_fw_free(hsw->sst_fw); + sst_fw_free_all(hsw->dsp); fw_err: dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, hsw->dx_context, hsw->dx_context_paddr); dma_err: sst_dsp_free(hsw->dsp); -dsp_err: - kthread_stop(hsw->tx_thread); -err_free_msg: - kfree(hsw->msg); - +dsp_new_err: + sst_ipc_fini(ipc); +ipc_init_err: + kfree(hsw); return ret; } EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); @@ -2010,7 +2215,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, hsw->dx_context, hsw->dx_context_paddr); sst_dsp_free(hsw->dsp); - kthread_stop(hsw->tx_thread); - kfree(hsw->msg); + sst_ipc_fini(&hsw->ipc); } EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/haswell/sst-haswell-ipc.h index 858096041cb1..06d71aefa1fe 100644 --- a/sound/soc/intel/sst-haswell-ipc.h +++ b/sound/soc/intel/haswell/sst-haswell-ipc.h @@ -37,6 +37,9 @@ #define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 #define SST_HSW_MAX_INFO_SIZE 64 #define SST_HSW_BUILD_HASH_LENGTH 40 +#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500 +#define WAVES_PARAM_COUNT 128 +#define WAVES_PARAM_LINES 160 struct sst_hsw; struct sst_hsw_stream; @@ -187,6 +190,28 @@ enum sst_hsw_performance_action { SST_HSW_PERF_STOP = 1, }; +struct sst_hsw_transfer_info { + uint32_t destination; /* destination address */ + uint32_t reverse:1; /* if 1 data flows from destination */ + uint32_t size:31; /* transfer size in bytes.*/ + uint16_t first_page_offset; /* offset to data in the first page. */ + uint8_t packed_pages; /* page addresses. Each occupies 20 bits */ +} __attribute__((packed)); + +struct sst_hsw_transfer_list { + uint32_t transfers_count; + struct sst_hsw_transfer_info transfers; +} __attribute__((packed)); + +struct sst_hsw_transfer_parameter { + uint32_t parameter_id; + uint32_t data_size; + union { + uint8_t data[1]; + struct sst_hsw_transfer_list transfer_list; + }; +} __attribute__((packed)); + /* SST firmware module info */ struct sst_hsw_module_info { u8 name[SST_HSW_MAX_INFO_SIZE]; @@ -215,6 +240,12 @@ struct sst_hsw_fx_enable { struct sst_hsw_memory_info persistent_mem; } __attribute__((packed)); +struct sst_hsw_ipc_module_config { + struct sst_hsw_module_map map; + struct sst_hsw_memory_info persistent_mem; + struct sst_hsw_memory_info scratch_mem; +} __attribute__((packed)); + struct sst_hsw_get_fx_param { u32 parameter_id; u32 param_size; @@ -467,6 +498,28 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); +/* fw module function */ +void sst_hsw_init_module_state(struct sst_hsw *hsw); +bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id); +bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); +void sst_hsw_reset_param_buf(struct sst_hsw *hsw); +int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf); +int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf); +int sst_hsw_launch_param_buf(struct sst_hsw *hsw); + +int sst_hsw_module_load(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, char *name); +int sst_hsw_module_enable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id); +int sst_hsw_module_disable(struct sst_hsw *hsw, + u32 module_id, u32 instance_id); +int sst_hsw_module_set_param(struct sst_hsw *hsw, + u32 module_id, u32 instance_id, u32 parameter_id, + u32 param_size, char *param); + /* runtime module management */ struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, int mod_id, int offset); diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c index 7e21e8f85885..23ae0400d6db 100644 --- a/sound/soc/intel/sst-haswell-pcm.c +++ b/sound/soc/intel/haswell/sst-haswell-pcm.c @@ -29,9 +29,9 @@ #include <sound/tlv.h> #include <sound/compress_driver.h> -#include "sst-haswell-ipc.h" -#include "sst-dsp-priv.h" -#include "sst-dsp.h" +#include "../haswell/sst-haswell-ipc.h" +#include "../common/sst-dsp-priv.h" +#include "../common/sst-dsp.h" #define HSW_PCM_COUNT 6 #define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ @@ -137,6 +137,7 @@ struct hsw_priv_data { struct device *dev; enum hsw_pm_state pm_state; struct snd_soc_card *soc_card; + struct sst_module_runtime *runtime_waves; /* sound effect module */ /* page tables */ struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; @@ -318,6 +319,93 @@ static int hsw_volume_get(struct snd_kcontrol *kcontrol, return 0; } +static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + + ucontrol->value.integer.value[0] = + (sst_hsw_is_module_active(hsw, id) || + sst_hsw_is_module_enabled_rtd3(hsw, id)); + return 0; +} + +static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret = 0; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + bool switch_on = (bool)ucontrol->value.integer.value[0]; + + /* if module is in RAM on the DSP, apply user settings to module through + * ipc. If module is not in RAM on the DSP, store user setting for + * track */ + if (sst_hsw_is_module_loaded(hsw, id)) { + if (switch_on == sst_hsw_is_module_active(hsw, id)) + return 0; + + if (switch_on) + ret = sst_hsw_module_enable(hsw, id, 0); + else + ret = sst_hsw_module_disable(hsw, id, 0); + } else { + if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id)) + return 0; + + if (switch_on) + sst_hsw_set_module_enabled_rtd3(hsw, id); + else + sst_hsw_set_module_disabled_rtd3(hsw, id); + } + + return ret; +} + +static int hsw_waves_param_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + + /* return a matching line from param buffer */ + return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data); +} + +static int hsw_waves_param_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol); + struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform); + struct sst_hsw *hsw = pdata->hsw; + int ret; + enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; + int param_id = ucontrol->value.bytes.data[0]; + int param_size = WAVES_PARAM_COUNT; + + /* clear param buffer and reset buffer index */ + if (param_id == 0xFF) { + sst_hsw_reset_param_buf(hsw); + return 0; + } + + /* store params into buffer */ + ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data); + if (ret < 0) + return ret; + + if (sst_hsw_is_module_active(hsw, id)) + ret = sst_hsw_module_set_param(hsw, id, 0, param_id, + param_size, ucontrol->value.bytes.data); + return ret; +} + /* TLV used by both global and stream volumes */ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); @@ -339,6 +427,12 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = { SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, ARRAY_SIZE(volume_map) - 1, 0, hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), + /* enable/disable module waves */ + SOC_SINGLE_BOOL_EXT("Waves Switch", 0, + hsw_waves_switch_get, hsw_waves_switch_put), + /* set parameters to module waves */ + SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT, + hsw_waves_param_get, hsw_waves_param_put), }; /* Create DMA buffer page table for DSP */ @@ -807,6 +901,14 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) pcm_data->runtime->persistent_offset; } + /* create runtime blocks for module waves */ + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + pdata->runtime_waves = sst_hsw_runtime_module_create(hsw, + SST_HSW_MODULE_WAVES, 0); + if (pdata->runtime_waves == NULL) + goto err; + } + return 0; err: @@ -820,14 +922,17 @@ err: static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) { + struct sst_hsw *hsw = pdata->hsw; struct hsw_pcm_data *pcm_data; int i; for (i = 0; i < ARRAY_SIZE(mod_map); i++) { pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - sst_hsw_runtime_module_free(pcm_data->runtime); } + if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { + sst_hsw_runtime_module_free(pdata->runtime_waves); + } } static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd) @@ -984,7 +1089,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) } /* allocate runtime modules */ - hsw_pcm_create_modules(priv_data); + ret = hsw_pcm_create_modules(priv_data); + if (ret < 0) + goto err; /* enable runtime PM with auto suspend */ pm_runtime_set_autosuspend_delay(platform->dev, @@ -996,7 +1103,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform) return 0; err: - for (;i >= 0; i--) { + for (--i; i >= 0; i--) { if (hsw_dais[i].playback.channels_min) snd_dma_free_pages(&priv_data->dmab[i][0]); if (hsw_dais[i].capture.channels_min) @@ -1101,10 +1208,18 @@ static int hsw_pcm_runtime_suspend(struct device *dev) { struct hsw_priv_data *pdata = dev_get_drvdata(dev); struct sst_hsw *hsw = pdata->hsw; + int ret; if (pdata->pm_state >= HSW_PM_STATE_RTD3) return 0; + /* fw modules will be unloaded on RTD3, set flag to track */ + if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + return ret; + sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES); + } sst_hsw_dsp_runtime_suspend(hsw); sst_hsw_dsp_runtime_sleep(hsw); pdata->pm_state = HSW_PM_STATE_RTD3; @@ -1139,6 +1254,19 @@ static int hsw_pcm_runtime_resume(struct device *dev) else if (ret == 1) /* no action required */ return 0; + /* check flag when resume */ + if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) { + ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0); + if (ret < 0) + return ret; + /* put parameters from buffer to dsp */ + ret = sst_hsw_launch_param_buf(hsw); + if (ret < 0) + return ret; + /* unset flag */ + sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES); + } + pdata->pm_state = HSW_PM_STATE_D0; return ret; } diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c index 07f77815a586..b05fb1c1a848 100644 --- a/sound/soc/jz4740/jz4740-i2s.c +++ b/sound/soc/jz4740/jz4740-i2s.c @@ -58,6 +58,12 @@ #define JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 12 #define JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 8 +#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET 24 +#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET 16 +#define JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_MASK \ + (0xf << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) +#define JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_MASK \ + (0x1f << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) #define JZ_AIC_CTRL_OUTPUT_SAMPLE_SIZE_MASK (0x7 << 19) #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK (0x7 << 16) @@ -79,6 +85,7 @@ #define JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET 16 #define JZ_AIC_I2S_FMT_DISABLE_BIT_CLK BIT(12) +#define JZ_AIC_I2S_FMT_DISABLE_BIT_ICLK BIT(13) #define JZ_AIC_I2S_FMT_ENABLE_SYS_CLK BIT(4) #define JZ_AIC_I2S_FMT_MSB BIT(0) @@ -87,6 +94,13 @@ #define JZ_AIC_CLK_DIV_MASK 0xf #define I2SDIV_DV_SHIFT 8 #define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT) +#define I2SDIV_IDV_SHIFT 8 +#define I2SDIV_IDV_MASK (0xf << I2SDIV_IDV_SHIFT) + +enum jz47xx_i2s_version { + JZ_I2S_JZ4740, + JZ_I2S_JZ4780, +}; struct jz4740_i2s { struct resource *mem; @@ -98,6 +112,8 @@ struct jz4740_i2s { struct snd_dmaengine_dai_dma_data playback_dma_data; struct snd_dmaengine_dai_dma_data capture_dma_data; + + enum jz47xx_i2s_version version; }; static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, @@ -267,13 +283,22 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; else ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; + + div_reg &= ~I2SDIV_DV_MASK; + div_reg |= (div - 1) << I2SDIV_DV_SHIFT; } else { ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; + + if (i2s->version >= JZ_I2S_JZ4780) { + div_reg &= ~I2SDIV_IDV_MASK; + div_reg |= (div - 1) << I2SDIV_IDV_SHIFT; + } else { + div_reg &= ~I2SDIV_DV_MASK; + div_reg |= (div - 1) << I2SDIV_DV_SHIFT; + } } - div_reg &= ~I2SDIV_DV_MASK; - div_reg |= (div - 1) << I2SDIV_DV_SHIFT; jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg); @@ -369,11 +394,19 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, &i2s->capture_dma_data); - conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | - (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | - JZ_AIC_CONF_OVERFLOW_PLAY_LAST | - JZ_AIC_CONF_I2S | - JZ_AIC_CONF_INTERNAL_CODEC; + if (i2s->version >= JZ_I2S_JZ4780) { + conf = (7 << JZ4780_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ4780_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | + JZ_AIC_CONF_OVERFLOW_PLAY_LAST | + JZ_AIC_CONF_I2S | + JZ_AIC_CONF_INTERNAL_CODEC; + } else { + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | + JZ_AIC_CONF_OVERFLOW_PLAY_LAST | + JZ_AIC_CONF_I2S | + JZ_AIC_CONF_INTERNAL_CODEC; + } jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, JZ_AIC_CONF_RESET); jz4740_i2s_write(i2s, JZ_REG_AIC_CONF, conf); @@ -422,13 +455,34 @@ static struct snd_soc_dai_driver jz4740_i2s_dai = { .resume = jz4740_i2s_resume, }; +static struct snd_soc_dai_driver jz4780_i2s_dai = { + .probe = jz4740_i2s_dai_probe, + .remove = jz4740_i2s_dai_remove, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = JZ4740_I2S_FMTS, + }, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = JZ4740_I2S_FMTS, + }, + .ops = &jz4740_i2s_dai_ops, + .suspend = jz4740_i2s_suspend, + .resume = jz4740_i2s_resume, +}; + static const struct snd_soc_component_driver jz4740_i2s_component = { .name = "jz4740-i2s", }; #ifdef CONFIG_OF static const struct of_device_id jz4740_of_matches[] = { - { .compatible = "ingenic,jz4740-i2s" }, + { .compatible = "ingenic,jz4740-i2s", .data = (void *)JZ_I2S_JZ4740 }, + { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 }, { /* sentinel */ } }; #endif @@ -438,11 +492,16 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) struct jz4740_i2s *i2s; struct resource *mem; int ret; + const struct of_device_id *match; i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL); if (!i2s) return -ENOMEM; + match = of_match_device(jz4740_of_matches, &pdev->dev); + if (match) + i2s->version = (enum jz47xx_i2s_version)match->data; + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2s->base = devm_ioremap_resource(&pdev->dev, mem); if (IS_ERR(i2s->base)) @@ -460,8 +519,13 @@ static int jz4740_i2s_dev_probe(struct platform_device *pdev) platform_set_drvdata(pdev, i2s); - ret = devm_snd_soc_register_component(&pdev->dev, - &jz4740_i2s_component, &jz4740_i2s_dai, 1); + if (i2s->version == JZ_I2S_JZ4780) + ret = devm_snd_soc_register_component(&pdev->dev, + &jz4740_i2s_component, &jz4780_i2s_dai, 1); + else + ret = devm_snd_soc_register_component(&pdev->dev, + &jz4740_i2s_component, &jz4740_i2s_dai, 1); + if (ret) return ret; diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c index d19483081f9b..3a36d60e1785 100644 --- a/sound/soc/kirkwood/kirkwood-i2s.c +++ b/sound/soc/kirkwood/kirkwood-i2s.c @@ -643,7 +643,7 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev) } #ifdef CONFIG_OF -static struct of_device_id mvebu_audio_of_match[] = { +static const struct of_device_id mvebu_audio_of_match[] = { { .compatible = "marvell,kirkwood-audio" }, { .compatible = "marvell,dove-audio" }, { .compatible = "marvell,armada370-audio" }, diff --git a/sound/soc/nuc900/nuc900-audio.h b/sound/soc/nuc900/nuc900-audio.h index 59f7e8ed1a68..d0b725705914 100644 --- a/sound/soc/nuc900/nuc900-audio.h +++ b/sound/soc/nuc900/nuc900-audio.h @@ -100,10 +100,7 @@ struct nuc900_audio { void __iomem *mmio; spinlock_t lock; - dma_addr_t dma_addr[2]; - unsigned long buffersize[2]; unsigned long irq_num; - struct snd_pcm_substream *substream; struct resource *res; struct clk *clk; struct device *dev; diff --git a/sound/soc/nuc900/nuc900-pcm.c b/sound/soc/nuc900/nuc900-pcm.c index b809fa909e4d..5ae5ca15b6d6 100644 --- a/sound/soc/nuc900/nuc900-pcm.c +++ b/sound/soc/nuc900/nuc900-pcm.c @@ -42,29 +42,10 @@ static const struct snd_pcm_hardware nuc900_pcm_hardware = { static int nuc900_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_pcm_runtime *runtime = substream->runtime; - struct nuc900_audio *nuc900_audio = runtime->private_data; - unsigned long flags; - int ret = 0; - - ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); - if (ret < 0) - return ret; - - spin_lock_irqsave(&nuc900_audio->lock, flags); - - nuc900_audio->substream = substream; - nuc900_audio->dma_addr[substream->stream] = runtime->dma_addr; - nuc900_audio->buffersize[substream->stream] = - params_buffer_bytes(params); - - spin_unlock_irqrestore(&nuc900_audio->lock, flags); - - return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); } -static void nuc900_update_dma_register(struct snd_pcm_substream *substream, - dma_addr_t dma_addr, size_t count) +static void nuc900_update_dma_register(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct nuc900_audio *nuc900_audio = runtime->private_data; @@ -78,8 +59,8 @@ static void nuc900_update_dma_register(struct snd_pcm_substream *substream, mmio_len = nuc900_audio->mmio + ACTL_RDST_LENGTH; } - AUDIO_WRITE(mmio_addr, dma_addr); - AUDIO_WRITE(mmio_len, count); + AUDIO_WRITE(mmio_addr, runtime->dma_addr); + AUDIO_WRITE(mmio_len, runtime->dma_bytes); } static void nuc900_dma_start(struct snd_pcm_substream *substream) @@ -170,9 +151,7 @@ static int nuc900_dma_prepare(struct snd_pcm_substream *substream) spin_lock_irqsave(&nuc900_audio->lock, flags); - nuc900_update_dma_register(substream, - nuc900_audio->dma_addr[substream->stream], - nuc900_audio->buffersize[substream->stream]); + nuc900_update_dma_register(substream); val = AUDIO_READ(nuc900_audio->mmio + ACTL_RESET); diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index a2cd3486ac55..6768e4f7d7d0 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -100,17 +100,19 @@ config SND_OMAP_SOC_OMAP_TWL4030 config SND_OMAP_SOC_OMAP_ABE_TWL6040 tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST) + depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST) select SND_OMAP_SOC_DMIC select SND_OMAP_SOC_MCPDM select SND_SOC_TWL6040 select SND_SOC_DMIC + select COMMON_CLK_PALMAS if MFD_PALMAS help Say Y if you want to add support for SoC audio on OMAP boards using ABE and twl6040 codec. This driver currently supports: - SDP4430/Blaze boards - PandaBoard (4430) - PandaBoardES (4460) + - omap5-uevm (5432) config SND_OMAP_SOC_OMAP3_PANDORA tristate "SoC Audio support for OMAP3 Pandora" diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c index 706613077c15..16cc95fa4573 100644 --- a/sound/soc/omap/ams-delta.c +++ b/sound/soc/omap/ams-delta.c @@ -479,8 +479,8 @@ static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) /* Add hook switch - can be used to control the codec from userspace * even if line discipline fails */ - ret = snd_soc_jack_new(rtd->codec, "hook_switch", - SND_JACK_HEADSET, &ams_delta_hook_switch); + ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET, + &ams_delta_hook_switch, NULL, 0); if (ret) dev_warn(card->dev, "Failed to allocate resources for hook switch, " diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 5d7f9cebe041..dcb5336b5698 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_codec *codec = rtd->codec; snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); - n810_ext_control(&codec->dapm); + n810_ext_control(&rtd->card->dapm); return clk_prepare_enable(sys_clkout2); } @@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { n810_get_input, n810_set_input), }; -static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - /* Not connected */ - snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); - snd_soc_dapm_nc_pin(dapm, "HPLCOM"); - snd_soc_dapm_nc_pin(dapm, "HPRCOM"); - snd_soc_dapm_nc_pin(dapm, "MIC3L"); - snd_soc_dapm_nc_pin(dapm, "MIC3R"); - snd_soc_dapm_nc_pin(dapm, "LINE1R"); - snd_soc_dapm_nc_pin(dapm, "LINE2L"); - snd_soc_dapm_nc_pin(dapm, "LINE2R"); - - return 0; -} - /* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link n810_dai = { .name = "TLV320AIC33", @@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = { .codec_dai_name = "tlv320aic3x-hifi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM, - .init = n810_aic33_init, .ops = &n810_ops, }; @@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = { .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), .dapm_routes = audio_map, .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, }; static struct platform_device *n810_snd_device; diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c index b9c65f1ad5a8..0843a68f277c 100644 --- a/sound/soc/omap/omap-abe-twl6040.c +++ b/sound/soc/omap/omap-abe-twl6040.c @@ -182,17 +182,17 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) /* Headset jack detection only if it is supported */ if (priv->jack_detection) { - ret = snd_soc_jack_new(codec, "Headset Jack", - SND_JACK_HEADSET, &hs_jack); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET); } - return ret; + return 0; } static const struct snd_soc_dapm_route dmic_audio_map[] = { diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f7eb42aa3f38..4775da4c4db5 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c @@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; - iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID; - iec->status[1] = IEC958_AES1_CON_GENERAL; iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 1343ecbf0bd5..6bb623a2a4df 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -39,7 +39,7 @@ #define pcm_omap1510() 0 #endif -static const struct snd_pcm_hardware omap_pcm_hardware = { +static struct snd_pcm_hardware omap_pcm_hardware = { .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -53,6 +53,24 @@ static const struct snd_pcm_hardware omap_pcm_hardware = { .buffer_bytes_max = 128 * 1024, }; +/* sDMA supports only 1, 2, and 4 byte transfer elements. */ +static void omap_pcm_limit_supported_formats(void) +{ + int i; + + for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { + switch (snd_pcm_format_physical_width(i)) { + case 8: + case 16: + case 32: + omap_pcm_hardware.formats |= (1LL << i); + break; + default: + break; + } + } +} + /* this may get called several times by oss emulation */ static int omap_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) @@ -235,6 +253,7 @@ static struct snd_soc_platform_driver omap_soc_platform = { int omap_pcm_platform_register(struct device *dev) { + omap_pcm_limit_supported_formats(); return devm_snd_soc_register_platform(dev, &omap_soc_platform); } EXPORT_SYMBOL_GPL(omap_pcm_platform_register); diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c index fb1f6bb87cd4..3673ada43bfb 100644 --- a/sound/soc/omap/omap-twl4030.c +++ b/sound/soc/omap/omap-twl4030.c @@ -170,14 +170,10 @@ static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) if (priv->jack_detect > 0) { hs_jack_gpios[0].gpio = priv->jack_detect; - ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &priv->hs_jack); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&priv->hs_jack, - ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &priv->hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) return ret; diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c index 7f299357c2d2..c2ddf0fbfa28 100644 --- a/sound/soc/omap/rx51.c +++ b/sound/soc/omap/rx51.c @@ -311,9 +311,9 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) } /* AV jack detection */ - err = snd_soc_jack_new(codec, "AV Jack", - SND_JACK_HEADSET | SND_JACK_VIDEOOUT, - &rx51_av_jack); + err = snd_soc_card_jack_new(rtd->card, "AV Jack", + SND_JACK_HEADSET | SND_JACK_VIDEOOUT, + &rx51_av_jack, NULL, 0); if (err) { dev_err(card->dev, "Failed to add AV Jack\n"); return err; diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c index 73eb5ddf9753..9f8be7cd567e 100644 --- a/sound/soc/pxa/hx4700.c +++ b/sound/soc/pxa/hx4700.c @@ -126,17 +126,12 @@ static const struct snd_soc_dapm_route hx4700_audio_map[] = { */ static int hx4700_ak4641_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; int err; /* Jack detection API stuff */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pin), - hs_jack_pin); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, hs_jack_pin, + ARRAY_SIZE(hs_jack_pin)); if (err) return err; diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 910336c5ebeb..c20bbc042425 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -75,17 +75,12 @@ static struct snd_soc_card palm27x_asoc; static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; int err; /* Jack detection API stuff */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &hs_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (err) return err; diff --git a/sound/soc/pxa/ttc-dkb.c b/sound/soc/pxa/ttc-dkb.c index 5001dbb9b257..1753c7d9e760 100644 --- a/sound/soc/pxa/ttc-dkb.c +++ b/sound/soc/pxa/ttc-dkb.c @@ -78,15 +78,12 @@ static int ttc_pm860x_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_codec *codec = rtd->codec; /* Headset jack detection */ - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE - | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, - &hs_jack); - snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); - snd_soc_jack_new(codec, "Microphone Jack", SND_JACK_MICROPHONE, - &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2, + &hs_jack, hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); + snd_soc_card_jack_new(rtd->card, "Microphone Jack", SND_JACK_MICROPHONE, + &mic_jack, mic_jack_pins, + ARRAY_SIZE(mic_jack_pins)); /* headphone, microphone detection & headset short detection */ pm860x_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADPHONE, diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c index 76ccb172d0a7..bcbfbe8303f7 100644 --- a/sound/soc/pxa/z2.c +++ b/sound/soc/pxa/z2.c @@ -143,13 +143,9 @@ static int z2_wm8750_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "MONO1"); /* Jack detection API stuff */ - ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &hs_jack); - if (ret) - goto err; - - ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins), - hs_jack_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &hs_jack, hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); if (ret) goto err; diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig new file mode 100644 index 000000000000..5f58e4f1bca9 --- /dev/null +++ b/sound/soc/qcom/Kconfig @@ -0,0 +1,25 @@ +config SND_SOC_QCOM + tristate "ASoC support for QCOM platforms" + help + Say Y or M if you want to add support to use audio devices + in Qualcomm Technologies SOC-based platforms. + +config SND_SOC_LPASS_CPU + tristate + depends on SND_SOC_QCOM + select REGMAP_MMIO + +config SND_SOC_LPASS_PLATFORM + tristate + depends on SND_SOC_QCOM + select REGMAP_MMIO + +config SND_SOC_STORM + tristate "ASoC I2S support for Storm boards" + depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + select SND_SOC_MAX98357A + help + Say Y or M if you want add support for SoC audio on the + Qualcomm Technologies IPQ806X-based Storm board. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile new file mode 100644 index 000000000000..c5ce96c761c4 --- /dev/null +++ b/sound/soc/qcom/Makefile @@ -0,0 +1,11 @@ +# Platform +snd-soc-lpass-cpu-objs := lpass-cpu.o +snd-soc-lpass-platform-objs := lpass-platform.o + +obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o +obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o + +# Machine +snd-soc-storm-objs := storm.o + +obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c new file mode 100644 index 000000000000..6698d058de29 --- /dev/null +++ b/sound/soc/qcom/lpass-cpu.c @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS + */ + +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> +#include "lpass-lpaif-ipq806x.h" +#include "lpass.h" + +static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); + if (ret) + dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", + __func__, freq, ret); + + return ret; +} + +static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(drvdata->mi2s_osr_clk); + if (ret) { + dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", + __func__, ret); + return ret; + } + + ret = clk_prepare_enable(drvdata->mi2s_bit_clk); + if (ret) { + dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", + __func__, ret); + clk_disable_unprepare(drvdata->mi2s_osr_clk); + return ret; + } + + return 0; +} + +static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(drvdata->mi2s_bit_clk); + clk_disable_unprepare(drvdata->mi2s_osr_clk); +} + +static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + snd_pcm_format_t format = params_format(params); + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int regval; + int bitwidth, ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(dai->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | + LPAIF_I2SCTL_WSSRC_INTERNAL; + + switch (bitwidth) { + case 16: + regval |= LPAIF_I2SCTL_BITWIDTH_16; + break; + case 24: + regval |= LPAIF_I2SCTL_BITWIDTH_24; + break; + case 32: + regval |= LPAIF_I2SCTL_BITWIDTH_32; + break; + default: + dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", + __func__, bitwidth); + return -EINVAL; + } + + switch (channels) { + case 1: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_MONO; + break; + case 2: + regval |= LPAIF_I2SCTL_SPKMODE_SD0; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 4: + regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 6: + regval |= LPAIF_I2SCTL_SPKMODE_6CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + case 8: + regval |= LPAIF_I2SCTL_SPKMODE_8CH; + regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + break; + default: + dev_err(dai->dev, "%s() invalid channels given: %u\n", + __func__, channels); + return -EINVAL; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval); + if (ret) { + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + return ret; + } + + ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2); + if (ret) { + dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", + __func__, rate * bitwidth * 2, ret); + return ret; + } + + return 0; +} + +static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + + return ret; +} + +static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + + return ret; +} + +static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_SPKEN_MASK, + LPAIF_I2SCTL_SPKEN_ENABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), + LPAIF_I2SCTL_SPKEN_MASK, + LPAIF_I2SCTL_SPKEN_DISABLE); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + break; + } + + return ret; +} + +static struct snd_soc_dai_ops lpass_cpu_dai_ops = { + .set_sysclk = lpass_cpu_daiops_set_sysclk, + .startup = lpass_cpu_daiops_startup, + .shutdown = lpass_cpu_daiops_shutdown, + .hw_params = lpass_cpu_daiops_hw_params, + .hw_free = lpass_cpu_daiops_hw_free, + .prepare = lpass_cpu_daiops_prepare, + .trigger = lpass_cpu_daiops_trigger, +}; + +static int lpass_cpu_dai_probe(struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + int ret; + + /* ensure audio hardware is disabled */ + ret = regmap_write(drvdata->lpaif_map, + LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); + if (ret) + dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", + __func__, ret); + + return ret; +} + +static struct snd_soc_dai_driver lpass_cpu_dai_driver = { + .playback = { + .stream_name = "lpass-cpu-playback", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000, + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 1, + .channels_max = 8, + }, + .probe = &lpass_cpu_dai_probe, + .ops = &lpass_cpu_dai_ops, +}; + +static const struct snd_soc_component_driver lpass_cpu_comp_driver = { + .name = "lpass-cpu", +}; + +static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) + if (reg == LPAIF_I2SCTL_REG(i)) + return true; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { + if (reg == LPAIF_IRQEN_REG(i)) + return true; + if (reg == LPAIF_IRQCLEAR_REG(i)) + return true; + } + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { + if (reg == LPAIF_RDMACTL_REG(i)) + return true; + if (reg == LPAIF_RDMABASE_REG(i)) + return true; + if (reg == LPAIF_RDMABUFF_REG(i)) + return true; + if (reg == LPAIF_RDMAPER_REG(i)) + return true; + } + + return false; +} + +static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) + if (reg == LPAIF_I2SCTL_REG(i)) + return true; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { + if (reg == LPAIF_IRQEN_REG(i)) + return true; + if (reg == LPAIF_IRQSTAT_REG(i)) + return true; + } + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { + if (reg == LPAIF_RDMACTL_REG(i)) + return true; + if (reg == LPAIF_RDMABASE_REG(i)) + return true; + if (reg == LPAIF_RDMABUFF_REG(i)) + return true; + if (reg == LPAIF_RDMACURR_REG(i)) + return true; + if (reg == LPAIF_RDMAPER_REG(i)) + return true; + } + + return false; +} + +static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) +{ + int i; + + for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) + if (reg == LPAIF_IRQSTAT_REG(i)) + return true; + + for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) + if (reg == LPAIF_RDMACURR_REG(i)) + return true; + + return false; +} + +static const struct regmap_config lpass_cpu_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX), + .writeable_reg = lpass_cpu_regmap_writeable, + .readable_reg = lpass_cpu_regmap_readable, + .volatile_reg = lpass_cpu_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + +static int lpass_cpu_platform_probe(struct platform_device *pdev) +{ + struct lpass_data *drvdata; + struct device_node *dsp_of_node; + struct resource *res; + int ret; + + dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); + if (dsp_of_node) { + dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", + __func__); + return -EBUSY; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + platform_set_drvdata(pdev, drvdata); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); + if (!res) { + dev_err(&pdev->dev, "%s() error getting resource\n", __func__); + return -ENODEV; + } + + drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR((void const __force *)drvdata->lpaif)) { + dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", + __func__, + PTR_ERR((void const __force *)drvdata->lpaif)); + return PTR_ERR((void const __force *)drvdata->lpaif); + } + + drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, + &lpass_cpu_regmap_config); + if (IS_ERR(drvdata->lpaif_map)) { + dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", + __func__, PTR_ERR(drvdata->lpaif_map)); + return PTR_ERR(drvdata->lpaif_map); + } + + drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk"); + if (IS_ERR(drvdata->mi2s_osr_clk)) { + dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n", + __func__, PTR_ERR(drvdata->mi2s_osr_clk)); + return PTR_ERR(drvdata->mi2s_osr_clk); + } + + drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk"); + if (IS_ERR(drvdata->mi2s_bit_clk)) { + dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", + __func__, PTR_ERR(drvdata->mi2s_bit_clk)); + return PTR_ERR(drvdata->mi2s_bit_clk); + } + + drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", + __func__, PTR_ERR(drvdata->ahbix_clk)); + return PTR_ERR(drvdata->ahbix_clk); + } + + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + if (ret) { + dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", + __func__, ret); + return ret; + } + dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", + __func__, ret); + return ret; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); + if (ret) { + dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", + __func__, ret); + goto err_clk; + } + + ret = asoc_qcom_lpass_platform_register(pdev); + if (ret) { + dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", + __func__, ret); + goto err_clk; + } + + return 0; + +err_clk: + clk_disable_unprepare(drvdata->ahbix_clk); + return ret; +} + +static int lpass_cpu_platform_remove(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(drvdata->ahbix_clk); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id lpass_cpu_device_id[] = { + { .compatible = "qcom,lpass-cpu" }, + {} +}; +MODULE_DEVICE_TABLE(of, lpass_cpu_device_id); +#endif + +static struct platform_driver lpass_cpu_platform_driver = { + .driver = { + .name = "lpass-cpu", + .of_match_table = of_match_ptr(lpass_cpu_device_id), + }, + .probe = lpass_cpu_platform_probe, + .remove = lpass_cpu_platform_remove, +}; +module_platform_driver(lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("QTi LPASS CPU Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-ipq806x.h new file mode 100644 index 000000000000..dc423b888842 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif-ipq806x.h @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS + */ + +#ifndef __LPASS_LPAIF_H__ +#define __LPASS_LPAIF_H__ + +#define LPAIF_BANK_OFFSET 0x1000 + +/* LPAIF I2S */ + +#define LPAIF_I2SCTL_REG_BASE 0x0010 +#define LPAIF_I2SCTL_REG_STRIDE 0x4 +#define LPAIF_I2SCTL_REG_ADDR(addr, port) \ + (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port))) + +enum lpaif_i2s_ports { + LPAIF_I2S_PORT_MIN = 0, + + LPAIF_I2S_PORT_CODEC_SPK = 0, + LPAIF_I2S_PORT_CODEC_MIC = 1, + LPAIF_I2S_PORT_SEC_SPK = 2, + LPAIF_I2S_PORT_SEC_MIC = 3, + LPAIF_I2S_PORT_MI2S = 4, + + LPAIF_I2S_PORT_MAX = 4, + LPAIF_I2S_PORT_NUM = 5, +}; + +#define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port)) + +#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 +#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 +#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) +#define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) + +#define LPAIF_I2SCTL_SPKEN_MASK 0x4000 +#define LPAIF_I2SCTL_SPKEN_SHIFT 14 +#define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) +#define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) + +#define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 +#define LPAIF_I2SCTL_SPKMODE_SHIFT 10 +#define LPAIF_I2SCTL_SPKMODE_NONE (0 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD0 (1 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD1 (2 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD2 (3 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_SD3 (4 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_QUAD01 (5 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_QUAD23 (6 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_6CH (7 << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE_8CH (8 << LPAIF_I2SCTL_SPKMODE_SHIFT) + +#define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 +#define LPAIF_I2SCTL_SPKMONO_SHIFT 9 +#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) + +#define LPAIF_I2SCTL_WSSRC_MASK 0x0004 +#define LPAIF_I2SCTL_WSSRC_SHIFT 2 +#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) +#define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) + +#define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 +#define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 +#define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) + +/* LPAIF IRQ */ + +#define LPAIF_IRQ_REG_BASE 0x3000 +#define LPAIF_IRQ_REG_STRIDE 0x1000 +#define LPAIF_IRQ_REG_ADDR(addr, port) \ + (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port))) + +enum lpaif_irq_ports { + LPAIF_IRQ_PORT_MIN = 0, + + LPAIF_IRQ_PORT_HOST = 0, + LPAIF_IRQ_PORT_ADSP = 1, + + LPAIF_IRQ_PORT_MAX = 2, + LPAIF_IRQ_PORT_NUM = 3, +}; + +#define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port)) +#define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port)) +#define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port)) + +#define LPAIF_IRQ_BITSTRIDE 3 +#define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) +#define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan))) +#define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) +#define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) + +/* LPAIF DMA */ + +#define LPAIF_RDMA_REG_BASE 0x6000 +#define LPAIF_RDMA_REG_STRIDE 0x1000 +#define LPAIF_RDMA_REG_ADDR(addr, chan) \ + (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan))) + +enum lpaif_dma_channels { + LPAIF_RDMA_CHAN_MIN = 0, + + LPAIF_RDMA_CHAN_MI2S = 0, + LPAIF_RDMA_CHAN_PCM0 = 1, + LPAIF_RDMA_CHAN_PCM1 = 2, + + LPAIF_RDMA_CHAN_MAX = 4, + LPAIF_RDMA_CHAN_NUM = 5, +}; + +#define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan)) +#define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan)) +#define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan)) +#define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan)) +#define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan)) + +#define LPAIF_RDMACTL_BURSTEN_MASK 0x800 +#define LPAIF_RDMACTL_BURSTEN_SHIFT 11 +#define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) +#define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) + +#define LPAIF_RDMACTL_WPSCNT_MASK 0x700 +#define LPAIF_RDMACTL_WPSCNT_SHIFT 8 +#define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) +#define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) + +#define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 +#define LPAIF_RDMACTL_AUDINTF_SHIFT 4 +#define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT) +#define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT) + +#define LPAIF_RDMACTL_FIFOWM_MASK 0x00E +#define LPAIF_RDMACTL_FIFOWM_SHIFT 1 +#define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) +#define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) + +#define LPAIF_RDMACTL_ENABLE_MASK 0x1 +#define LPAIF_RDMACTL_ENABLE_SHIFT 0 +#define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) +#define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) + +#endif /* __LPASS_LPAIF_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c new file mode 100644 index 000000000000..2fa6280dfb23 --- /dev/null +++ b/sound/soc/qcom/lpass-platform.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS + */ + +#include <linux/compiler.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <sound/memalloc.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/regmap.h> +#include <sound/soc.h> +#include "lpass-lpaif-ipq806x.h" +#include "lpass.h" + +#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) +#define LPASS_PLATFORM_PERIODS 2 + +static struct snd_pcm_hardware lpass_platform_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE, + .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE / + LPASS_PLATFORM_PERIODS, + .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE / + LPASS_PLATFORM_PERIODS, + .periods_min = LPASS_PLATFORM_PERIODS, + .periods_max = LPASS_PLATFORM_PERIODS, + .fifo_size = 0, +}; + +static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + int ret; + + snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); + + runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n", + __func__, ret); + return -EINVAL; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + snd_pcm_format_t format = params_format(params); + unsigned int channels = params_channels(params); + unsigned int regval; + int bitwidth; + int ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + regval = LPAIF_RDMACTL_BURSTEN_INCR4 | + LPAIF_RDMACTL_AUDINTF_MI2S | + LPAIF_RDMACTL_FIFOWM_8; + + switch (bitwidth) { + case 16: + switch (channels) { + case 1: + case 2: + regval |= LPAIF_RDMACTL_WPSCNT_ONE; + break; + case 4: + regval |= LPAIF_RDMACTL_WPSCNT_TWO; + break; + case 6: + regval |= LPAIF_RDMACTL_WPSCNT_THREE; + break; + case 8: + regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + break; + case 24: + case 32: + switch (channels) { + case 1: + regval |= LPAIF_RDMACTL_WPSCNT_ONE; + break; + case 2: + regval |= LPAIF_RDMACTL_WPSCNT_TWO; + break; + case 4: + regval |= LPAIF_RDMACTL_WPSCNT_FOUR; + break; + case 6: + regval |= LPAIF_RDMACTL_WPSCNT_SIX; + break; + case 8: + regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + break; + default: + dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", + __func__, bitwidth, channels); + return -EINVAL; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); + if (ret) + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + + return ret; +} + +static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), + runtime->dma_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S), + (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S), + (snd_pcm_lib_period_bytes(substream) >> 2) - 1); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* clear status before enabling interrupts */ + ret = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), + LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_ENABLE_MASK, + LPAIF_RDMACTL_ENABLE_ON); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), + LPAIF_RDMACTL_ENABLE_MASK, + LPAIF_RDMACTL_ENABLE_OFF); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_update_bits(drvdata->lpaif_map, + LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + break; + } + + return 0; +} + +static snd_pcm_uframes_t lpass_platform_pcmops_pointer( + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + unsigned int base_addr, curr_addr; + int ret; + + ret = regmap_read(drvdata->lpaif_map, + LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", + __func__, ret); + return ret; + } + + ret = regmap_read(drvdata->lpaif_map, + LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr); + if (ret) { + dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", + __func__, ret); + return ret; + } + + return bytes_to_frames(substream->runtime, curr_addr - base_addr); +} + +static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + return dma_mmap_coherent(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, + runtime->dma_bytes); +} + +static struct snd_pcm_ops lpass_platform_pcm_ops = { + .open = lpass_platform_pcmops_open, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = lpass_platform_pcmops_hw_params, + .hw_free = lpass_platform_pcmops_hw_free, + .prepare = lpass_platform_pcmops_prepare, + .trigger = lpass_platform_pcmops_trigger, + .pointer = lpass_platform_pcmops_pointer, + .mmap = lpass_platform_pcmops_mmap, +}; + +static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) +{ + struct snd_pcm_substream *substream = data; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + unsigned int interrupts; + irqreturn_t ret = IRQ_NONE; + int rv; + + rv = regmap_read(drvdata->lpaif_map, + LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts); + if (rv) { + dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S); + + if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + snd_pcm_period_elapsed(substream); + ret = IRQ_HANDLED; + } + + if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__); + snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); + ret = IRQ_HANDLED; + } + + if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) { + rv = regmap_write(drvdata->lpaif_map, + LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), + LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)); + if (rv) { + dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", + __func__, rv); + return IRQ_NONE; + } + dev_err(soc_runtime->dev, "%s() bus access error\n", __func__); + snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = soc_runtime->dev; + buf->private_data = NULL; + buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, + GFP_KERNEL); + if (!buf->area) { + dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", + __func__); + return -ENOMEM; + } + buf->bytes = size; + + return 0; +} + +static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, + struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_dma_buffer *buf = &substream->dma_buffer; + + if (buf->area) { + dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, + buf->addr); + } + buf->area = NULL; +} + +static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) +{ + struct snd_pcm *pcm = soc_runtime->pcm; + struct snd_pcm_substream *substream = + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct lpass_data *drvdata = + snd_soc_platform_get_drvdata(soc_runtime->platform); + int ret; + + soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); + soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; + + ret = lpass_platform_alloc_buffer(substream, soc_runtime); + if (ret) + return ret; + + ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq, + lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING, + "lpass-irq-lpaif", substream); + if (ret) { + dev_err(soc_runtime->dev, "%s() irq request failed: %d\n", + __func__, ret); + goto err_buf; + } + + /* ensure audio hardware is disabled */ + ret = regmap_write(drvdata->lpaif_map, + LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", + __func__, ret); + return ret; + } + ret = regmap_write(drvdata->lpaif_map, + LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); + if (ret) { + dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", + __func__, ret); + return ret; + } + + return 0; + +err_buf: + lpass_platform_free_buffer(substream, soc_runtime); + return ret; +} + +static void lpass_platform_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream = + pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + + lpass_platform_free_buffer(substream, soc_runtime); +} + +static struct snd_soc_platform_driver lpass_platform_driver = { + .pcm_new = lpass_platform_pcm_new, + .pcm_free = lpass_platform_pcm_free, + .ops = &lpass_platform_pcm_ops, +}; + +int asoc_qcom_lpass_platform_register(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); + if (drvdata->lpaif_irq < 0) { + dev_err(&pdev->dev, "%s() error getting irq handle: %d\n", + __func__, drvdata->lpaif_irq); + return -ENODEV; + } + + return devm_snd_soc_register_platform(&pdev->dev, + &lpass_platform_driver); +} +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register); + +MODULE_DESCRIPTION("QTi LPASS Platform Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h new file mode 100644 index 000000000000..5c99b3dace86 --- /dev/null +++ b/sound/soc/qcom/lpass.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * lpass.h - Definitions for the QTi LPASS + */ + +#ifndef __LPASS_H__ +#define __LPASS_H__ + +#include <linux/clk.h> +#include <linux/compiler.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 + +/* Both the CPU DAI and platform drivers will access this data */ +struct lpass_data { + + /* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */ + struct clk *ahbix_clk; + + /* MI2S system clock */ + struct clk *mi2s_osr_clk; + + /* MI2S bit clock (derived from system clock by a divider */ + struct clk *mi2s_bit_clk; + + /* low-power audio interface (LPAIF) registers */ + void __iomem *lpaif; + + /* regmap backed by the low-power audio interface (LPAIF) registers */ + struct regmap *lpaif_map; + + /* interrupts from the low-power audio interface (LPAIF) */ + int lpaif_irq; +}; + +/* register the platform driver from the CPU DAI driver */ +int asoc_qcom_lpass_platform_register(struct platform_device *); + +#endif /* __LPASS_H__ */ diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c new file mode 100644 index 000000000000..b8bd296190ad --- /dev/null +++ b/sound/soc/qcom/storm.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define STORM_SYSCLK_MULT 4 + +static int storm_ops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; + struct snd_soc_card *card = soc_runtime->card; + snd_pcm_format_t format = params_format(params); + unsigned int rate = params_rate(params); + unsigned int sysclk_freq; + int bitwidth, ret; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(card->dev, "%s() invalid bit width given: %d\n", + __func__, bitwidth); + return bitwidth; + } + + /* + * as the CPU DAI is the I2S bus master and no system clock is needed by + * the MAX98357a DAC, simply set the system clock to be a constant + * multiple of the bit clock for the clock divider + */ + sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; + + ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); + if (ret) { + dev_err(card->dev, "%s() error setting sysclk to %u: %d\n", + __func__, sysclk_freq, ret); + return ret; + } + + return 0; +} + +static struct snd_soc_ops storm_soc_ops = { + .hw_params = storm_ops_hw_params, +}; + +static struct snd_soc_dai_link storm_dai_link = { + .name = "Primary", + .stream_name = "Primary", + .codec_dai_name = "HiFi", + .ops = &storm_soc_ops, +}; + +static struct snd_soc_card storm_soc_card = { + .name = "ipq806x-storm", + .dev = NULL, +}; + +static int storm_parse_of(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link = card->dai_link; + struct device_node *np = card->dev->of_node; + + dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0); + if (!dai_link->cpu_of_node) { + dev_err(card->dev, "%s() error getting cpu phandle\n", + __func__); + return -EINVAL; + } + dai_link->platform_of_node = dai_link->cpu_of_node; + + dai_link->codec_of_node = of_parse_phandle(np, "codec", 0); + if (!dai_link->codec_of_node) { + dev_err(card->dev, "%s() error getting codec phandle\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int storm_platform_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &storm_soc_card; + int ret; + + if (card->dev) { + dev_err(&pdev->dev, "%s() error, existing soundcard\n", + __func__); + return -ENODEV; + } + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + + ret = snd_soc_of_parse_card_name(card, "qcom,model"); + if (ret) { + dev_err(&pdev->dev, "%s() error parsing card name: %d\n", + __func__, ret); + return ret; + } + + card->dai_link = &storm_dai_link; + card->num_links = 1; + + ret = storm_parse_of(card); + if (ret) { + dev_err(&pdev->dev, "%s() error resolving dai links: %d\n", + __func__, ret); + return ret; + } + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret == -EPROBE_DEFER) { + card->dev = NULL; + return ret; + } else if (ret) { + dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id storm_device_id[] = { + { .compatible = "google,storm-audio" }, + {}, +}; +MODULE_DEVICE_TABLE(of, storm_device_id); +#endif + +static struct platform_driver storm_platform_driver = { + .driver = { + .name = "storm-audio", + .of_match_table = + of_match_ptr(storm_device_id), + }, + .probe = storm_platform_probe, +}; +module_platform_driver(storm_platform_driver); + +MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index 59b044255b78..c72e9fb26658 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -162,13 +162,8 @@ static struct platform_device *s3c24xx_snd_device; static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &hp_jack); - - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), hp_jack_gpios); diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c index 141519c21e21..31a820eb0ac3 100644 --- a/sound/soc/samsung/littlemill.c +++ b/sound/soc/samsung/littlemill.c @@ -260,12 +260,12 @@ static int littlemill_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_MECHANICAL | - SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3 | - SND_JACK_BTN_4 | SND_JACK_BTN_5, - &littlemill_headset); + ret = snd_soc_card_jack_new(card, "Headset", + SND_JACK_HEADSET | SND_JACK_MECHANICAL | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5, + &littlemill_headset, NULL, 0); if (ret) return ret; diff --git a/sound/soc/samsung/lowland.c b/sound/soc/samsung/lowland.c index 243dea7ba38f..5f156093101e 100644 --- a/sound/soc/samsung/lowland.c +++ b/sound/soc/samsung/lowland.c @@ -56,16 +56,10 @@ static int lowland_wm5100_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET | - SND_JACK_BTN_0, - &lowland_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&lowland_headset, - ARRAY_SIZE(lowland_headset_pins), - lowland_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | + SND_JACK_HEADSET | SND_JACK_BTN_0, + &lowland_headset, lowland_headset_pins, + ARRAY_SIZE(lowland_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 873f2cb4bebe..35e37c457f1f 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -211,13 +211,8 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream, static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; - - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &hp_jack); - - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &hp_jack, hp_jack_pins, ARRAY_SIZE(hp_jack_pins)); snd_soc_jack_add_gpios(&hp_jack, ARRAY_SIZE(hp_jack_gpios), hp_jack_gpios); diff --git a/sound/soc/samsung/smartq_wm8987.c b/sound/soc/samsung/smartq_wm8987.c index 8291d2a5f152..dfbe2db1c407 100644 --- a/sound/soc/samsung/smartq_wm8987.c +++ b/sound/soc/samsung/smartq_wm8987.c @@ -151,13 +151,10 @@ static int smartq_wm8987_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_disable_pin(dapm, "Headphone Jack"); /* Headphone jack detection */ - err = snd_soc_jack_new(codec, "Headphone Jack", - SND_JACK_HEADPHONE, &smartq_jack); - if (err) - return err; - - err = snd_soc_jack_add_pins(&smartq_jack, ARRAY_SIZE(smartq_jack_pins), - smartq_jack_pins); + err = snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &smartq_jack, + smartq_jack_pins, + ARRAY_SIZE(smartq_jack_pins)); if (err) return err; diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c index 5ec7c52282f2..2dcb988bdff2 100644 --- a/sound/soc/samsung/speyside.c +++ b/sound/soc/samsung/speyside.c @@ -153,16 +153,10 @@ static int speyside_wm8996_init(struct snd_soc_pcm_runtime *rtd) pr_err("Failed to request HP_SEL GPIO: %d\n", ret); gpio_direction_output(WM8996_HPSEL_GPIO, speyside_jack_polarity); - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET | - SND_JACK_BTN_0, - &speyside_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&speyside_headset, - ARRAY_SIZE(speyside_headset_pins), - speyside_headset_pins); + ret = snd_soc_card_jack_new(rtd->card, "Headset", SND_JACK_LINEOUT | + SND_JACK_HEADSET | SND_JACK_BTN_0, + &speyside_headset, speyside_headset_pins, + ARRAY_SIZE(speyside_headset_pins)); if (ret) return ret; diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c index 9c80506527c4..85ccfb7188cb 100644 --- a/sound/soc/samsung/tobermory.c +++ b/sound/soc/samsung/tobermory.c @@ -179,15 +179,10 @@ static int tobermory_late_probe(struct snd_soc_card *card) if (ret < 0) return ret; - ret = snd_soc_jack_new(codec, "Headset", - SND_JACK_HEADSET | SND_JACK_BTN_0, - &tobermory_headset); - if (ret) - return ret; - - ret = snd_soc_jack_add_pins(&tobermory_headset, - ARRAY_SIZE(tobermory_headset_pins), - tobermory_headset_pins); + ret = snd_soc_card_jack_new(card, "Headset", SND_JACK_HEADSET | + SND_JACK_BTN_0, &tobermory_headset, + tobermory_headset_pins, + ARRAY_SIZE(tobermory_headset_pins)); if (ret) return ret; diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 80245b6eebd6..07114b0b0dc1 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig @@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU config SND_SOC_RCAR tristate "R-Car series SRU/SCU/SSIU/SSI support" + depends on DMA_OF select SND_SIMPLE_CARD select REGMAP_MMIO help This option enables R-Car SUR/SCU/SSIU/SSI sound support +config SND_SOC_RSRC_CARD + tristate "Renesas Sampling Rate Convert Sound Card" + help + This option enables simple sound if you need sampling rate convert + ## ## Boards ## diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index b87b22e88e43..0c2af21b0b82 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -1876,7 +1876,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } -static struct of_device_id fsi_of_match[]; +static const struct fsi_core fsi1_core = { + .ver = 1, + + /* Interrupt */ + .int_st = INT_ST, + .iemsk = IEMSK, + .imsk = IMSK, +}; + +static const struct fsi_core fsi2_core = { + .ver = 2, + + /* Interrupt */ + .int_st = CPU_INT_ST, + .iemsk = CPU_IEMSK, + .imsk = CPU_IMSK, + .a_mclk = A_MST_CTLR, + .b_mclk = B_MST_CTLR, +}; + +static const struct of_device_id fsi_of_match[] = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + +static const struct platform_device_id fsi_id_table[] = { + { "sh_fsi", (kernel_ulong_t)&fsi1_core }, + { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, + {}, +}; +MODULE_DEVICE_TABLE(platform, fsi_id_table); + static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; @@ -2072,40 +2105,6 @@ static struct dev_pm_ops fsi_pm_ops = { .resume = fsi_resume, }; -static struct fsi_core fsi1_core = { - .ver = 1, - - /* Interrupt */ - .int_st = INT_ST, - .iemsk = IEMSK, - .imsk = IMSK, -}; - -static struct fsi_core fsi2_core = { - .ver = 2, - - /* Interrupt */ - .int_st = CPU_INT_ST, - .iemsk = CPU_IEMSK, - .imsk = CPU_IMSK, - .a_mclk = A_MST_CTLR, - .b_mclk = B_MST_CTLR, -}; - -static struct of_device_id fsi_of_match[] = { - { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, - { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, - {}, -}; -MODULE_DEVICE_TABLE(of, fsi_of_match); - -static struct platform_device_id fsi_id_table[] = { - { "sh_fsi", (kernel_ulong_t)&fsi1_core }, - { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, - {}, -}; -MODULE_DEVICE_TABLE(platform, fsi_id_table); - static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", @@ -2119,7 +2118,7 @@ static struct platform_driver fsi_driver = { module_platform_driver(fsi_driver); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("SuperH onchip FSI audio driver"); MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); MODULE_ALIAS("platform:fsi-pcm-audio"); diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9ac536429800..f1b445173fba 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile @@ -1,2 +1,5 @@ -snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o -obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
\ No newline at end of file +snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o +obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o + +snd-soc-rsrc-card-objs := rsrc-card.o +obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 7ac35c9d1cb8..fefc881dbac2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, rsnd_mod_bset(mod, DIV_EN, en, en); + dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); + return 0; } @@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev, priv->adg = adg; - dev_dbg(dev, "adg probed\n"); - return 0; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 110577c52317..9f48d75fa992 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -94,21 +94,20 @@ * */ #include <linux/pm_runtime.h> -#include <linux/shdma-base.h> #include "rsnd.h" #define RSND_RATES SNDRV_PCM_RATE_8000_96000 #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -static struct rsnd_of_data rsnd_of_data_gen1 = { +static const struct rsnd_of_data rsnd_of_data_gen1 = { .flags = RSND_GEN1, }; -static struct rsnd_of_data rsnd_of_data_gen2 = { +static const struct rsnd_of_data rsnd_of_data_gen2 = { .flags = RSND_GEN2, }; -static struct of_device_id rsnd_of_match[] = { +static const struct of_device_id rsnd_of_match[] = { { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, {}, @@ -138,249 +137,37 @@ char *rsnd_mod_name(struct rsnd_mod *mod) return mod->ops->name; } -char *rsnd_mod_dma_name(struct rsnd_mod *mod) +struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) { - if (!mod || !mod->ops) - return "unknown"; - - if (!mod->ops->dma_name) - return mod->ops->name; + if (!mod || !mod->ops || !mod->ops->dma_req) + return NULL; - return mod->ops->dma_name(mod); + return mod->ops->dma_req(mod); } -void rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, int id) { + int ret = clk_prepare(clk); + + if (ret) + return ret; + mod->id = id; mod->ops = ops; mod->type = type; mod->clk = clk; -} - -/* - * rsnd_dma functions - */ -void rsnd_dma_stop(struct rsnd_dma *dma) -{ - dmaengine_terminate_all(dma->chan); -} - -static void rsnd_dma_complete(void *data) -{ - struct rsnd_dma *dma = (struct rsnd_dma *)data; - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - - /* - * Renesas sound Gen1 needs 1 DMAC, - * Gen2 needs 2 DMAC. - * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. - * But, Audio-DMAC-peri-peri doesn't have interrupt, - * and this driver is assuming that here. - * - * If Audio-DMAC-peri-peri has interrpt, - * rsnd_dai_pointer_update() will be called twice, - * ant it will breaks io->byte_pos - */ - - rsnd_dai_pointer_update(io, io->byte_per_period); -} - -void rsnd_dma_start(struct rsnd_dma *dma) -{ - struct rsnd_mod *mod = rsnd_dma_to_mod(dma); - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - struct snd_pcm_substream *substream = io->substream; - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_async_tx_descriptor *desc; - - desc = dmaengine_prep_dma_cyclic(dma->chan, - (dma->addr) ? dma->addr : - substream->runtime->dma_addr, - snd_pcm_lib_buffer_bytes(substream), - snd_pcm_lib_period_bytes(substream), - dma->dir, - DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - - if (!desc) { - dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); - return; - } - - desc->callback = rsnd_dma_complete; - desc->callback_param = dma; - - if (dmaengine_submit(desc) < 0) { - dev_err(dev, "dmaengine_submit() fail\n"); - return; - } - - dma_async_issue_pending(dma->chan); -} - -int rsnd_dma_available(struct rsnd_dma *dma) -{ - return !!dma->chan; -} - -#define DMA_NAME_SIZE 16 -#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ -static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) -{ - if (mod) - return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", - rsnd_mod_dma_name(mod), rsnd_mod_id(mod)); - else - return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); - -} - -static void rsnd_dma_of_name(struct rsnd_mod *mod_from, - struct rsnd_mod *mod_to, - char *dma_name) -{ - int index = 0; - - index = _rsnd_dma_of_name(dma_name + index, mod_from); - *(dma_name + index++) = '_'; - index = _rsnd_dma_of_name(dma_name + index, mod_to); -} - -static void rsnd_dma_of_path(struct rsnd_dma *dma, - int is_play, - struct rsnd_mod **mod_from, - struct rsnd_mod **mod_to) -{ - struct rsnd_mod *this = rsnd_dma_to_mod(dma); - struct rsnd_dai_stream *io = rsnd_mod_to_io(this); - struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); - struct rsnd_mod *src = rsnd_io_to_mod_src(io); - struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); - struct rsnd_mod *mod[MOD_MAX]; - int i, index; - - for (i = 0; i < MOD_MAX; i++) - mod[i] = NULL; - - /* - * in play case... - * - * src -> dst - * - * mem -> SSI - * mem -> SRC -> SSI - * mem -> SRC -> DVC -> SSI - */ - mod[0] = NULL; /* for "mem" */ - index = 1; - for (i = 1; i < MOD_MAX; i++) { - if (!src) { - mod[i] = ssi; - } else if (!dvc) { - mod[i] = src; - src = NULL; - } else { - if ((!is_play) && (this == src)) - this = dvc; - - mod[i] = (is_play) ? src : dvc; - i++; - mod[i] = (is_play) ? dvc : src; - src = NULL; - dvc = NULL; - } - - if (mod[i] == this) - index = i; - - if (mod[i] == ssi) - break; - } - - if (is_play) { - *mod_from = mod[index - 1]; - *mod_to = mod[index]; - } else { - *mod_from = mod[index]; - *mod_to = mod[index - 1]; - } -} - -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct dma_slave_config cfg; - struct rsnd_mod *mod_from; - struct rsnd_mod *mod_to; - char dma_name[DMA_NAME_SIZE]; - dma_cap_mask_t mask; - int ret; - - if (dma->chan) { - dev_err(dev, "it already has dma channel\n"); - return -EIO; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - - rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); - rsnd_dma_of_name(mod_from, mod_to, dma_name); - - cfg.slave_id = id; - cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1); - cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0); - cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - - dev_dbg(dev, "dma : %s %pad -> %pad\n", - dma_name, &cfg.src_addr, &cfg.dst_addr); - - dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, - (void *)id, dev, - dma_name); - if (!dma->chan) { - dev_err(dev, "can't get dma channel\n"); - goto rsnd_dma_channel_err; - } - - ret = dmaengine_slave_config(dma->chan, &cfg); - if (ret < 0) - goto rsnd_dma_init_err; - - dma->addr = is_play ? cfg.src_addr : cfg.dst_addr; - dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; - - return 0; - -rsnd_dma_init_err: - rsnd_dma_quit(priv, dma); -rsnd_dma_channel_err: - - /* - * DMA failed. try to PIO mode - * see - * rsnd_ssi_fallback() - * rsnd_rdai_continuance_probe() - */ - return -EAGAIN; + return ret; } -void rsnd_dma_quit(struct rsnd_priv *priv, - struct rsnd_dma *dma) +void rsnd_mod_quit(struct rsnd_mod *mod) { - if (dma->chan) - dma_release_channel(dma->chan); - - dma->chan = NULL; + if (mod->clk) + clk_unprepare(mod->clk); } /* @@ -416,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) ({ \ struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ struct device *dev = rsnd_priv_to_dev(priv); \ - u32 mask = 1 << __rsnd_mod_shift_##func; \ + u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ int ret = 0; \ if ((mod->status & mask) == call) { \ @@ -458,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_err(dev, "%s%d is not empty\n", + dev_err(dev, "%s[%d] is not empty\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); return -EIO; @@ -874,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev, drv[i].name = rdai[i].name; drv[i].ops = &rsnd_soc_dai_ops; if (pmod) { + snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE, + "DAI%d Playback", i); + drv[i].playback.rates = RSND_RATES; drv[i].playback.formats = RSND_FMTS; drv[i].playback.channels_min = 2; drv[i].playback.channels_max = 2; + drv[i].playback.stream_name = rdai[i].playback.name; rdai[i].playback.info = &info->dai_info[i].playback; rdai[i].playback.rdai = rdai + i; rsnd_path_init(priv, &rdai[i], &rdai[i].playback); } if (cmod) { + snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE, + "DAI%d Capture", i); + drv[i].capture.rates = RSND_RATES; drv[i].capture.formats = RSND_FMTS; drv[i].capture.channels_min = 2; drv[i].capture.channels_max = 2; + drv[i].capture.stream_name = rdai[i].capture.name; rdai[i].capture.info = &info->dai_info[i].capture; rdai[i].capture.rdai = rdai + i; @@ -933,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream) static int rsnd_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { + struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); + struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); + struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + int ret; + + ret = rsnd_dai_call(hw_params, io, substream, hw_params); + if (ret) + return ret; + return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); } @@ -1197,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev) const struct rsnd_of_data *of_data, struct rsnd_priv *priv) = { rsnd_gen_probe, + rsnd_dma_probe, rsnd_ssi_probe, rsnd_src_probe, rsnd_dvc_probe, @@ -1290,6 +1095,12 @@ static int rsnd_remove(struct platform_device *pdev) { struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); struct rsnd_dai *rdai; + void (*remove_func[])(struct platform_device *pdev, + struct rsnd_priv *priv) = { + rsnd_ssi_remove, + rsnd_src_remove, + rsnd_dvc_remove, + }; int ret = 0, i; pm_runtime_disable(&pdev->dev); @@ -1299,6 +1110,9 @@ static int rsnd_remove(struct platform_device *pdev) ret |= rsnd_dai_call(remove, &rdai->capture, priv); } + for (i = 0; i < ARRAY_SIZE(remove_func); i++) + remove_func[i](pdev, priv); + snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_platform(&pdev->dev); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c new file mode 100644 index 000000000000..ac3756f6af60 --- /dev/null +++ b/sound/soc/sh/rcar/dma.c @@ -0,0 +1,616 @@ +/* + * Renesas R-Car Audio DMAC support + * + * Copyright (C) 2015 Renesas Electronics Corp. + * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/of_dma.h> +#include "rsnd.h" + +/* + * Audio DMAC peri peri register + */ +#define PDMASAR 0x00 +#define PDMADAR 0x04 +#define PDMACHCR 0x0c + +/* PDMACHCR */ +#define PDMACHCR_DE (1 << 0) + +struct rsnd_dma_ctrl { + void __iomem *base; + int dmapp_num; +}; + +#define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) + +/* + * Audio DMAC + */ +static void rsnd_dmaen_complete(void *data) +{ + struct rsnd_dma *dma = (struct rsnd_dma *)data; + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + + /* + * Renesas sound Gen1 needs 1 DMAC, + * Gen2 needs 2 DMAC. + * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. + * But, Audio-DMAC-peri-peri doesn't have interrupt, + * and this driver is assuming that here. + * + * If Audio-DMAC-peri-peri has interrpt, + * rsnd_dai_pointer_update() will be called twice, + * ant it will breaks io->byte_pos + */ + + rsnd_dai_pointer_update(io, io->byte_per_period); +} + +static void rsnd_dmaen_stop(struct rsnd_dma *dma) +{ + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + dmaengine_terminate_all(dmaen->chan); +} + +static void rsnd_dmaen_start(struct rsnd_dma *dma) +{ + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_substream *substream = io->substream; + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_async_tx_descriptor *desc; + int is_play = rsnd_io_is_play(io); + + desc = dmaengine_prep_dma_cyclic(dmaen->chan, + substream->runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream), + is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + + if (!desc) { + dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); + return; + } + + desc->callback = rsnd_dmaen_complete; + desc->callback_param = dma; + + if (dmaengine_submit(desc) < 0) { + dev_err(dev, "dmaengine_submit() fail\n"); + return; + } + + dma_async_issue_pending(dmaen->chan); +} + +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, + struct rsnd_mod *mod, char *name) +{ + struct dma_chan *chan; + struct device_node *np; + int i = 0; + + for_each_child_of_node(of_node, np) { + if (i == rsnd_mod_id(mod)) + break; + i++; + } + + chan = of_dma_request_slave_channel(np, name); + + of_node_put(np); + of_node_put(of_node); + + return chan; +} + +static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + if ((!mod_from && !mod_to) || + (mod_from && mod_to)) + return NULL; + + if (mod_from) + return rsnd_mod_dma_req(mod_from); + else + return rsnd_mod_dma_req(mod_to); +} + +static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) +{ + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + struct device *dev = rsnd_priv_to_dev(priv); + struct dma_slave_config cfg = {}; + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + int is_play = rsnd_io_is_play(io); + int ret; + + if (dmaen->chan) { + dev_err(dev, "it already has dma channel\n"); + return -EIO; + } + + if (dev->of_node) { + dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); + } else { + dma_cap_mask_t mask; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + dmaen->chan = dma_request_channel(mask, shdma_chan_filter, + (void *)id); + } + if (IS_ERR_OR_NULL(dmaen->chan)) { + dev_err(dev, "can't get dma channel\n"); + goto rsnd_dma_channel_err; + } + + cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; + cfg.src_addr = dma->src_addr; + cfg.dst_addr = dma->dst_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + dev_dbg(dev, "dma : %pad -> %pad\n", + &cfg.src_addr, &cfg.dst_addr); + + ret = dmaengine_slave_config(dmaen->chan, &cfg); + if (ret < 0) + goto rsnd_dma_init_err; + + return 0; + +rsnd_dma_init_err: + rsnd_dma_quit(dma); +rsnd_dma_channel_err: + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + return -EAGAIN; +} + +static void rsnd_dmaen_quit(struct rsnd_dma *dma) +{ + struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); + + if (dmaen->chan) + dma_release_channel(dmaen->chan); + + dmaen->chan = NULL; +} + +static struct rsnd_dma_ops rsnd_dmaen_ops = { + .start = rsnd_dmaen_start, + .stop = rsnd_dmaen_stop, + .init = rsnd_dmaen_init, + .quit = rsnd_dmaen_quit, +}; + +/* + * Audio DMAC peri peri + */ +static const u8 gen2_id_table_ssiu[] = { + 0x00, /* SSI00 */ + 0x04, /* SSI10 */ + 0x08, /* SSI20 */ + 0x0c, /* SSI3 */ + 0x0d, /* SSI4 */ + 0x0e, /* SSI5 */ + 0x0f, /* SSI6 */ + 0x10, /* SSI7 */ + 0x11, /* SSI8 */ + 0x12, /* SSI90 */ +}; +static const u8 gen2_id_table_scu[] = { + 0x2d, /* SCU_SRCI0 */ + 0x2e, /* SCU_SRCI1 */ + 0x2f, /* SCU_SRCI2 */ + 0x30, /* SCU_SRCI3 */ + 0x31, /* SCU_SRCI4 */ + 0x32, /* SCU_SRCI5 */ + 0x33, /* SCU_SRCI6 */ + 0x34, /* SCU_SRCI7 */ + 0x35, /* SCU_SRCI8 */ + 0x36, /* SCU_SRCI9 */ +}; +static const u8 gen2_id_table_cmd[] = { + 0x37, /* SCU_CMD0 */ + 0x38, /* SCU_CMD1 */ +}; + +static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + const u8 *entry = NULL; + int id = rsnd_mod_id(mod); + int size = 0; + + if (mod == ssi) { + entry = gen2_id_table_ssiu; + size = ARRAY_SIZE(gen2_id_table_ssiu); + } else if (mod == src) { + entry = gen2_id_table_scu; + size = ARRAY_SIZE(gen2_id_table_scu); + } else if (mod == dvc) { + entry = gen2_id_table_cmd; + size = ARRAY_SIZE(gen2_id_table_cmd); + } + + if (!entry) + return 0xFF; + + if (size <= id) + return 0xFF; + + return entry[id]; +} + +static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, + struct rsnd_mod *mod_to) +{ + return (rsnd_dmapp_get_id(mod_from) << 24) + + (rsnd_dmapp_get_id(mod_to) << 16); +} + +#define rsnd_dmapp_addr(dmac, dma, reg) \ + (dmac->base + 0x20 + reg + \ + (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) +static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + + dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); + + iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); +} + +static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); +} + +static void rsnd_dmapp_stop(struct rsnd_dma *dma) +{ + int i; + + rsnd_dmapp_write(dma, 0, PDMACHCR); + + for (i = 0; i < 1024; i++) { + if (0 == rsnd_dmapp_read(dma, PDMACHCR)) + return; + udelay(1); + } +} + +static void rsnd_dmapp_start(struct rsnd_dma *dma) +{ + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + + rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); + rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); + rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); +} + +static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) +{ + struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + struct device *dev = rsnd_priv_to_dev(priv); + + dmapp->dmapp_id = dmac->dmapp_num; + dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; + + dmac->dmapp_num++; + + rsnd_dmapp_stop(dma); + + dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", + dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); + + return 0; +} + +static struct rsnd_dma_ops rsnd_dmapp_ops = { + .start = rsnd_dmapp_start, + .stop = rsnd_dmapp_stop, + .init = rsnd_dmapp_init, + .quit = rsnd_dmapp_stop, +}; + +/* + * Common DMAC Interface + */ + +/* + * DMA read/write register offset + * + * RSND_xxx_I_N for Audio DMAC input + * RSND_xxx_O_N for Audio DMAC output + * RSND_xxx_I_P for Audio DMAC peri peri input + * RSND_xxx_O_P for Audio DMAC peri peri output + * + * ex) R-Car H2 case + * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out + * SSI : 0xec541000 / 0xec241008 / 0xec24100c + * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 + * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 + * CMD : 0xec500000 / / 0xec008000 0xec308000 + */ +#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) +#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) + +#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) +#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) + +#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) +#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) + +#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) +#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) + +#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) +#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) + +#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) +#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) + +static dma_addr_t +rsnd_gen2_dma_addr(struct rsnd_priv *priv, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); + phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); + int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); + int use_src = !!rsnd_io_to_mod_src(io); + int use_dvc = !!rsnd_io_to_mod_dvc(io); + int id = rsnd_mod_id(mod); + struct dma_addr { + dma_addr_t out_addr; + dma_addr_t in_addr; + } dma_addrs[3][2][3] = { + /* SRC */ + {{{ 0, 0 }, + /* Capture */ + { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, + { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, + /* Playback */ + {{ 0, 0, }, + { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, + { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } + }, + /* SSI */ + /* Capture */ + {{{ RDMA_SSI_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 } }, + /* Playback */ + {{ 0, RDMA_SSI_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) } } + }, + /* SSIU */ + /* Capture */ + {{{ RDMA_SSIU_O_N(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 }, + { RDMA_SSIU_O_P(ssi, id), 0 } }, + /* Playback */ + {{ 0, RDMA_SSIU_I_N(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) }, + { 0, RDMA_SSIU_I_P(ssi, id) } } }, + }; + + /* it shouldn't happen */ + if (use_dvc && !use_src) + dev_err(dev, "DVC is selected without SRC\n"); + + /* use SSIU or SSI ? */ + if (is_ssi && rsnd_ssi_use_busif(mod)) + is_ssi++; + + return (is_from) ? + dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : + dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; +} + +static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, + struct rsnd_mod *mod, + int is_play, int is_from) +{ + /* + * gen1 uses default DMA addr + */ + if (rsnd_is_gen1(priv)) + return 0; + + if (!mod) + return 0; + + return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); +} + +#define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ +static void rsnd_dma_of_path(struct rsnd_dma *dma, + int is_play, + struct rsnd_mod **mod_from, + struct rsnd_mod **mod_to) +{ + struct rsnd_mod *this = rsnd_dma_to_mod(dma); + struct rsnd_dai_stream *io = rsnd_mod_to_io(this); + struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *src = rsnd_io_to_mod_src(io); + struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); + struct rsnd_mod *mod[MOD_MAX]; + int i, index; + + + for (i = 0; i < MOD_MAX; i++) + mod[i] = NULL; + + /* + * in play case... + * + * src -> dst + * + * mem -> SSI + * mem -> SRC -> SSI + * mem -> SRC -> DVC -> SSI + */ + mod[0] = NULL; /* for "mem" */ + index = 1; + for (i = 1; i < MOD_MAX; i++) { + if (!src) { + mod[i] = ssi; + } else if (!dvc) { + mod[i] = src; + src = NULL; + } else { + if ((!is_play) && (this == src)) + this = dvc; + + mod[i] = (is_play) ? src : dvc; + i++; + mod[i] = (is_play) ? dvc : src; + src = NULL; + dvc = NULL; + } + + if (mod[i] == this) + index = i; + + if (mod[i] == ssi) + break; + } + + if (is_play) { + *mod_from = mod[index - 1]; + *mod_to = mod[index]; + } else { + *mod_from = mod[index]; + *mod_to = mod[index - 1]; + } +} + +void rsnd_dma_stop(struct rsnd_dma *dma) +{ + dma->ops->stop(dma); +} + +void rsnd_dma_start(struct rsnd_dma *dma) +{ + dma->ops->start(dma); +} + +void rsnd_dma_quit(struct rsnd_dma *dma) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + + if (!dmac) + return; + + dma->ops->quit(dma); +} + +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) +{ + struct rsnd_mod *mod = rsnd_dma_to_mod(dma); + struct rsnd_mod *mod_from; + struct rsnd_mod *mod_to; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); + int is_play = rsnd_io_is_play(io); + + /* + * DMA failed. try to PIO mode + * see + * rsnd_ssi_fallback() + * rsnd_rdai_continuance_probe() + */ + if (!dmac) + return -EAGAIN; + + rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); + + dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); + dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); + + /* for Gen2 */ + if (mod_from && mod_to) + dma->ops = &rsnd_dmapp_ops; + else + dma->ops = &rsnd_dmaen_ops; + + /* for Gen1, overwrite */ + if (rsnd_is_gen1(priv)) + dma->ops = &rsnd_dmaen_ops; + + return dma->ops->init(priv, dma, id, mod_from, mod_to); +} + +int rsnd_dma_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dma_ctrl *dmac; + struct resource *res; + + /* + * for Gen1 + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * for Gen2 + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); + dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac || !res) { + dev_err(dev, "dma allocate failed\n"); + return 0; /* it will be PIO mode */ + } + + dmac->dmapp_num = 0; + dmac->base = devm_ioremap_resource(dev, res); + if (IS_ERR(dmac->base)) + return PTR_ERR(dmac->base); + + priv->dma = dmac; + + return 0; +} diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d7f9ed959c4e..e5fcb062ad77 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -24,6 +24,9 @@ struct rsnd_dvc { struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ }; +#define rsnd_dvc_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") + #define rsnd_mod_to_dvc(_mod) \ container_of((_mod), struct rsnd_dvc, mod) @@ -33,7 +36,7 @@ struct rsnd_dvc { ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ i++) -static const char const *dvc_ramp_rate[] = { +static const char * const dvc_ramp_rate[] = { "128 dB/1 step", /* 00000 */ "64 dB/1 step", /* 00001 */ "32 dB/1 step", /* 00010 */ @@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) rsnd_mod_write(mod, DVC_DVUER, 1); } -static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { @@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, return 0; } +static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + + return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), + mod, "tx"); +} + static struct rsnd_mod_ops rsnd_dvc_ops = { .name = DVC_NAME, - .probe = rsnd_dvc_probe_gen2, + .dma_req = rsnd_dvc_dma_req, .remove = rsnd_dvc_remove_gen2, .init = rsnd_dvc_init, .quit = rsnd_dvc_quit, @@ -333,7 +333,7 @@ int rsnd_dvc_probe(struct platform_device *pdev, struct rsnd_dvc *dvc; struct clk *clk; char name[RSND_DVC_NAME_SIZE]; - int i, nr; + int i, nr, ret; rsnd_of_parse_dvc(pdev, of_data, priv); @@ -366,11 +366,22 @@ int rsnd_dvc_probe(struct platform_device *pdev, dvc->info = &info->dvc_info[i]; - rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, + ret = rsnd_mod_init(&dvc->mod, &rsnd_dvc_ops, clk, RSND_MOD_DVC, i); - - dev_dbg(dev, "CMD%d probed\n", i); + if (ret) + return ret; } return 0; } + +void rsnd_dvc_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_dvc *dvc; + int i; + + for_each_rsnd_dvc(dvc, priv, i) { + rsnd_mod_quit(&dvc->mod); + } +} diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index de0685f2abae..8c7dc51b1c4f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -28,6 +28,7 @@ struct rsnd_gen { struct regmap *regmap[RSND_BASE_MAX]; struct regmap_field *regs[RSND_REG_MAX]; + phys_addr_t res[RSND_REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, mask, data); } -#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \ - _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf)) +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) +{ + struct rsnd_gen *gen = rsnd_priv_to_gen(priv); + + return gen->res[reg_id]; +} + +#define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ + _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, int id_size, int reg_id, + const char *name, struct rsnd_regmap_field_conf *conf, int conf_size) { @@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, regc.reg_bits = 32; regc.val_bits = 32; regc.reg_stride = 4; + regc.name = name; - res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); if (!res) return -ENODEV; @@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, gen->base[reg_id] = base; gen->regmap[reg_id] = regmap; + gen->res[reg_id] = res->start; for (i = 0; i < conf_size; i++) { @@ -176,125 +189,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, } /* - * DMA read/write register offset - * - * RSND_xxx_I_N for Audio DMAC input - * RSND_xxx_O_N for Audio DMAC output - * RSND_xxx_I_P for Audio DMAC peri peri input - * RSND_xxx_O_P for Audio DMAC peri peri output - * - * ex) R-Car H2 case - * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out - * SSI : 0xec541000 / 0xec241008 / 0xec24100c - * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 - * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 - * CMD : 0xec500000 / / 0xec008000 0xec308000 - */ -#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) -#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) - -#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) -#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) - -#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) -#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) - -#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) -#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) - -#define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) -#define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) - -#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) -#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) - -static dma_addr_t -rsnd_gen2_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - struct platform_device *pdev = rsnd_priv_to_pdev(priv); - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); - dma_addr_t ssi_reg = platform_get_resource(pdev, - IORESOURCE_MEM, RSND_GEN2_SSI)->start; - dma_addr_t src_reg = platform_get_resource(pdev, - IORESOURCE_MEM, RSND_GEN2_SCU)->start; - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); - int use_src = !!rsnd_io_to_mod_src(io); - int use_dvc = !!rsnd_io_to_mod_dvc(io); - int id = rsnd_mod_id(mod); - struct dma_addr { - dma_addr_t out_addr; - dma_addr_t in_addr; - } dma_addrs[3][2][3] = { - /* SRC */ - {{{ 0, 0 }, - /* Capture */ - { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, - { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, - /* Playback */ - {{ 0, 0, }, - { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, - { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } - }, - /* SSI */ - /* Capture */ - {{{ RDMA_SSI_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 } }, - /* Playback */ - {{ 0, RDMA_SSI_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) } } - }, - /* SSIU */ - /* Capture */ - {{{ RDMA_SSIU_O_N(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 }, - { RDMA_SSIU_O_P(ssi, id), 0 } }, - /* Playback */ - {{ 0, RDMA_SSIU_I_N(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) }, - { 0, RDMA_SSIU_I_P(ssi, id) } } }, - }; - - /* it shouldn't happen */ - if (use_dvc && !use_src) - dev_err(dev, "DVC is selected without SRC\n"); - - /* use SSIU or SSI ? */ - if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu"))) - is_ssi++; - - return (is_from) ? - dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : - dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; -} - -dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from) -{ - /* - * gen1 uses default DMA addr - */ - if (rsnd_is_gen1(priv)) - return 0; - - if (!mod) - return 0; - - return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); -} - -/* * Gen2 */ static int rsnd_gen2_probe(struct platform_device *pdev, struct rsnd_priv *priv) { - struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_regmap_field_conf conf_ssiu[] = { RSND_GEN_S_REG(SSI_MODE0, 0x800), RSND_GEN_S_REG(SSI_MODE1, 0x804), @@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev, int ret_adg; int ret_ssi; - ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu); - ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, conf_scu); - ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, conf_adg); - ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, conf_ssi); + ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu); + ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, "scu", conf_scu); + ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, "adg", conf_adg); + ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, "ssi", conf_ssi); if (ret_ssiu < 0 || ret_scu < 0 || ret_adg < 0 || ret_ssi < 0) return ret_ssiu | ret_scu | ret_adg | ret_ssi; - dev_dbg(dev, "Gen2 is probed\n"); - return 0; } @@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev, static int rsnd_gen1_probe(struct platform_device *pdev, struct rsnd_priv *priv) { - struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_regmap_field_conf conf_sru[] = { RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), @@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev, int ret_adg; int ret_ssi; - ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, conf_sru); - ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, conf_adg); - ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, conf_ssi); + ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru); + ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); + ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); if (ret_sru < 0 || ret_adg < 0 || ret_ssi < 0) return ret_sru | ret_adg | ret_ssi; - dev_dbg(dev, "Gen1 is probed\n"); - return 0; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index e7914bd610e2..4e6de6804cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod); /* * R-Car DMA */ -struct rsnd_dma { - struct sh_dmae_slave slave; +struct rsnd_dma; +struct rsnd_dma_ops { + void (*start)(struct rsnd_dma *dma); + void (*stop)(struct rsnd_dma *dma); + int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, + struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); + void (*quit)(struct rsnd_dma *dma); +}; + +struct rsnd_dmaen { struct dma_chan *chan; - enum dma_transfer_direction dir; - dma_addr_t addr; }; +struct rsnd_dmapp { + int dmapp_id; + u32 chcr; +}; + +struct rsnd_dma { + struct rsnd_dma_ops *ops; + dma_addr_t src_addr; + dma_addr_t dst_addr; + union { + struct rsnd_dmaen en; + struct rsnd_dmapp pp; + } dma; +}; +#define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) +#define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) + void rsnd_dma_start(struct rsnd_dma *dma); void rsnd_dma_stop(struct rsnd_dma *dma); -int rsnd_dma_available(struct rsnd_dma *dma); -int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, - int is_play, int id); -void rsnd_dma_quit(struct rsnd_priv *priv, - struct rsnd_dma *dma); +int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); +void rsnd_dma_quit(struct rsnd_dma *dma); +int rsnd_dma_probe(struct platform_device *pdev, + const struct rsnd_of_data *of_data, + struct rsnd_priv *priv); +struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, + struct rsnd_mod *mod, char *name); +#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) /* * R-Car sound mod @@ -198,7 +224,7 @@ enum rsnd_mod_type { struct rsnd_mod_ops { char *name; - char* (*dma_name)(struct rsnd_mod *mod); + struct dma_chan* (*dma_req)(struct rsnd_mod *mod); int (*probe)(struct rsnd_mod *mod, struct rsnd_priv *priv); int (*remove)(struct rsnd_mod *mod, @@ -213,6 +239,9 @@ struct rsnd_mod_ops { struct rsnd_priv *priv); int (*pcm_new)(struct rsnd_mod *mod, struct snd_soc_pcm_runtime *rtd); + int (*hw_params)(struct rsnd_mod *mod, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); int (*fallback)(struct rsnd_mod *mod, struct rsnd_priv *priv); }; @@ -236,6 +265,9 @@ struct rsnd_mod { * 2 0: start 1: stop * 3 0: pcm_new * 4 0: fallback + * + * 31 bit is always called (see __rsnd_mod_call) + * 31 0: hw_params */ #define __rsnd_mod_shift_probe 0 #define __rsnd_mod_shift_remove 0 @@ -245,6 +277,7 @@ struct rsnd_mod { #define __rsnd_mod_shift_stop 2 #define __rsnd_mod_shift_pcm_new 3 #define __rsnd_mod_shift_fallback 4 +#define __rsnd_mod_shift_hw_params 31 /* always called */ #define __rsnd_mod_call_probe 0 #define __rsnd_mod_call_remove 1 @@ -254,28 +287,30 @@ struct rsnd_mod { #define __rsnd_mod_call_stop 1 #define __rsnd_mod_call_pcm_new 0 #define __rsnd_mod_call_fallback 0 +#define __rsnd_mod_call_hw_params 0 #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) #define rsnd_mod_to_dma(mod) (&(mod)->dma) -#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) #define rsnd_mod_to_io(mod) ((mod)->io) #define rsnd_mod_id(mod) ((mod)->id) -#define rsnd_mod_hw_start(mod) clk_prepare_enable((mod)->clk) -#define rsnd_mod_hw_stop(mod) clk_disable_unprepare((mod)->clk) +#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) +#define rsnd_mod_hw_stop(mod) clk_disable((mod)->clk) -void rsnd_mod_init(struct rsnd_mod *mod, +int rsnd_mod_init(struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, enum rsnd_mod_type type, int id); +void rsnd_mod_quit(struct rsnd_mod *mod); char *rsnd_mod_name(struct rsnd_mod *mod); -char *rsnd_mod_dma_name(struct rsnd_mod *mod); +struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); /* * R-Car sound DAI */ #define RSND_DAI_NAME_SIZE 16 struct rsnd_dai_stream { + char name[RSND_DAI_NAME_SIZE]; struct snd_pcm_substream *substream; struct rsnd_mod *mod[RSND_MOD_MAX]; struct rsnd_dai_path_info *info; /* rcar_snd.h */ @@ -331,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev, void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); -dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, - struct rsnd_mod *mod, - int is_play, int is_from); +phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) @@ -389,6 +422,11 @@ struct rsnd_priv { void *adg; /* + * below value will be filled on rsnd_dma_probe() + */ + void *dma; + + /* * below value will be filled on rsnd_ssi_probe() */ void *ssi; @@ -414,19 +452,6 @@ struct rsnd_priv { #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) -#define rsnd_info_is_playback(priv, type) \ -({ \ - struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ - int i, is_play = 0; \ - for (i = 0; i < info->dai_info_nr; i++) { \ - if (info->dai_info[i].playback.type == (type)->info) { \ - is_play = 1; \ - break; \ - } \ - } \ - is_play; \ -}) - /* * rsnd_kctrl */ @@ -480,6 +505,8 @@ int rsnd_kctrl_new_e(struct rsnd_mod *mod, int rsnd_src_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); +void rsnd_src_remove(struct platform_device *pdev, + struct rsnd_priv *priv); struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id); unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, @@ -498,9 +525,12 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod); int rsnd_ssi_probe(struct platform_device *pdev, const struct rsnd_of_data *of_data, struct rsnd_priv *priv); +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); +int rsnd_ssi_use_busif(struct rsnd_mod *mod); /* * R-Car DVC diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c new file mode 100644 index 000000000000..a68517afe615 --- /dev/null +++ b/sound/soc/sh/rcar/rsrc-card.c @@ -0,0 +1,512 @@ +/* + * Renesas Sampling Rate Convert Sound Card for DPCM + * + * Copyright (C) 2015 Renesas Solutions Corp. + * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on ${LINUX}/sound/soc/generic/simple-card.c + * + * 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/device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/string.h> +#include <sound/jack.h> +#include <sound/soc.h> +#include <sound/soc-dai.h> + +struct rsrc_card_of_data { + const char *prefix; + const struct snd_soc_dapm_route *routes; + int num_routes; +}; + +static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = { + {"ak4642 Playback", NULL, "DAI0 Playback"}, + {"DAI0 Capture", NULL, "ak4642 Capture"}, +}; + +static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { + .prefix = "ak4642", + .routes = routes_ssi0_ak4642, + .num_routes = ARRAY_SIZE(routes_ssi0_ak4642), +}; + +static const struct of_device_id rsrc_card_of_match[] = { + { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, + { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, + {}, +}; +MODULE_DEVICE_TABLE(of, rsrc_card_of_match); + +struct rsrc_card_dai { + const char *name; + unsigned int fmt; + unsigned int sysclk; + struct clk *clk; +}; + +#define RSRC_FB_NUM 2 /* FE/BE */ +#define IDX_CPU 0 +#define IDX_CODEC 1 +struct rsrc_card_priv { + struct snd_soc_card snd_card; + struct rsrc_card_dai_props { + struct rsrc_card_dai cpu_dai; + struct rsrc_card_dai codec_dai; + } dai_props[RSRC_FB_NUM]; + struct snd_soc_codec_conf codec_conf; + struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; + u32 convert_rate; +}; + +#define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) +#define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) +#define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i) +#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) + +static int rsrc_card_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct rsrc_card_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + int ret; + + ret = clk_prepare_enable(dai_props->cpu_dai.clk); + if (ret) + return ret; + + ret = clk_prepare_enable(dai_props->codec_dai.clk); + if (ret) + clk_disable_unprepare(dai_props->cpu_dai.clk); + + return ret; +} + +static void rsrc_card_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct rsrc_card_dai_props *dai_props = + &priv->dai_props[rtd - rtd->card->rtd]; + + clk_disable_unprepare(dai_props->cpu_dai.clk); + + clk_disable_unprepare(dai_props->codec_dai.clk); +} + +static struct snd_soc_ops rsrc_card_ops = { + .startup = rsrc_card_startup, + .shutdown = rsrc_card_shutdown, +}; + +static int __rsrc_card_dai_init(struct snd_soc_dai *dai, + struct rsrc_card_dai *set) +{ + int ret; + + if (set->fmt) { + ret = snd_soc_dai_set_fmt(dai, set->fmt); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "set_fmt error\n"); + goto err; + } + } + + if (set->sysclk) { + ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + if (ret && ret != -ENOTSUPP) { + dev_err(dai->dev, "set_sysclk error\n"); + goto err; + } + } + + ret = 0; + +err: + return ret; +} + +static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *codec = rtd->codec_dai; + struct snd_soc_dai *cpu = rtd->cpu_dai; + struct rsrc_card_dai_props *dai_props; + int num, ret; + + num = rtd - rtd->card->rtd; + dai_props = &priv->dai_props[num]; + ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); + if (ret < 0) + return ret; + + ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; +} + +static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + + if (!priv->convert_rate) + return 0; + + rate->min = rate->max = priv->convert_rate; + + return 0; +} + +static int +rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, + struct device_node *np, + struct rsrc_card_dai *dai, + struct snd_soc_dai_link *dai_link, + int *args_count) +{ + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + struct of_phandle_args args; + struct device_node **p_node; + struct clk *clk; + const char **dai_name; + const char **name; + u32 val; + int ret; + + if (args_count) { + p_node = &dai_link->cpu_of_node; + dai_name = &dai_link->cpu_dai_name; + name = &dai_link->cpu_name; + } else { + p_node = &dai_link->codec_of_node; + dai_name = &dai_link->codec_dai_name; + name = &dai_link->codec_name; + } + + if (!np) { + /* use snd-soc-dummy */ + *p_node = NULL; + *dai_name = "snd-soc-dummy-dai"; + *name = "snd-soc-dummy"; + return 0; + } + + /* + * Get node via "sound-dai = <&phandle port>" + * it will be used as xxx_of_node on soc_bind_dai_link() + */ + ret = of_parse_phandle_with_args(np, "sound-dai", + "#sound-dai-cells", 0, &args); + if (ret) + return ret; + + *p_node = args.np; + + /* Get dai->name */ + ret = snd_soc_of_get_dai_name(np, dai_name); + if (ret < 0) + return ret; + + /* + * FIXME + * + * rsrc assumes DPCM playback/capture + */ + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + + if (args_count) { + *args_count = args.args_count; + dai_link->dynamic = 1; + } else { + dai_link->no_pcm = 1; + priv->codec_conf.of_node = (*p_node); + priv->codec_conf.name_prefix = of_data->prefix; + } + + /* + * Parse dai->sysclk come from "clocks = <&xxx>" + * (if system has common clock) + * or "system-clock-frequency = <xxx>" + * or device's module clock. + */ + if (of_property_read_bool(np, "clocks")) { + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + return ret; + } + + dai->sysclk = clk_get_rate(clk); + dai->clk = clk; + } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { + dai->sysclk = val; + } else { + clk = of_clk_get(args.np, 0); + if (!IS_ERR(clk)) + dai->sysclk = clk_get_rate(clk); + } + + return 0; +} + +static int rsrc_card_parse_daifmt(struct device_node *node, + struct rsrc_card_priv *priv, + struct device_node *codec, + int idx) +{ + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); + struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; + struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; + unsigned int daifmt; + + daifmt = snd_soc_of_parse_daifmt(node, NULL, + &bitclkmaster, &framemaster); + daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + + if (!bitclkmaster && !framemaster) + return -EINVAL; + + if (codec == bitclkmaster) + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; + else + daifmt |= (codec == framemaster) ? + SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; + + cpu_dai->fmt = daifmt; + codec_dai->fmt = daifmt; + + of_node_put(bitclkmaster); + of_node_put(framemaster); + + return 0; +} + +static int rsrc_card_dai_link_of(struct device_node *node, + struct rsrc_card_priv *priv, + int idx) +{ + struct device *dev = rsrc_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); + struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); + struct device_node *cpu = NULL; + struct device_node *codec = NULL; + char *name; + char prop[128]; + int ret, cpu_args; + + cpu = of_get_child_by_name(node, "cpu"); + codec = of_get_child_by_name(node, "codec"); + + if (!cpu || !codec) { + ret = -EINVAL; + dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); + goto dai_link_of_err; + } + + ret = rsrc_card_parse_daifmt(node, priv, codec, idx); + if (ret < 0) + goto dai_link_of_err; + + ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, + &dai_props->cpu_dai, + dai_link, + &cpu_args); + if (ret < 0) + goto dai_link_of_err; + + ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, + &dai_props->codec_dai, + dai_link, + NULL); + if (ret < 0) + goto dai_link_of_err; + + if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { + ret = -EINVAL; + goto dai_link_of_err; + } + + /* Simple Card assumes platform == cpu */ + dai_link->platform_of_node = dai_link->cpu_of_node; + + /* DAI link name is created from CPU/CODEC dai name */ + name = devm_kzalloc(dev, + strlen(dai_link->cpu_dai_name) + + strlen(dai_link->codec_dai_name) + 2, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto dai_link_of_err; + } + + sprintf(name, "%s-%s", dai_link->cpu_dai_name, + dai_link->codec_dai_name); + dai_link->name = dai_link->stream_name = name; + dai_link->ops = &rsrc_card_ops; + dai_link->init = rsrc_card_dai_init; + + if (idx == IDX_CODEC) + dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; + + dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); + dev_dbg(dev, "\tcpu : %s / %04x / %d\n", + dai_link->cpu_dai_name, + dai_props->cpu_dai.fmt, + dai_props->cpu_dai.sysclk); + dev_dbg(dev, "\tcodec : %s / %04x / %d\n", + dai_link->codec_dai_name, + dai_props->codec_dai.fmt, + dai_props->codec_dai.sysclk); + + /* + * In soc_bind_dai_link() will check cpu name after + * of_node matching if dai_link has cpu_dai_name. + * but, it will never match if name was created by + * fmt_single_name() remove cpu_dai_name if cpu_args + * was 0. See: + * fmt_single_name() + * fmt_multiple_name() + */ + if (!cpu_args) + dai_link->cpu_dai_name = NULL; + +dai_link_of_err: + of_node_put(cpu); + of_node_put(codec); + + return ret; +} + +static int rsrc_card_parse_of(struct device_node *node, + struct rsrc_card_priv *priv) +{ + struct device *dev = rsrc_priv_to_dev(priv); + const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); + int ret; + int i; + + if (!node) + return -EINVAL; + + /* Parse the card name from DT */ + snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); + + /* DAPM routes */ + priv->snd_card.of_dapm_routes = of_data->routes; + priv->snd_card.num_of_dapm_routes = of_data->num_routes; + + /* sampling rate convert */ + of_property_read_u32(node, "convert-rate", &priv->convert_rate); + + dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", + priv->snd_card.name ? priv->snd_card.name : "", + priv->convert_rate); + + /* FE/BE */ + for (i = 0; i < RSRC_FB_NUM; i++) { + ret = rsrc_card_dai_link_of(node, priv, i); + if (ret < 0) + return ret; + } + + if (!priv->snd_card.name) + priv->snd_card.name = priv->snd_card.dai_link->name; + + return 0; +} + +/* Decrease the reference count of the device nodes */ +static int rsrc_card_unref(struct snd_soc_card *card) +{ + struct snd_soc_dai_link *dai_link; + int num_links; + + for (num_links = 0, dai_link = card->dai_link; + num_links < card->num_links; + num_links++, dai_link++) { + of_node_put(dai_link->cpu_of_node); + of_node_put(dai_link->codec_of_node); + } + return 0; +} + +static int rsrc_card_probe(struct platform_device *pdev) +{ + struct rsrc_card_priv *priv; + struct snd_soc_dai_link *dai_link; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + int ret; + + /* Allocate the private data */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + /* Init snd_soc_card */ + priv->snd_card.owner = THIS_MODULE; + priv->snd_card.dev = dev; + dai_link = priv->dai_link; + priv->snd_card.dai_link = dai_link; + priv->snd_card.num_links = RSRC_FB_NUM; + priv->snd_card.codec_conf = &priv->codec_conf; + priv->snd_card.num_configs = 1; + + ret = rsrc_card_parse_of(np, priv); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(dev, "parse error %d\n", ret); + goto err; + } + + snd_soc_card_set_drvdata(&priv->snd_card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); + if (ret >= 0) + return ret; +err: + rsrc_card_unref(&priv->snd_card); + + return ret; +} + +static int rsrc_card_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + return rsrc_card_unref(card); +} + +static struct platform_driver rsrc_card = { + .driver = { + .name = "renesas-src-audio-card", + .of_match_table = rsrc_card_of_match, + }, + .probe = rsrc_card_probe, + .remove = rsrc_card_remove, +}; + +module_platform_driver(rsrc_card); + +MODULE_ALIAS("platform:renesas-src-audio-card"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card"); +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 81c182b4bad5..3beb32eb412a 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -22,16 +22,20 @@ struct rsnd_src { struct rsnd_src_platform_info *info; /* rcar_snd.h */ struct rsnd_mod mod; + struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ + struct rsnd_kctrl_cfg_s sync; /* sync convert */ + u32 convert_rate; /* sampling rate convert */ int err; }; #define RSND_SRC_NAME_SIZE 16 -#define rsnd_src_convert_rate(p) ((p)->info->convert_rate) +#define rsnd_enable_sync_convert(src) ((src)->sen.val) +#define rsnd_src_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") + #define rsnd_mod_to_src(_mod) \ container_of((_mod), struct rsnd_src, mod) -#define rsnd_src_dma_available(src) \ - rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) #define for_each_rsnd_src(pos, priv, i) \ for ((i) = 0; \ @@ -113,6 +117,17 @@ struct rsnd_src { /* * Gen1/Gen2 common functions */ +static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + int is_play = rsnd_io_is_play(io); + + return rsnd_dma_request_channel(rsnd_src_of_node(priv), + mod, + is_play ? "rx" : "tx"); +} + int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, int use_busif) { @@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) return 0; } +static u32 rsnd_src_convert_rate(struct rsnd_src *src) +{ + struct rsnd_mod *mod = &src->mod; + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + u32 convert_rate; + + if (!runtime) + return 0; + + if (!rsnd_enable_sync_convert(src)) + return src->convert_rate; + + convert_rate = src->sync.val; + + if (!convert_rate) + convert_rate = src->convert_rate; + + if (!convert_rate) + convert_rate = runtime->rate; + + return convert_rate; +} + unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct snd_pcm_runtime *runtime) @@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) return 0; } -static int rsnd_src_init(struct rsnd_mod *mod) +static int rsnd_src_hw_params(struct rsnd_mod *mod, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *fe_params) +{ + struct rsnd_src *src = rsnd_mod_to_src(mod); + struct snd_soc_pcm_runtime *fe = substream->private_data; + + /* default value (mainly for non-DT) */ + src->convert_rate = src->info->convert_rate; + + /* + * SRC assumes that it is used under DPCM if user want to use + * sampling rate convert. Then, SRC should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + if (fe->dai_link->dynamic) { + int stream = substream->stream; + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + + list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { + be_params = &dpcm->hw_params; + + if (params_rate(fe_params) != params_rate(be_params)) + src->convert_rate = params_rate(be_params); + } + } + + return 0; +} + +static int rsnd_src_init(struct rsnd_mod *mod, + struct rsnd_priv *priv) { struct rsnd_src *src = rsnd_mod_to_src(mod); @@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod) src->err = 0; + /* reset sync convert_rate */ + src->sync.val = 0; + /* * Initialize the operation of the SRC internal circuits * see rsnd_src_start() @@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, dev_warn(dev, "%s[%d] under/over flow err = %d\n", rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); + src->convert_rate = 0; + + /* reset sync convert_rate */ + src->sync.val = 0; + return 0; } @@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) return 0; } -static int rsnd_src_probe_gen1(struct rsnd_mod *mod, - struct rsnd_priv *priv) -{ - struct device *dev = rsnd_priv_to_dev(priv); - - dev_dbg(dev, "%s[%d] (Gen1) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return 0; -} - static int rsnd_src_init_gen1(struct rsnd_mod *mod, struct rsnd_priv *priv) { int ret; - ret = rsnd_src_init(mod); + ret = rsnd_src_init(mod, priv); if (ret < 0) return ret; @@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod, static struct rsnd_mod_ops rsnd_src_gen1_ops = { .name = SRC_NAME, - .probe = rsnd_src_probe_gen1, + .dma_req = rsnd_src_dma_req, .init = rsnd_src_init_gen1, .quit = rsnd_src_quit, .start = rsnd_src_start_gen1, .stop = rsnd_src_stop_gen1, + .hw_params = rsnd_src_hw_params, }; /* @@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) if (rsnd_src_error_record_gen2(mod)) { struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_src *src = rsnd_mod_to_src(mod); struct device *dev = rsnd_priv_to_dev(priv); - _rsnd_src_stop_gen2(mod); - _rsnd_src_start_gen2(mod); - dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + _rsnd_src_stop_gen2(mod); + if (src->err < 1024) + _rsnd_src_start_gen2(mod); + else + dev_warn(dev, "no more SRC restart\n"); } return IRQ_HANDLED; @@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_src *src = rsnd_mod_to_src(mod); u32 convert_rate = rsnd_src_convert_rate(src); + u32 cr, route; uint ratio; int ret; @@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) if (ret < 0) return ret; - rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); - + cr = 0x00011110; + route = 0x0; if (convert_rate) { - /* Gen1/Gen2 are not compatible */ - rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); + route = 0x1; + + if (rsnd_enable_sync_convert(src)) { + cr |= 0x1; + route |= rsnd_io_is_play(io) ? + (0x1 << 24) : (0x1 << 25); + } } + rsnd_mod_write(mod, SRC_SRCCR, cr); + rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); + switch (rsnd_mod_id(mod)) { case 5: case 6: @@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, IRQF_SHARED, dev_name(dev), mod); if (ret) - goto rsnd_src_probe_gen2_fail; + return ret; } ret = rsnd_dma_init(priv, rsnd_mod_to_dma(mod), - rsnd_info_is_playback(priv, src), src->info->dma_id); - if (ret) - goto rsnd_src_probe_gen2_fail; - - dev_dbg(dev, "%s[%d] (Gen2) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return ret; - -rsnd_src_probe_gen2_fail: - dev_err(dev, "%s[%d] (Gen2) failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail: static int rsnd_src_remove_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { - rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); + rsnd_dma_quit(rsnd_mod_to_dma(mod)); return 0; } @@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, { int ret; - ret = rsnd_src_init(mod); + ret = rsnd_src_init(mod, priv); if (ret < 0) return ret; @@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, return ret; } +static void rsnd_src_reconvert_update(struct rsnd_mod *mod) +{ + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + u32 convert_rate = rsnd_src_convert_rate(src); + u32 fsrate; + + if (!runtime) + return; + + if (!convert_rate) + convert_rate = runtime->rate; + + fsrate = 0x0400000 / convert_rate * runtime->rate; + + /* update IFS */ + rsnd_mod_write(mod, SRC_IFSVR, fsrate); +} + +static int rsnd_src_pcm_new(struct rsnd_mod *mod, + struct snd_soc_pcm_runtime *rtd) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_src *src = rsnd_mod_to_src(mod); + int ret; + + /* + * enable SRC sync convert if possible + */ + + /* + * Gen1 is not supported + */ + if (rsnd_is_gen1(priv)) + return 0; + + /* + * SRC sync convert needs clock master + */ + if (!rsnd_rdai_is_clk_master(rdai)) + return 0; + + /* + * We can't use SRC sync convert + * if it has DVC + */ + if (rsnd_io_to_mod_dvc(io)) + return 0; + + /* + * enable sync convert + */ + ret = rsnd_kctrl_new_s(mod, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate Switch" : + "SRC In Rate Switch", + rsnd_src_reconvert_update, + &src->sen, 1); + if (ret < 0) + return ret; + + ret = rsnd_kctrl_new_s(mod, rtd, + rsnd_io_is_play(io) ? + "SRC Out Rate" : + "SRC In Rate", + rsnd_src_reconvert_update, + &src->sync, 192000); + + return ret; +} + static struct rsnd_mod_ops rsnd_src_gen2_ops = { .name = SRC_NAME, + .dma_req = rsnd_src_dma_req, .probe = rsnd_src_probe_gen2, .remove = rsnd_src_remove_gen2, .init = rsnd_src_init_gen2, .quit = rsnd_src_quit, .start = rsnd_src_start_gen2, .stop = rsnd_src_stop_gen2, + .hw_params = rsnd_src_hw_params, + .pcm_new = rsnd_src_pcm_new, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev, if (!of_data) return; - src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); + src_node = rsnd_src_of_node(priv); if (!src_node) return; @@ -850,7 +1001,7 @@ int rsnd_src_probe(struct platform_device *pdev, struct rsnd_mod_ops *ops; struct clk *clk; char name[RSND_SRC_NAME_SIZE]; - int i, nr; + int i, nr, ret; ops = NULL; if (rsnd_is_gen1(priv)) @@ -890,10 +1041,21 @@ int rsnd_src_probe(struct platform_device *pdev, src->info = &info->src_info[i]; - rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); - - dev_dbg(dev, "SRC%d probed\n", i); + ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); + if (ret) + return ret; } return 0; } + +void rsnd_src_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_src *src; + int i; + + for_each_rsnd_src(src, priv, i) { + rsnd_mod_quit(&src->mod); + } +} diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 9e7b627c08e2..7bb9c087f3dc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -80,13 +80,13 @@ struct rsnd_ssi { #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) -#define rsnd_ssi_dma_available(ssi) \ - rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) #define rsnd_ssi_mode_flags(p) ((p)->info->flags) #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) +#define rsnd_ssi_of_node(priv) \ + of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") -static int rsnd_ssi_use_busif(struct rsnd_mod *mod) +int rsnd_ssi_use_busif(struct rsnd_mod *mod) { struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); @@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) /* * restart SSI */ - rsnd_ssi_stop(mod, priv); - rsnd_ssi_start(mod, priv); - dev_dbg(dev, "%s[%d] restart\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); + + rsnd_ssi_stop(mod, priv); + if (ssi->err < 1024) + rsnd_ssi_start(mod, priv); + else + dev_warn(dev, "no more SSI restart\n"); } rsnd_ssi_record_error(ssi, status); @@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, rsnd_ssi_interrupt, IRQF_SHARED, dev_name(dev), ssi); - if (ret) - dev_err(dev, "%s[%d] (PIO) request interrupt failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - else - dev_dbg(dev, "%s[%d] (PIO) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, IRQF_SHARED, dev_name(dev), ssi); if (ret) - goto rsnd_ssi_dma_probe_fail; + return ret; ret = rsnd_dma_init( priv, rsnd_mod_to_dma(mod), - rsnd_info_is_playback(priv, ssi), dma_id); - if (ret) - goto rsnd_ssi_dma_probe_fail; - - dev_dbg(dev, "%s[%d] (DMA) is probed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); - - return ret; - -rsnd_ssi_dma_probe_fail: - dev_err(dev, "%s[%d] (DMA) is failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); return ret; } @@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, struct device *dev = rsnd_priv_to_dev(priv); int irq = ssi->info->irq; - rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); + rsnd_dma_quit(rsnd_mod_to_dma(mod)); /* PIO will request IRQ again */ devm_free_irq(dev, irq, ssi); @@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, return 0; } -static char *rsnd_ssi_dma_name(struct rsnd_mod *mod) +static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) { - return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME; + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); + int is_play = rsnd_io_is_play(io); + char *name; + + if (rsnd_ssi_use_busif(mod)) + name = is_play ? "rxu" : "txu"; + else + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), + mod, name); } static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .name = SSI_NAME, - .dma_name = rsnd_ssi_dma_name, + .dma_req = rsnd_ssi_dma_req, .probe = rsnd_ssi_dma_probe, .remove = rsnd_ssi_dma_remove, .init = rsnd_ssi_init, @@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, if (!of_data) return; - node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); + node = rsnd_ssi_of_node(priv); if (!node) return; @@ -697,7 +693,7 @@ int rsnd_ssi_probe(struct platform_device *pdev, struct clk *clk; struct rsnd_ssi *ssi; char name[RSND_SSI_NAME_SIZE]; - int i, nr; + int i, nr, ret; rsnd_of_parse_ssi(pdev, of_data, priv); @@ -732,10 +728,23 @@ int rsnd_ssi_probe(struct platform_device *pdev, else if (rsnd_ssi_pio_available(ssi)) ops = &rsnd_ssi_pio_ops; - rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + ret = rsnd_mod_init(&ssi->mod, ops, clk, RSND_MOD_SSI, i); + if (ret) + return ret; rsnd_ssi_parent_clk_setup(priv, ssi); } return 0; } + +void rsnd_ssi_remove(struct platform_device *pdev, + struct rsnd_priv *priv) +{ + struct rsnd_ssi *ssi; + int i; + + for_each_rsnd_ssi(ssi, priv, i) { + rsnd_mod_quit(&ssi->mod); + } +} diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 10f0886e78ec..23732523f87c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -292,6 +292,9 @@ static const struct file_operations codec_reg_fops = { static void soc_init_component_debugfs(struct snd_soc_component *component) { + if (!component->card->debugfs_card_root) + return; + if (component->debugfs_prefix) { char *name; @@ -455,6 +458,9 @@ static const struct file_operations platform_list_fops = { static void soc_init_card_debugfs(struct snd_soc_card *card) { + if (!snd_soc_debugfs_root) + return; + card->debugfs_card_root = debugfs_create_dir(card->name, snd_soc_debugfs_root); if (!card->debugfs_card_root) { @@ -476,6 +482,34 @@ static void soc_cleanup_card_debugfs(struct snd_soc_card *card) debugfs_remove_recursive(card->debugfs_card_root); } + +static void snd_soc_debugfs_init(void) +{ + snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); + if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { + pr_warn("ASoC: Failed to create debugfs directory\n"); + snd_soc_debugfs_root = NULL; + return; + } + + if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, + &codec_list_fops)) + pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); + + if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, + &dai_list_fops)) + pr_warn("ASoC: Failed to create DAI list debugfs file\n"); + + if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, + &platform_list_fops)) + pr_warn("ASoC: Failed to create platform list debugfs file\n"); +} + +static void snd_soc_debugfs_exit(void) +{ + debugfs_remove_recursive(snd_soc_debugfs_root); +} + #else #define soc_init_codec_debugfs NULL @@ -497,6 +531,15 @@ static inline void soc_init_card_debugfs(struct snd_soc_card *card) static inline void soc_cleanup_card_debugfs(struct snd_soc_card *card) { } + +static inline void snd_soc_debugfs_init(void) +{ +} + +static inline void snd_soc_debugfs_exit(void) +{ +} + #endif struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card, @@ -595,15 +638,9 @@ int snd_soc_suspend(struct device *dev) cpu_dai->driver->suspend(cpu_dai); } - /* close any waiting streams and save state */ - for (i = 0; i < card->num_rtd; i++) { - struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais; + /* close any waiting streams */ + for (i = 0; i < card->num_rtd; i++) flush_delayed_work(&card->rtd[i].delayed_work); - for (j = 0; j < card->rtd[i].num_codecs; j++) { - codec_dais[j]->codec->dapm.suspend_bias_level = - codec_dais[j]->codec->dapm.bias_level; - } - } for (i = 0; i < card->num_rtd; i++) { @@ -1261,7 +1298,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = cpu_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); @@ -1273,7 +1311,8 @@ static int soc_link_dai_widgets(struct snd_soc_card *card, capture_w = codec_dai->capture_widget; if (play_w && capture_w) { ret = snd_soc_dapm_new_pcm(card, dai_link->params, - capture_w, play_w); + dai_link->num_params, capture_w, + play_w); if (ret != 0) { dev_err(card->dev, "ASoC: Can't link %s to %s: %d\n", play_w->name, capture_w->name, ret); @@ -1322,21 +1361,17 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order) } } + if (dai_link->dai_fmt) + snd_soc_runtime_set_dai_fmt(rtd, dai_link->dai_fmt); + ret = soc_post_component_init(rtd, dai_link->name); if (ret) return ret; #ifdef CONFIG_DEBUG_FS /* add DPCM sysfs entries */ - if (dai_link->dynamic) { - ret = soc_dpcm_debugfs_add(rtd); - if (ret < 0) { - dev_err(rtd->dev, - "ASoC: failed to add dpcm sysfs entries: %d\n", - ret); - return ret; - } - } + if (dai_link->dynamic) + soc_dpcm_debugfs_add(rtd); #endif if (cpu_dai->driver->compress_dai) { @@ -1426,7 +1461,6 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num) /* unregister the rtd device */ if (rtd->dev_registered) { - device_remove_file(rtd->dev, &dev_attr_codec_reg); device_unregister(rtd->dev); rtd->dev_registered = 0; } @@ -1560,6 +1594,8 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) goto base_error; } + soc_init_card_debugfs(card); + card->dapm.bias_level = SND_SOC_BIAS_OFF; card->dapm.dev = card->dev; card->dapm.card = card; @@ -1578,6 +1614,10 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets, card->num_dapm_widgets); + if (card->of_dapm_widgets) + snd_soc_dapm_new_controls(&card->dapm, card->of_dapm_widgets, + card->num_of_dapm_widgets); + /* initialise the sound card only once */ if (card->probe) { ret = card->probe(card); @@ -1633,11 +1673,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card) snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, card->num_dapm_routes); - for (i = 0; i < card->num_links; i++) { - if (card->dai_link[i].dai_fmt) - snd_soc_runtime_set_dai_fmt(&card->rtd[i], - card->dai_link[i].dai_fmt); - } + if (card->of_dapm_routes) + snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes, + card->num_of_dapm_routes); snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname), "%s", card->name); @@ -1694,6 +1732,7 @@ card_probe_error: if (card->remove) card->remove(card); + soc_cleanup_card_debugfs(card); snd_card_free(card->snd_card); base_error: @@ -2372,8 +2411,6 @@ int snd_soc_register_card(struct snd_soc_card *card) snd_soc_initialize_card_lists(card); - soc_init_card_debugfs(card); - card->rtd = devm_kzalloc(card->dev, sizeof(struct snd_soc_pcm_runtime) * (card->num_links + card->num_aux_devs), @@ -2404,7 +2441,7 @@ int snd_soc_register_card(struct snd_soc_card *card) ret = snd_soc_instantiate_card(card); if (ret != 0) - soc_cleanup_card_debugfs(card); + return ret; /* deactivate pins to sleep state */ for (i = 0; i < card->num_rtd; i++) { @@ -3242,8 +3279,8 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card, widgets[i].name = wname; } - card->dapm_widgets = widgets; - card->num_dapm_widgets = num_widgets; + card->of_dapm_widgets = widgets; + card->num_of_dapm_widgets = num_widgets; return 0; } @@ -3327,8 +3364,8 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card->num_dapm_routes = num_routes; - card->dapm_routes = routes; + card->num_of_dapm_routes = num_routes; + card->of_dapm_routes = routes; return 0; } @@ -3587,26 +3624,7 @@ EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs); static int __init snd_soc_init(void) { -#ifdef CONFIG_DEBUG_FS - snd_soc_debugfs_root = debugfs_create_dir("asoc", NULL); - if (IS_ERR(snd_soc_debugfs_root) || !snd_soc_debugfs_root) { - pr_warn("ASoC: Failed to create debugfs directory\n"); - snd_soc_debugfs_root = NULL; - } - - if (!debugfs_create_file("codecs", 0444, snd_soc_debugfs_root, NULL, - &codec_list_fops)) - pr_warn("ASoC: Failed to create CODEC list debugfs file\n"); - - if (!debugfs_create_file("dais", 0444, snd_soc_debugfs_root, NULL, - &dai_list_fops)) - pr_warn("ASoC: Failed to create DAI list debugfs file\n"); - - if (!debugfs_create_file("platforms", 0444, snd_soc_debugfs_root, NULL, - &platform_list_fops)) - pr_warn("ASoC: Failed to create platform list debugfs file\n"); -#endif - + snd_soc_debugfs_init(); snd_soc_util_init(); return platform_driver_register(&soc_driver); @@ -3616,9 +3634,9 @@ module_init(snd_soc_init); static void __exit snd_soc_exit(void) { snd_soc_util_exit(); + snd_soc_debugfs_exit(); #ifdef CONFIG_DEBUG_FS - debugfs_remove_recursive(snd_soc_debugfs_root); #endif platform_driver_unregister(&soc_driver); } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index b6f88202b8c9..defe0f0082b5 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -473,16 +473,6 @@ struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm( } EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm); -/** - * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol - * @kcontrol: The kcontrol - */ -struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol) -{ - return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol)); -} -EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec); - static void dapm_reset(struct snd_soc_card *card) { struct snd_soc_dapm_widget *w; @@ -853,6 +843,36 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w) return 0; } +/* create new dapm dai link control */ +static int dapm_new_dai_link(struct snd_soc_dapm_widget *w) +{ + int i, ret; + struct snd_kcontrol *kcontrol; + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_card *card = dapm->card->snd_card; + + /* create control for links with > 1 config */ + if (w->num_params <= 1) + return 0; + + /* add kcontrol */ + for (i = 0; i < w->num_kcontrols; i++) { + kcontrol = snd_soc_cnew(&w->kcontrol_news[i], w, + w->name, NULL); + ret = snd_ctl_add(card, kcontrol); + if (ret < 0) { + dev_err(dapm->dev, + "ASoC: failed to add widget %s dapm kcontrol %s: %d\n", + w->name, w->kcontrol_news[i].name, ret); + return ret; + } + kcontrol->private_data = w; + w->kcontrols[i] = kcontrol; + } + + return 0; +} + /* We implement power down on suspend by checking the power state of * the ALSA card - when we are suspending the ALSA state for the card * is set to D3. @@ -1898,6 +1918,9 @@ void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, { struct dentry *d; + if (!parent) + return; + dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); if (!dapm->debugfs_dapm) { @@ -2719,6 +2742,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card) case snd_soc_dapm_out_drv: dapm_new_pga(w); break; + case snd_soc_dapm_dai_link: + dapm_new_dai_link(w); + break; default: break; } @@ -3193,7 +3219,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, { struct snd_soc_dapm_path *source_p, *sink_p; struct snd_soc_dai *source, *sink; - const struct snd_soc_pcm_stream *config = w->params; + const struct snd_soc_pcm_stream *config = w->params + w->params_select; struct snd_pcm_substream substream; struct snd_pcm_hw_params *params = NULL; u64 fmt; @@ -3285,22 +3311,97 @@ out: return ret; } +static int snd_soc_dapm_dai_link_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + ucontrol->value.integer.value[0] = w->params_select; + + return 0; +} + +static int snd_soc_dapm_dai_link_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *w = snd_kcontrol_chip(kcontrol); + + /* Can't change the config when widget is already powered */ + if (w->power) + return -EBUSY; + + if (ucontrol->value.integer.value[0] == w->params_select) + return 0; + + if (ucontrol->value.integer.value[0] >= w->num_params) + return -EINVAL; + + w->params_select = ucontrol->value.integer.value[0]; + + return 0; +} + int snd_soc_dapm_new_pcm(struct snd_soc_card *card, const struct snd_soc_pcm_stream *params, + unsigned int num_params, struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink) { struct snd_soc_dapm_widget template; struct snd_soc_dapm_widget *w; - size_t len; char *link_name; - int ret; - - len = strlen(source->name) + strlen(sink->name) + 2; - link_name = devm_kzalloc(card->dev, len, GFP_KERNEL); - if (!link_name) + int ret, count; + unsigned long private_value; + const char **w_param_text; + struct soc_enum w_param_enum[] = { + SOC_ENUM_SINGLE(0, 0, 0, NULL), + }; + struct snd_kcontrol_new kcontrol_dai_link[] = { + SOC_ENUM_EXT(NULL, w_param_enum[0], + snd_soc_dapm_dai_link_get, + snd_soc_dapm_dai_link_put), + }; + const struct snd_soc_pcm_stream *config = params; + + w_param_text = devm_kcalloc(card->dev, num_params, + sizeof(char *), GFP_KERNEL); + if (!w_param_text) return -ENOMEM; - snprintf(link_name, len, "%s-%s", source->name, sink->name); + + link_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-%s", + source->name, sink->name); + if (!link_name) { + ret = -ENOMEM; + goto outfree_w_param; + } + + for (count = 0 ; count < num_params; count++) { + if (!config->stream_name) { + dev_warn(card->dapm.dev, + "ASoC: anonymous config %d for dai link %s\n", + count, link_name); + w_param_text[count] = + devm_kasprintf(card->dev, GFP_KERNEL, + "Anonymous Configuration %d", + count); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + } else { + w_param_text[count] = devm_kmemdup(card->dev, + config->stream_name, + strlen(config->stream_name) + 1, + GFP_KERNEL); + if (!w_param_text[count]) { + ret = -ENOMEM; + goto outfree_link_name; + } + } + config++; + } + w_param_enum[0].items = num_params; + w_param_enum[0].texts = w_param_text; memset(&template, 0, sizeof(template)); template.reg = SND_SOC_NOPM; @@ -3309,6 +3410,30 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, template.event = snd_soc_dai_link_event; template.event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD; + template.num_kcontrols = 1; + /* duplicate w_param_enum on heap so that memory persists */ + private_value = + (unsigned long) devm_kmemdup(card->dev, + (void *)(kcontrol_dai_link[0].private_value), + sizeof(struct soc_enum), GFP_KERNEL); + if (!private_value) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_link_name; + } + kcontrol_dai_link[0].private_value = private_value; + /* duplicate kcontrol_dai_link on heap so that memory persists */ + template.kcontrol_news = + devm_kmemdup(card->dev, &kcontrol_dai_link[0], + sizeof(struct snd_kcontrol_new), + GFP_KERNEL); + if (!template.kcontrol_news) { + dev_err(card->dev, "ASoC: Failed to create control for %s widget\n", + link_name); + ret = -ENOMEM; + goto outfree_private_value; + } dev_dbg(card->dev, "ASoC: adding %s widget\n", link_name); @@ -3316,15 +3441,32 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card, if (!w) { dev_err(card->dev, "ASoC: Failed to create %s widget\n", link_name); - return -ENOMEM; + ret = -ENOMEM; + goto outfree_kcontrol_news; } w->params = params; + w->num_params = num_params; ret = snd_soc_dapm_add_path(&card->dapm, source, w, NULL, NULL); if (ret) - return ret; + goto outfree_w; return snd_soc_dapm_add_path(&card->dapm, w, sink, NULL, NULL); + +outfree_w: + devm_kfree(card->dev, w); +outfree_kcontrol_news: + devm_kfree(card->dev, (void *)template.kcontrol_news); +outfree_private_value: + devm_kfree(card->dev, (void *)private_value); +outfree_link_name: + devm_kfree(card->dev, link_name); +outfree_w_param: + for (count = 0 ; count < num_params; count++) + devm_kfree(card->dev, (void *)w_param_text[count]); + devm_kfree(card->dev, w_param_text); + + return ret; } int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index 4380dcc064a5..9f60c25c4568 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -22,30 +22,42 @@ #include <trace/events/asoc.h> /** - * snd_soc_jack_new - Create a new jack - * @codec: ASoC codec + * snd_soc_card_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 + * @pins: Array of jack pins to be added to the jack or NULL + * @num_pins: Number of elements in the @pins array * * 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_codec *codec, const char *id, int type, - struct snd_soc_jack *jack) +int snd_soc_card_jack_new(struct snd_soc_card *card, const char *id, int type, + struct snd_soc_jack *jack, struct snd_soc_jack_pin *pins, + unsigned int num_pins) { + int ret; + mutex_init(&jack->mutex); - jack->codec = codec; + jack->card = card; INIT_LIST_HEAD(&jack->pins); INIT_LIST_HEAD(&jack->jack_zones); BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier); - return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack); + ret = snd_jack_new(card->snd_card, id, type, &jack->jack); + if (ret) + return ret; + + if (num_pins) + return snd_soc_jack_add_pins(jack, num_pins, pins); + + return 0; } -EXPORT_SYMBOL_GPL(snd_soc_jack_new); +EXPORT_SYMBOL_GPL(snd_soc_card_jack_new); /** * snd_soc_jack_report - Report the current status for a jack @@ -63,7 +75,6 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_new); */ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) { - struct snd_soc_codec *codec; struct snd_soc_dapm_context *dapm; struct snd_soc_jack_pin *pin; unsigned int sync = 0; @@ -74,8 +85,7 @@ void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask) if (!jack) return; - codec = jack->codec; - dapm = &codec->dapm; + dapm = &jack->card->dapm; mutex_lock(&jack->mutex); @@ -175,12 +185,12 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!pins[i].pin) { - dev_err(jack->codec->dev, "ASoC: No name for pin %d\n", + dev_err(jack->card->dev, "ASoC: No name for pin %d\n", i); return -EINVAL; } if (!pins[i].mask) { - dev_err(jack->codec->dev, "ASoC: No mask for pin %d" + dev_err(jack->card->dev, "ASoC: No mask for pin %d" " (%s)\n", i, pins[i].pin); return -EINVAL; } @@ -260,7 +270,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio) static irqreturn_t gpio_handler(int irq, void *data) { struct snd_soc_jack_gpio *gpio = data; - struct device *dev = gpio->jack->codec->component.card->dev; + struct device *dev = gpio->jack->card->dev; trace_snd_soc_jack_irq(gpio->name); @@ -299,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, for (i = 0; i < count; i++) { if (!gpios[i].name) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: No name for gpio at index %d\n", i); ret = -EINVAL; goto undo; @@ -320,7 +330,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, } else { /* legacy GPIO number */ if (!gpio_is_valid(gpios[i].gpio)) { - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Invalid gpio %d\n", gpios[i].gpio); ret = -EINVAL; @@ -350,7 +360,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, if (gpios[i].wake) { ret = irq_set_irq_wake(gpiod_to_irq(gpios[i].desc), 1); if (ret != 0) - dev_err(jack->codec->dev, + dev_err(jack->card->dev, "ASoC: Failed to mark GPIO at index %d as wake source: %d\n", i, ret); } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 6b0136e7cb88..35fe58f4fa86 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1097,8 +1097,9 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe, stream ? "<-" : "->", be->dai_link->name); #ifdef CONFIG_DEBUG_FS - dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, - fe->debugfs_dpcm_root, &dpcm->state); + if (fe->debugfs_dpcm_root) + dpcm->debugfs_state = debugfs_create_u32(be->dai_link->name, 0644, + fe->debugfs_dpcm_root, &dpcm->state); #endif return 1; } @@ -2511,6 +2512,7 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num) /* DAPM dai link stream work */ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + pcm->nonatomic = rtd->dai_link->nonatomic; rtd->pcm = pcm; pcm->private_data = rtd; @@ -2802,10 +2804,13 @@ static const struct file_operations dpcm_state_fops = { .llseek = default_llseek, }; -int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) +void soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) { if (!rtd->dai_link) - return 0; + return; + + if (!rtd->card->debugfs_card_root) + return; rtd->debugfs_dpcm_root = debugfs_create_dir(rtd->dai_link->name, rtd->card->debugfs_card_root); @@ -2813,13 +2818,11 @@ int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd) dev_dbg(rtd->dev, "ASoC: Failed to create dpcm debugfs directory %s\n", rtd->dai_link->name); - return -EINVAL; + return; } rtd->debugfs_dpcm_state = debugfs_create_file("state", 0444, rtd->debugfs_dpcm_root, rtd, &dpcm_state_fops); - - return 0; } #endif diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c index 769aca2fc5f5..ba272e21a6fa 100644 --- a/sound/soc/tegra/tegra_alc5632.c +++ b/sound/soc/tegra/tegra_alc5632.c @@ -101,16 +101,12 @@ static const struct snd_kcontrol_new tegra_alc5632_controls[] = { static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET, - &tegra_alc5632_hs_jack); - snd_soc_jack_add_pins(&tegra_alc5632_hs_jack, - ARRAY_SIZE(tegra_alc5632_hs_jack_pins), - tegra_alc5632_hs_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headset Jack", SND_JACK_HEADSET, + &tegra_alc5632_hs_jack, + tegra_alc5632_hs_jack_pins, + ARRAY_SIZE(tegra_alc5632_hs_jack_pins)); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_alc5632_hp_jack_gpio.gpio = machine->gpio_hp_det; @@ -119,7 +115,7 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd) &tegra_alc5632_hp_jack_gpio); } - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); return 0; } diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index af3fb997b752..902da36581d1 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -133,24 +133,26 @@ static const struct snd_soc_dapm_widget tegra_max98090_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphones", NULL), SND_SOC_DAPM_SPK("Speakers", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), }; static const struct snd_kcontrol_new tegra_max98090_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphones"), SOC_DAPM_PIN_SWITCH("Speakers"), + SOC_DAPM_PIN_SWITCH("Mic Jack"), + SOC_DAPM_PIN_SWITCH("Int Mic"), }; static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card); if (gpio_is_valid(machine->gpio_hp_det)) { - snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, - &tegra_max98090_hp_jack); - snd_soc_jack_add_pins(&tegra_max98090_hp_jack, - ARRAY_SIZE(tegra_max98090_hp_jack_pins), - tegra_max98090_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", + SND_JACK_HEADPHONE, + &tegra_max98090_hp_jack, + tegra_max98090_hp_jack_pins, + ARRAY_SIZE(tegra_max98090_hp_jack_pins)); tegra_max98090_hp_jack_gpio.gpio = machine->gpio_hp_det; snd_soc_jack_add_gpios(&tegra_max98090_hp_jack, @@ -159,11 +161,11 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd) } if (gpio_is_valid(machine->gpio_mic_det)) { - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_max98090_mic_jack); - snd_soc_jack_add_pins(&tegra_max98090_mic_jack, - ARRAY_SIZE(tegra_max98090_mic_jack_pins), - tegra_max98090_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, + &tegra_max98090_mic_jack, + tegra_max98090_mic_jack_pins, + ARRAY_SIZE(tegra_max98090_mic_jack_pins)); tegra_max98090_mic_jack_gpio.gpio = machine->gpio_mic_det; snd_soc_jack_add_gpios(&tegra_max98090_mic_jack, diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index ed759a3076b8..773daecaa5e8 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -108,15 +108,11 @@ static const struct snd_kcontrol_new tegra_rt5640_controls[] = { static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE, - &tegra_rt5640_hp_jack); - snd_soc_jack_add_pins(&tegra_rt5640_hp_jack, - ARRAY_SIZE(tegra_rt5640_hp_jack_pins), - tegra_rt5640_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphones", SND_JACK_HEADPHONE, + &tegra_rt5640_hp_jack, tegra_rt5640_hp_jack_pins, + ARRAY_SIZE(tegra_rt5640_hp_jack_pins)); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det; diff --git a/sound/soc/tegra/tegra_rt5677.c b/sound/soc/tegra/tegra_rt5677.c index e4cf978a6e3a..1470873ecde6 100644 --- a/sound/soc/tegra/tegra_rt5677.c +++ b/sound/soc/tegra/tegra_rt5677.c @@ -141,15 +141,11 @@ static const struct snd_kcontrol_new tegra_rt5677_controls[] = { static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct tegra_rt5677 *machine = snd_soc_card_get_drvdata(rtd->card); - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &tegra_rt5677_hp_jack); - snd_soc_jack_add_pins(&tegra_rt5677_hp_jack, 1, - &tegra_rt5677_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, + &tegra_rt5677_hp_jack, + &tegra_rt5677_hp_jack_pins, 1); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_rt5677_hp_jack_gpio.gpio = machine->gpio_hp_det; @@ -158,10 +154,9 @@ static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) } - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_rt5677_mic_jack); - snd_soc_jack_add_pins(&tegra_rt5677_mic_jack, 1, - &tegra_rt5677_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_rt5677_mic_jack, + &tegra_rt5677_mic_jack_pins, 1); if (gpio_is_valid(machine->gpio_mic_present)) { tegra_rt5677_mic_jack_gpio.gpio = machine->gpio_mic_present; @@ -169,7 +164,7 @@ static int tegra_rt5677_asoc_init(struct snd_soc_pcm_runtime *rtd) &tegra_rt5677_mic_jack_gpio); } - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "MICBIAS1"); return 0; } @@ -331,7 +326,6 @@ static const struct of_device_id tegra_rt5677_of_match[] = { static struct platform_driver tegra_rt5677_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = tegra_rt5677_of_match, }, diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index e52420dae2b4..21604009bc1a 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -171,31 +171,28 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_card *card = rtd->card; struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card); if (gpio_is_valid(machine->gpio_hp_det)) { tegra_wm8903_hp_jack_gpio.gpio = machine->gpio_hp_det; - snd_soc_jack_new(codec, "Headphone Jack", SND_JACK_HEADPHONE, - &tegra_wm8903_hp_jack); - snd_soc_jack_add_pins(&tegra_wm8903_hp_jack, - ARRAY_SIZE(tegra_wm8903_hp_jack_pins), - tegra_wm8903_hp_jack_pins); + snd_soc_card_jack_new(rtd->card, "Headphone Jack", + SND_JACK_HEADPHONE, &tegra_wm8903_hp_jack, + tegra_wm8903_hp_jack_pins, + ARRAY_SIZE(tegra_wm8903_hp_jack_pins)); snd_soc_jack_add_gpios(&tegra_wm8903_hp_jack, 1, &tegra_wm8903_hp_jack_gpio); } - snd_soc_jack_new(codec, "Mic Jack", SND_JACK_MICROPHONE, - &tegra_wm8903_mic_jack); - snd_soc_jack_add_pins(&tegra_wm8903_mic_jack, - ARRAY_SIZE(tegra_wm8903_mic_jack_pins), - tegra_wm8903_mic_jack_pins); + snd_soc_card_jack_new(rtd->card, "Mic Jack", SND_JACK_MICROPHONE, + &tegra_wm8903_mic_jack, + tegra_wm8903_mic_jack_pins, + ARRAY_SIZE(tegra_wm8903_mic_jack_pins)); wm8903_mic_detect(codec, &tegra_wm8903_mic_jack, SND_JACK_MICROPHONE, 0); - snd_soc_dapm_force_enable_pin(dapm, "MICBIAS"); + snd_soc_dapm_force_enable_pin(&card->dapm, "MICBIAS"); return 0; } diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c index 2868b4839bc0..6492f8143ff1 100644 --- a/sound/soc/tegra/tegra_wm9712.c +++ b/sound/soc/tegra/tegra_wm9712.c @@ -46,11 +46,7 @@ static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_codec *codec = codec_dai->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - return snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + return snd_soc_dapm_force_enable_pin(&rtd->card->dapm, "Mic Bias"); } static struct snd_soc_dai_link tegra_wm9712_dai = { diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c index aa65370db82a..b81a7a4c938b 100644 --- a/sound/soc/ux500/mop500_ab8500.c +++ b/sound/soc/ux500/mop500_ab8500.c @@ -362,7 +362,7 @@ struct snd_soc_ops mop500_ab8500_ops[] = { int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) { - struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; struct device *dev = rtd->card->dev; struct mop500_ab8500_drvdata *drvdata; int ret; @@ -407,23 +407,23 @@ int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd) return ret; } - ret = snd_soc_dapm_disable_pin(&codec->dapm, "Earpiece"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Speaker Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Speaker Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineOut Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineOut Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Vibra 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Vibra 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Mic 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "Mic 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineIn Left"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "LineIn Right"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 1"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 2"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 3"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 4"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 5"); - ret |= snd_soc_dapm_disable_pin(&codec->dapm, "DMic 6"); + ret = snd_soc_dapm_disable_pin(dapm, "Earpiece"); + ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left"); + ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5"); + ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6"); return ret; } diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 9a28365126f9..7c5a70139278 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1115,6 +1115,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) { /* devices which do not support reading the sample rate. */ switch (chip->usb_id) { + case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */ case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */ case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */ return true; @@ -1125,17 +1126,24 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip) /* Marantz/Denon USB DACs need a vendor cmd to switch * between PCM and native DSD mode */ +static bool is_marantz_denon_dac(unsigned int id) +{ + switch (id) { + case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ + case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ + case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + return true; + } + return false; +} + int snd_usb_select_mode_quirk(struct snd_usb_substream *subs, struct audioformat *fmt) { struct usb_device *dev = subs->dev; int err; - switch (subs->stream->chip->usb_id) { - case USB_ID(0x154e, 0x1003): /* Denon DA-300USB */ - case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ - case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ - + if (is_marantz_denon_dac(subs->stream->chip->usb_id)) { /* First switch to alt set 0, otherwise the mode switch cmd * will not be accepted by the DAC */ @@ -1208,17 +1216,10 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe, /* Marantz/Denon devices with USB DAC functionality need a delay * after each class compliant request */ - if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) && - (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) { - - switch (le16_to_cpu(dev->descriptor.idProduct)) { - case 0x1003: /* Denon DA300-USB */ - case 0x3005: /* Marantz HD-DAC1 */ - case 0x3006: /* Marantz SA-14S1 */ - mdelay(20); - break; - } - } + if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct))) + && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) + mdelay(20); /* Zoom R16/24 needs a tiny delay here, otherwise requests like * get/set frequency return as failed despite actually succeeding. @@ -1273,15 +1274,9 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, } /* Denon/Marantz devices with USB DAC functionality */ - switch (chip->usb_id) { - case USB_ID(0x154e, 0x1003): /* Denon DA300-USB */ - case USB_ID(0x154e, 0x3005): /* Marantz HD-DAC1 */ - case USB_ID(0x154e, 0x3006): /* Marantz SA-14S1 */ + if (is_marantz_denon_dac(chip->usb_id)) { if (fp->altsetting == 2) return SNDRV_PCM_FMTBIT_DSD_U32_BE; - break; - default: - break; } return 0; |