2014-11-11 00:41:46 +03:00
/*
* soc - ac97 . c - - ALSA SoC Audio Layer AC97 support
*
* Copyright 2005 Wolfson Microelectronics PLC .
* Copyright 2005 Openedhand Ltd .
* Copyright ( C ) 2010 Slimlogic Ltd .
* Copyright ( C ) 2010 Texas Instruments Inc .
*
* Author : Liam Girdwood < lrg @ slimlogic . co . uk >
* with code , comments and ideas from : -
* Richard Purdie < richard @ openedhand . 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/ctype.h>
# include <linux/delay.h>
# include <linux/export.h>
# include <linux/gpio.h>
2015-11-11 15:12:51 +03:00
# include <linux/gpio/driver.h>
2014-11-11 00:41:46 +03:00
# include <linux/init.h>
# include <linux/of_gpio.h>
# include <linux/of.h>
# include <linux/pinctrl/consumer.h>
# include <linux/slab.h>
# include <sound/ac97_codec.h>
# include <sound/soc.h>
struct snd_ac97_reset_cfg {
struct pinctrl * pctl ;
struct pinctrl_state * pstate_reset ;
struct pinctrl_state * pstate_warm_reset ;
struct pinctrl_state * pstate_run ;
int gpio_sdata ;
int gpio_sync ;
int gpio_reset ;
} ;
2015-11-11 15:12:51 +03:00
struct snd_ac97_gpio_priv {
# ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip ;
# endif
unsigned int gpios_set ;
struct snd_soc_codec * codec ;
} ;
2014-11-11 00:41:47 +03:00
static struct snd_ac97_bus soc_ac97_bus = {
. ops = NULL , /* Gets initialized in snd_soc_set_ac97_ops() */
} ;
2014-11-11 00:41:46 +03:00
static void soc_ac97_device_release ( struct device * dev )
{
kfree ( to_ac97_t ( dev ) ) ;
}
2015-11-11 15:12:51 +03:00
# ifdef CONFIG_GPIOLIB
static inline struct snd_soc_codec * gpio_to_codec ( struct gpio_chip * chip )
{
2015-12-09 01:48:29 +03:00
struct snd_ac97_gpio_priv * gpio_priv = gpiochip_get_data ( chip ) ;
2015-11-11 15:12:51 +03:00
return gpio_priv - > codec ;
}
static int snd_soc_ac97_gpio_request ( struct gpio_chip * chip , unsigned offset )
{
if ( offset > = AC97_NUM_GPIOS )
return - EINVAL ;
return 0 ;
}
static int snd_soc_ac97_gpio_direction_in ( struct gpio_chip * chip ,
unsigned offset )
{
struct snd_soc_codec * codec = gpio_to_codec ( chip ) ;
dev_dbg ( codec - > dev , " set gpio %d to output \n " , offset ) ;
return snd_soc_update_bits ( codec , AC97_GPIO_CFG ,
1 < < offset , 1 < < offset ) ;
}
static int snd_soc_ac97_gpio_get ( struct gpio_chip * chip , unsigned offset )
{
struct snd_soc_codec * codec = gpio_to_codec ( chip ) ;
int ret ;
ret = snd_soc_read ( codec , AC97_GPIO_STATUS ) ;
dev_dbg ( codec - > dev , " get gpio %d : %d \n " , offset ,
ret < 0 ? ret : ret & ( 1 < < offset ) ) ;
2015-12-22 17:51:39 +03:00
return ret < 0 ? ret : ! ! ( ret & ( 1 < < offset ) ) ;
2015-11-11 15:12:51 +03:00
}
static void snd_soc_ac97_gpio_set ( struct gpio_chip * chip , unsigned offset ,
int value )
{
2015-12-09 01:48:29 +03:00
struct snd_ac97_gpio_priv * gpio_priv = gpiochip_get_data ( chip ) ;
2015-11-11 15:12:51 +03:00
struct snd_soc_codec * codec = gpio_to_codec ( chip ) ;
gpio_priv - > gpios_set & = ~ ( 1 < < offset ) ;
gpio_priv - > gpios_set | = ( ! ! value ) < < offset ;
snd_soc_write ( codec , AC97_GPIO_STATUS , gpio_priv - > gpios_set ) ;
dev_dbg ( codec - > dev , " set gpio %d to %d \n " , offset , ! ! value ) ;
}
static int snd_soc_ac97_gpio_direction_out ( struct gpio_chip * chip ,
unsigned offset , int value )
{
struct snd_soc_codec * codec = gpio_to_codec ( chip ) ;
dev_dbg ( codec - > dev , " set gpio %d to output \n " , offset ) ;
snd_soc_ac97_gpio_set ( chip , offset , value ) ;
return snd_soc_update_bits ( codec , AC97_GPIO_CFG , 1 < < offset , 0 ) ;
}
2016-09-11 15:14:41 +03:00
static const struct gpio_chip snd_soc_ac97_gpio_chip = {
2015-11-11 15:12:51 +03:00
. label = " snd_soc_ac97 " ,
. owner = THIS_MODULE ,
. request = snd_soc_ac97_gpio_request ,
. direction_input = snd_soc_ac97_gpio_direction_in ,
. get = snd_soc_ac97_gpio_get ,
. direction_output = snd_soc_ac97_gpio_direction_out ,
. set = snd_soc_ac97_gpio_set ,
. can_sleep = 1 ,
} ;
static int snd_soc_ac97_init_gpio ( struct snd_ac97 * ac97 ,
struct snd_soc_codec * codec )
{
struct snd_ac97_gpio_priv * gpio_priv ;
int ret ;
gpio_priv = devm_kzalloc ( codec - > dev , sizeof ( * gpio_priv ) , GFP_KERNEL ) ;
if ( ! gpio_priv )
return - ENOMEM ;
ac97 - > gpio_priv = gpio_priv ;
gpio_priv - > codec = codec ;
gpio_priv - > gpio_chip = snd_soc_ac97_gpio_chip ;
gpio_priv - > gpio_chip . ngpio = AC97_NUM_GPIOS ;
2015-12-07 15:32:28 +03:00
gpio_priv - > gpio_chip . parent = codec - > dev ;
2015-11-11 15:12:51 +03:00
gpio_priv - > gpio_chip . base = - 1 ;
2015-12-09 01:48:29 +03:00
ret = gpiochip_add_data ( & gpio_priv - > gpio_chip , gpio_priv ) ;
2015-11-11 15:12:51 +03:00
if ( ret ! = 0 )
dev_err ( codec - > dev , " Failed to add GPIOs: %d \n " , ret ) ;
return ret ;
}
static void snd_soc_ac97_free_gpio ( struct snd_ac97 * ac97 )
{
gpiochip_remove ( & ac97 - > gpio_priv - > gpio_chip ) ;
}
# else
static int snd_soc_ac97_init_gpio ( struct snd_ac97 * ac97 ,
struct snd_soc_codec * codec )
{
return 0 ;
}
static void snd_soc_ac97_free_gpio ( struct snd_ac97 * ac97 )
{
}
# endif
2014-11-11 00:41:46 +03:00
/**
2015-01-23 18:21:36 +03:00
* snd_soc_alloc_ac97_codec ( ) - Allocate new a AC ' 97 device
* @ codec : The CODEC for which to create the AC ' 97 device
2014-11-11 00:41:46 +03:00
*
2015-01-23 18:21:36 +03:00
* Allocated a new snd_ac97 device and intializes it , but does not yet register
* it . The caller is responsible to either call device_add ( & ac97 - > dev ) to
* register the device , or to call put_device ( & ac97 - > dev ) to free the device .
*
* Returns : A snd_ac97 device or a PTR_ERR in case of an error .
2014-11-11 00:41:46 +03:00
*/
2015-01-23 18:21:36 +03:00
struct snd_ac97 * snd_soc_alloc_ac97_codec ( struct snd_soc_codec * codec )
2014-11-11 00:41:46 +03:00
{
2014-11-11 00:41:53 +03:00
struct snd_ac97 * ac97 ;
2014-11-11 00:41:50 +03:00
2014-11-11 00:41:53 +03:00
ac97 = kzalloc ( sizeof ( struct snd_ac97 ) , GFP_KERNEL ) ;
if ( ac97 = = NULL )
return ERR_PTR ( - ENOMEM ) ;
2014-11-11 00:41:46 +03:00
2014-11-11 00:41:53 +03:00
ac97 - > bus = & soc_ac97_bus ;
ac97 - > num = 0 ;
2014-11-11 00:41:50 +03:00
2014-11-11 00:41:53 +03:00
ac97 - > dev . bus = & ac97_bus_type ;
ac97 - > dev . parent = codec - > component . card - > dev ;
ac97 - > dev . release = soc_ac97_device_release ;
2014-11-11 00:41:46 +03:00
2014-11-11 00:41:53 +03:00
dev_set_name ( & ac97 - > dev , " %d-%d:%s " ,
2014-11-11 00:41:50 +03:00
codec - > component . card - > snd_card - > number , 0 ,
codec - > component . name ) ;
2015-01-23 18:21:36 +03:00
device_initialize ( & ac97 - > dev ) ;
return ac97 ;
}
EXPORT_SYMBOL ( snd_soc_alloc_ac97_codec ) ;
/**
* snd_soc_new_ac97_codec - initailise AC97 device
* @ codec : audio codec
2015-07-21 22:53:01 +03:00
* @ id : The expected device ID
* @ id_mask : Mask that is applied to the device ID before comparing with @ id
2015-01-23 18:21:36 +03:00
*
* Initialises AC97 codec resources for use by ad - hoc devices only .
2015-07-21 22:53:01 +03:00
*
* If @ id is not 0 this function will reset the device , then read the ID from
* the device and check if it matches the expected ID . If it doesn ' t match an
* error will be returned and device will not be registered .
*
* Returns : A PTR_ERR ( ) on failure or a valid snd_ac97 struct on success .
2015-01-23 18:21:36 +03:00
*/
2015-07-21 22:53:01 +03:00
struct snd_ac97 * snd_soc_new_ac97_codec ( struct snd_soc_codec * codec ,
unsigned int id , unsigned int id_mask )
2015-01-23 18:21:36 +03:00
{
struct snd_ac97 * ac97 ;
int ret ;
ac97 = snd_soc_alloc_ac97_codec ( codec ) ;
if ( IS_ERR ( ac97 ) )
return ac97 ;
2015-07-21 22:53:01 +03:00
if ( id ) {
ret = snd_ac97_reset ( ac97 , false , id , id_mask ) ;
if ( ret < 0 ) {
dev_err ( codec - > dev , " Failed to reset AC97 device: %d \n " ,
ret ) ;
goto err_put_device ;
}
2014-11-11 00:41:53 +03:00
}
2014-11-11 00:41:46 +03:00
2015-07-21 22:53:01 +03:00
ret = device_add ( & ac97 - > dev ) ;
if ( ret )
goto err_put_device ;
2015-11-11 15:12:51 +03:00
ret = snd_soc_ac97_init_gpio ( ac97 , codec ) ;
if ( ret )
goto err_put_device ;
2014-11-11 00:41:53 +03:00
return ac97 ;
2015-07-21 22:53:01 +03:00
err_put_device :
put_device ( & ac97 - > dev ) ;
return ERR_PTR ( ret ) ;
2014-11-11 00:41:46 +03:00
}
EXPORT_SYMBOL_GPL ( snd_soc_new_ac97_codec ) ;
/**
* snd_soc_free_ac97_codec - free AC97 codec device
2017-01-12 14:38:15 +03:00
* @ ac97 : snd_ac97 device to be freed
2014-11-11 00:41:46 +03:00
*
* Frees AC97 codec device resources .
*/
2014-11-11 00:41:53 +03:00
void snd_soc_free_ac97_codec ( struct snd_ac97 * ac97 )
2014-11-11 00:41:46 +03:00
{
2015-11-11 15:12:51 +03:00
snd_soc_ac97_free_gpio ( ac97 ) ;
2014-11-11 00:41:53 +03:00
device_del ( & ac97 - > dev ) ;
ac97 - > bus = NULL ;
put_device ( & ac97 - > dev ) ;
2014-11-11 00:41:46 +03:00
}
EXPORT_SYMBOL_GPL ( snd_soc_free_ac97_codec ) ;
static struct snd_ac97_reset_cfg snd_ac97_rst_cfg ;
static void snd_soc_ac97_warm_reset ( struct snd_ac97 * ac97 )
{
struct pinctrl * pctl = snd_ac97_rst_cfg . pctl ;
pinctrl_select_state ( pctl , snd_ac97_rst_cfg . pstate_warm_reset ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_sync , 1 ) ;
udelay ( 10 ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_sync , 0 ) ;
pinctrl_select_state ( pctl , snd_ac97_rst_cfg . pstate_run ) ;
msleep ( 2 ) ;
}
static void snd_soc_ac97_reset ( struct snd_ac97 * ac97 )
{
struct pinctrl * pctl = snd_ac97_rst_cfg . pctl ;
pinctrl_select_state ( pctl , snd_ac97_rst_cfg . pstate_reset ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_sync , 0 ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_sdata , 0 ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_reset , 0 ) ;
udelay ( 10 ) ;
gpio_direction_output ( snd_ac97_rst_cfg . gpio_reset , 1 ) ;
pinctrl_select_state ( pctl , snd_ac97_rst_cfg . pstate_run ) ;
msleep ( 2 ) ;
}
static int snd_soc_ac97_parse_pinctl ( struct device * dev ,
struct snd_ac97_reset_cfg * cfg )
{
struct pinctrl * p ;
struct pinctrl_state * state ;
int gpio ;
int ret ;
p = devm_pinctrl_get ( dev ) ;
if ( IS_ERR ( p ) ) {
dev_err ( dev , " Failed to get pinctrl \n " ) ;
return PTR_ERR ( p ) ;
}
cfg - > pctl = p ;
state = pinctrl_lookup_state ( p , " ac97-reset " ) ;
if ( IS_ERR ( state ) ) {
dev_err ( dev , " Can't find pinctrl state ac97-reset \n " ) ;
return PTR_ERR ( state ) ;
}
cfg - > pstate_reset = state ;
state = pinctrl_lookup_state ( p , " ac97-warm-reset " ) ;
if ( IS_ERR ( state ) ) {
dev_err ( dev , " Can't find pinctrl state ac97-warm-reset \n " ) ;
return PTR_ERR ( state ) ;
}
cfg - > pstate_warm_reset = state ;
state = pinctrl_lookup_state ( p , " ac97-running " ) ;
if ( IS_ERR ( state ) ) {
dev_err ( dev , " Can't find pinctrl state ac97-running \n " ) ;
return PTR_ERR ( state ) ;
}
cfg - > pstate_run = state ;
gpio = of_get_named_gpio ( dev - > of_node , " ac97-gpios " , 0 ) ;
if ( gpio < 0 ) {
dev_err ( dev , " Can't find ac97-sync gpio \n " ) ;
return gpio ;
}
ret = devm_gpio_request ( dev , gpio , " AC97 link sync " ) ;
if ( ret ) {
dev_err ( dev , " Failed requesting ac97-sync gpio \n " ) ;
return ret ;
}
cfg - > gpio_sync = gpio ;
gpio = of_get_named_gpio ( dev - > of_node , " ac97-gpios " , 1 ) ;
if ( gpio < 0 ) {
dev_err ( dev , " Can't find ac97-sdata gpio %d \n " , gpio ) ;
return gpio ;
}
ret = devm_gpio_request ( dev , gpio , " AC97 link sdata " ) ;
if ( ret ) {
dev_err ( dev , " Failed requesting ac97-sdata gpio \n " ) ;
return ret ;
}
cfg - > gpio_sdata = gpio ;
gpio = of_get_named_gpio ( dev - > of_node , " ac97-gpios " , 2 ) ;
if ( gpio < 0 ) {
dev_err ( dev , " Can't find ac97-reset gpio \n " ) ;
return gpio ;
}
ret = devm_gpio_request ( dev , gpio , " AC97 link reset " ) ;
if ( ret ) {
dev_err ( dev , " Failed requesting ac97-reset gpio \n " ) ;
return ret ;
}
cfg - > gpio_reset = gpio ;
return 0 ;
}
struct snd_ac97_bus_ops * soc_ac97_ops ;
EXPORT_SYMBOL_GPL ( soc_ac97_ops ) ;
int snd_soc_set_ac97_ops ( struct snd_ac97_bus_ops * ops )
{
if ( ops = = soc_ac97_ops )
return 0 ;
if ( soc_ac97_ops & & ops )
return - EBUSY ;
soc_ac97_ops = ops ;
2014-11-11 00:41:47 +03:00
soc_ac97_bus . ops = ops ;
2014-11-11 00:41:46 +03:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_soc_set_ac97_ops ) ;
/**
* snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
*
* This function sets the reset and warm_reset properties of ops and parses
* the device node of pdev to get pinctrl states and gpio numbers to use .
*/
int snd_soc_set_ac97_ops_of_reset ( struct snd_ac97_bus_ops * ops ,
struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct snd_ac97_reset_cfg cfg ;
int ret ;
ret = snd_soc_ac97_parse_pinctl ( dev , & cfg ) ;
if ( ret )
return ret ;
ret = snd_soc_set_ac97_ops ( ops ) ;
if ( ret )
return ret ;
ops - > warm_reset = snd_soc_ac97_warm_reset ;
ops - > reset = snd_soc_ac97_reset ;
snd_ac97_rst_cfg = cfg ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( snd_soc_set_ac97_ops_of_reset ) ;