Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
7d590e46
Commit
7d590e46
authored
Feb 04, 2015
by
Mark Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'asoc/topic/pcm512x' into asoc-next
parents
3223d9c6
9c7da1a5
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1149 additions
and
25 deletions
+1149
-25
Documentation/devicetree/bindings/sound/pcm512x.txt
Documentation/devicetree/bindings/sound/pcm512x.txt
+25
-3
include/sound/pcm.h
include/sound/pcm.h
+12
-0
sound/core/pcm_lib.c
sound/core/pcm_lib.c
+85
-0
sound/soc/codecs/pcm512x-i2c.c
sound/soc/codecs/pcm512x-i2c.c
+4
-0
sound/soc/codecs/pcm512x-spi.c
sound/soc/codecs/pcm512x-spi.c
+4
-0
sound/soc/codecs/pcm512x.c
sound/soc/codecs/pcm512x.c
+915
-17
sound/soc/codecs/pcm512x.h
sound/soc/codecs/pcm512x.h
+104
-5
No files found.
Documentation/devicetree/bindings/sound/pcm512x.txt
View file @
7d590e46
...
...
@@ -5,7 +5,8 @@ on the board).
Required properties:
- compatible : One of "ti,pcm5121" or "ti,pcm5122"
- compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141" or
"ti,pcm5142"
- reg : the I2C address of the device for I2C, the chip select
number for SPI.
...
...
@@ -16,9 +17,16 @@ Required properties:
Optional properties:
- clocks : A clock specifier for the clock connected as SCLK. If this
is absent the device will be configured to clock from BCLK.
is absent the device will be configured to clock from BCLK. If pll-in
and pll-out are specified in addition to a clock, the device is
configured to accept clock input on a specified gpio pin.
Example:
- pll-in, pll-out : gpio pins used to connect the pll using <1>
through <6>. The device will be configured for clock input on the
given pll-in pin and PLL output on the given pll-out pin. An
external connection from the pll-out pin to the SCLK pin is assumed.
Examples:
pcm5122: pcm5122@4c {
compatible = "ti,pcm5122";
...
...
@@ -28,3 +36,17 @@ Example:
DVDD-supply = <®_1v8>;
CPVDD-supply = <®_3v3>;
};
pcm5142: pcm5142@4c {
compatible = "ti,pcm5142";
reg = <0x4c>;
AVDD-supply = <®_3v3_analog>;
DVDD-supply = <®_1v8>;
CPVDD-supply = <®_3v3>;
clocks = <&sck>;
pll-in = <3>;
pll-out = <6>;
};
include/sound/pcm.h
View file @
7d590e46
...
...
@@ -275,6 +275,12 @@ struct snd_pcm_hw_constraint_list {
unsigned
int
mask
;
};
struct
snd_pcm_hw_constraint_ranges
{
unsigned
int
count
;
const
struct
snd_interval
*
ranges
;
unsigned
int
mask
;
};
struct
snd_pcm_hwptr_log
;
struct
snd_pcm_runtime
{
...
...
@@ -910,6 +916,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
const
struct
snd_interval
*
b
,
struct
snd_interval
*
c
);
int
snd_interval_list
(
struct
snd_interval
*
i
,
unsigned
int
count
,
const
unsigned
int
*
list
,
unsigned
int
mask
);
int
snd_interval_ranges
(
struct
snd_interval
*
i
,
unsigned
int
count
,
const
struct
snd_interval
*
list
,
unsigned
int
mask
);
int
snd_interval_ratnum
(
struct
snd_interval
*
i
,
unsigned
int
rats_count
,
struct
snd_ratnum
*
rats
,
unsigned
int
*
nump
,
unsigned
int
*
denp
);
...
...
@@ -934,6 +942,10 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
unsigned
int
cond
,
snd_pcm_hw_param_t
var
,
const
struct
snd_pcm_hw_constraint_list
*
l
);
int
snd_pcm_hw_constraint_ranges
(
struct
snd_pcm_runtime
*
runtime
,
unsigned
int
cond
,
snd_pcm_hw_param_t
var
,
const
struct
snd_pcm_hw_constraint_ranges
*
r
);
int
snd_pcm_hw_constraint_ratnums
(
struct
snd_pcm_runtime
*
runtime
,
unsigned
int
cond
,
snd_pcm_hw_param_t
var
,
...
...
sound/core/pcm_lib.c
View file @
7d590e46
...
...
@@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count,
EXPORT_SYMBOL
(
snd_interval_list
);
/**
* snd_interval_ranges - refine the interval value from the list of ranges
* @i: the interval value to refine
* @count: the number of elements in the list of ranges
* @ranges: the ranges list
* @mask: the bit-mask to evaluate
*
* Refines the interval value from the list of ranges.
* When mask is non-zero, only the elements corresponding to bit 1 are
* evaluated.
*
* Return: Positive if the value is changed, zero if it's not changed, or a
* negative error code.
*/
int
snd_interval_ranges
(
struct
snd_interval
*
i
,
unsigned
int
count
,
const
struct
snd_interval
*
ranges
,
unsigned
int
mask
)
{
unsigned
int
k
;
struct
snd_interval
range_union
;
struct
snd_interval
range
;
if
(
!
count
)
{
snd_interval_none
(
i
);
return
-
EINVAL
;
}
snd_interval_any
(
&
range_union
);
range_union
.
min
=
UINT_MAX
;
range_union
.
max
=
0
;
for
(
k
=
0
;
k
<
count
;
k
++
)
{
if
(
mask
&&
!
(
mask
&
(
1
<<
k
)))
continue
;
snd_interval_copy
(
&
range
,
&
ranges
[
k
]);
if
(
snd_interval_refine
(
&
range
,
i
)
<
0
)
continue
;
if
(
snd_interval_empty
(
&
range
))
continue
;
if
(
range
.
min
<
range_union
.
min
)
{
range_union
.
min
=
range
.
min
;
range_union
.
openmin
=
1
;
}
if
(
range
.
min
==
range_union
.
min
&&
!
range
.
openmin
)
range_union
.
openmin
=
0
;
if
(
range
.
max
>
range_union
.
max
)
{
range_union
.
max
=
range
.
max
;
range_union
.
openmax
=
1
;
}
if
(
range
.
max
==
range_union
.
max
&&
!
range
.
openmax
)
range_union
.
openmax
=
0
;
}
return
snd_interval_refine
(
i
,
&
range_union
);
}
EXPORT_SYMBOL
(
snd_interval_ranges
);
static
int
snd_interval_step
(
struct
snd_interval
*
i
,
unsigned
int
step
)
{
unsigned
int
n
;
...
...
@@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime,
EXPORT_SYMBOL
(
snd_pcm_hw_constraint_list
);
static
int
snd_pcm_hw_rule_ranges
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
)
{
struct
snd_pcm_hw_constraint_ranges
*
r
=
rule
->
private
;
return
snd_interval_ranges
(
hw_param_interval
(
params
,
rule
->
var
),
r
->
count
,
r
->
ranges
,
r
->
mask
);
}
/**
* snd_pcm_hw_constraint_ranges - apply list of range constraints to a parameter
* @runtime: PCM runtime instance
* @cond: condition bits
* @var: hw_params variable to apply the list of range constraints
* @r: ranges
*
* Apply the list of range constraints to an interval parameter.
*
* Return: Zero if successful, or a negative error code on failure.
*/
int
snd_pcm_hw_constraint_ranges
(
struct
snd_pcm_runtime
*
runtime
,
unsigned
int
cond
,
snd_pcm_hw_param_t
var
,
const
struct
snd_pcm_hw_constraint_ranges
*
r
)
{
return
snd_pcm_hw_rule_add
(
runtime
,
cond
,
var
,
snd_pcm_hw_rule_ranges
,
(
void
*
)
r
,
var
,
-
1
);
}
EXPORT_SYMBOL
(
snd_pcm_hw_constraint_ranges
);
static
int
snd_pcm_hw_rule_ratnums
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
)
{
...
...
sound/soc/codecs/pcm512x-i2c.c
View file @
7d590e46
...
...
@@ -46,6 +46,8 @@ static int pcm512x_i2c_remove(struct i2c_client *i2c)
static
const
struct
i2c_device_id
pcm512x_i2c_id
[]
=
{
{
"pcm5121"
,
},
{
"pcm5122"
,
},
{
"pcm5141"
,
},
{
"pcm5142"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
pcm512x_i2c_id
);
...
...
@@ -53,6 +55,8 @@ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
static
const
struct
of_device_id
pcm512x_of_match
[]
=
{
{
.
compatible
=
"ti,pcm5121"
,
},
{
.
compatible
=
"ti,pcm5122"
,
},
{
.
compatible
=
"ti,pcm5141"
,
},
{
.
compatible
=
"ti,pcm5142"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
pcm512x_of_match
);
...
...
sound/soc/codecs/pcm512x-spi.c
View file @
7d590e46
...
...
@@ -43,6 +43,8 @@ static int pcm512x_spi_remove(struct spi_device *spi)
static
const
struct
spi_device_id
pcm512x_spi_id
[]
=
{
{
"pcm5121"
,
},
{
"pcm5122"
,
},
{
"pcm5141"
,
},
{
"pcm5142"
,
},
{
},
};
MODULE_DEVICE_TABLE
(
spi
,
pcm512x_spi_id
);
...
...
@@ -50,6 +52,8 @@ MODULE_DEVICE_TABLE(spi, pcm512x_spi_id);
static
const
struct
of_device_id
pcm512x_of_match
[]
=
{
{
.
compatible
=
"ti,pcm5121"
,
},
{
.
compatible
=
"ti,pcm5122"
,
},
{
.
compatible
=
"ti,pcm5141"
,
},
{
.
compatible
=
"ti,pcm5142"
,
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
pcm512x_of_match
);
...
...
sound/soc/codecs/pcm512x.c
View file @
7d590e46
...
...
@@ -21,12 +21,19 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/gcd.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include "pcm512x.h"
#define DIV_ROUND_DOWN_ULL(ll, d) \
({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; })
#define DIV_ROUND_CLOSEST_ULL(ll, d) \
({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; })
#define PCM512x_NUM_SUPPLIES 3
static
const
char
*
const
pcm512x_supply_names
[
PCM512x_NUM_SUPPLIES
]
=
{
"AVDD"
,
...
...
@@ -39,6 +46,14 @@ struct pcm512x_priv {
struct
clk
*
sclk
;
struct
regulator_bulk_data
supplies
[
PCM512x_NUM_SUPPLIES
];
struct
notifier_block
supply_nb
[
PCM512x_NUM_SUPPLIES
];
int
fmt
;
int
pll_in
;
int
pll_out
;
int
pll_r
;
int
pll_j
;
int
pll_d
;
int
pll_p
;
unsigned
long
real_pll
;
};
/*
...
...
@@ -69,6 +84,7 @@ static const struct reg_default pcm512x_reg_defaults[] = {
{
PCM512x_MUTE
,
0x00
},
{
PCM512x_DSP
,
0x00
},
{
PCM512x_PLL_REF
,
0x00
},
{
PCM512x_DAC_REF
,
0x00
},
{
PCM512x_DAC_ROUTING
,
0x11
},
{
PCM512x_DSP_PROGRAM
,
0x01
},
{
PCM512x_CLKDET
,
0x00
},
...
...
@@ -87,6 +103,25 @@ static const struct reg_default pcm512x_reg_defaults[] = {
{
PCM512x_ANALOG_GAIN_BOOST
,
0x00
},
{
PCM512x_VCOM_CTRL_1
,
0x00
},
{
PCM512x_VCOM_CTRL_2
,
0x01
},
{
PCM512x_BCLK_LRCLK_CFG
,
0x00
},
{
PCM512x_MASTER_MODE
,
0x7c
},
{
PCM512x_GPIO_DACIN
,
0x00
},
{
PCM512x_GPIO_PLLIN
,
0x00
},
{
PCM512x_SYNCHRONIZE
,
0x10
},
{
PCM512x_PLL_COEFF_0
,
0x00
},
{
PCM512x_PLL_COEFF_1
,
0x00
},
{
PCM512x_PLL_COEFF_2
,
0x00
},
{
PCM512x_PLL_COEFF_3
,
0x00
},
{
PCM512x_PLL_COEFF_4
,
0x00
},
{
PCM512x_DSP_CLKDIV
,
0x00
},
{
PCM512x_DAC_CLKDIV
,
0x00
},
{
PCM512x_NCP_CLKDIV
,
0x00
},
{
PCM512x_OSR_CLKDIV
,
0x00
},
{
PCM512x_MASTER_CLKDIV_1
,
0x00
},
{
PCM512x_MASTER_CLKDIV_2
,
0x00
},
{
PCM512x_FS_SPEED_MODE
,
0x00
},
{
PCM512x_IDAC_1
,
0x01
},
{
PCM512x_IDAC_2
,
0x00
},
};
static
bool
pcm512x_readable
(
struct
device
*
dev
,
unsigned
int
reg
)
...
...
@@ -103,6 +138,10 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg)
case
PCM512x_DSP_GPIO_INPUT
:
case
PCM512x_MASTER_MODE
:
case
PCM512x_PLL_REF
:
case
PCM512x_DAC_REF
:
case
PCM512x_GPIO_DACIN
:
case
PCM512x_GPIO_PLLIN
:
case
PCM512x_SYNCHRONIZE
:
case
PCM512x_PLL_COEFF_0
:
case
PCM512x_PLL_COEFF_1
:
case
PCM512x_PLL_COEFF_2
:
...
...
@@ -143,6 +182,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg)
case
PCM512x_RATE_DET_2
:
case
PCM512x_RATE_DET_3
:
case
PCM512x_RATE_DET_4
:
case
PCM512x_CLOCK_STATUS
:
case
PCM512x_ANALOG_MUTE_DET
:
case
PCM512x_GPIN
:
case
PCM512x_DIGITAL_MUTE_DET
:
...
...
@@ -154,6 +194,8 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg)
case
PCM512x_VCOM_CTRL_1
:
case
PCM512x_VCOM_CTRL_2
:
case
PCM512x_CRAM_CTRL
:
case
PCM512x_FLEX_A
:
case
PCM512x_FLEX_B
:
return
true
;
default:
/* There are 256 raw register addresses */
...
...
@@ -170,6 +212,7 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg)
case
PCM512x_RATE_DET_2
:
case
PCM512x_RATE_DET_3
:
case
PCM512x_RATE_DET_4
:
case
PCM512x_CLOCK_STATUS
:
case
PCM512x_ANALOG_MUTE_DET
:
case
PCM512x_GPIN
:
case
PCM512x_DIGITAL_MUTE_DET
:
...
...
@@ -277,7 +320,7 @@ SOC_ENUM("Auto Mute Time Right", pcm512x_autom_r),
SOC_SINGLE
(
"Auto Mute Mono Switch"
,
PCM512x_DIGITAL_MUTE_3
,
PCM512x_ACTL_SHIFT
,
1
,
0
),
SOC_DOUBLE
(
"Auto Mute Switch"
,
PCM512x_DIGITAL_MUTE_3
,
PCM512x_AMLE_SHIFT
,
PCM512x_AM
LR
_SHIFT
,
1
,
0
),
PCM512x_AM
RE
_SHIFT
,
1
,
0
),
SOC_ENUM
(
"Volume Ramp Down Rate"
,
pcm512x_vndf
),
SOC_ENUM
(
"Volume Ramp Down Step"
,
pcm512x_vnds
),
...
...
@@ -303,6 +346,136 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = {
{
"OUTR"
,
NULL
,
"DACR"
},
};
static
const
u32
pcm512x_dai_rates
[]
=
{
8000
,
11025
,
16000
,
22050
,
32000
,
44100
,
48000
,
64000
,
88200
,
96000
,
176400
,
192000
,
384000
,
};
static
const
struct
snd_pcm_hw_constraint_list
constraints_slave
=
{
.
count
=
ARRAY_SIZE
(
pcm512x_dai_rates
),
.
list
=
pcm512x_dai_rates
,
};
static
int
pcm512x_hw_rule_rate
(
struct
snd_pcm_hw_params
*
params
,
struct
snd_pcm_hw_rule
*
rule
)
{
struct
snd_interval
ranges
[
2
];
int
frame_size
;
frame_size
=
snd_soc_params_to_frame_size
(
params
);
if
(
frame_size
<
0
)
return
frame_size
;
switch
(
frame_size
)
{
case
32
:
/* No hole when the frame size is 32. */
return
0
;
case
48
:
case
64
:
/* There is only one hole in the range of supported
* rates, but it moves with the frame size.
*/
memset
(
ranges
,
0
,
sizeof
(
ranges
));
ranges
[
0
].
min
=
8000
;
ranges
[
0
].
max
=
25000000
/
frame_size
/
2
;
ranges
[
1
].
min
=
DIV_ROUND_UP
(
16000000
,
frame_size
);
ranges
[
1
].
max
=
384000
;
break
;
default:
return
-
EINVAL
;
}
return
snd_interval_ranges
(
hw_param_interval
(
params
,
rule
->
var
),
ARRAY_SIZE
(
ranges
),
ranges
,
0
);
}
static
int
pcm512x_dai_startup_master
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
struct
device
*
dev
=
dai
->
dev
;
struct
snd_pcm_hw_constraint_ratnums
*
constraints_no_pll
;
struct
snd_ratnum
*
rats_no_pll
;
if
(
IS_ERR
(
pcm512x
->
sclk
))
{
dev_err
(
dev
,
"Need SCLK for master mode: %ld
\n
"
,
PTR_ERR
(
pcm512x
->
sclk
));
return
PTR_ERR
(
pcm512x
->
sclk
);
}
if
(
pcm512x
->
pll_out
)
return
snd_pcm_hw_rule_add
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
pcm512x_hw_rule_rate
,
NULL
,
SNDRV_PCM_HW_PARAM_FRAME_BITS
,
SNDRV_PCM_HW_PARAM_CHANNELS
,
-
1
);
constraints_no_pll
=
devm_kzalloc
(
dev
,
sizeof
(
*
constraints_no_pll
),
GFP_KERNEL
);
if
(
!
constraints_no_pll
)
return
-
ENOMEM
;
constraints_no_pll
->
nrats
=
1
;
rats_no_pll
=
devm_kzalloc
(
dev
,
sizeof
(
*
rats_no_pll
),
GFP_KERNEL
);
if
(
!
rats_no_pll
)
return
-
ENOMEM
;
constraints_no_pll
->
rats
=
rats_no_pll
;
rats_no_pll
->
num
=
clk_get_rate
(
pcm512x
->
sclk
)
/
64
;
rats_no_pll
->
den_min
=
1
;
rats_no_pll
->
den_max
=
128
;
rats_no_pll
->
den_step
=
1
;
return
snd_pcm_hw_constraint_ratnums
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
constraints_no_pll
);
}
static
int
pcm512x_dai_startup_slave
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
struct
device
*
dev
=
dai
->
dev
;
struct
regmap
*
regmap
=
pcm512x
->
regmap
;
if
(
IS_ERR
(
pcm512x
->
sclk
))
{
dev_info
(
dev
,
"No SCLK, using BCLK: %ld
\n
"
,
PTR_ERR
(
pcm512x
->
sclk
));
/* Disable reporting of missing SCLK as an error */
regmap_update_bits
(
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_IDCH
,
PCM512x_IDCH
);
/* Switch PLL input to BCLK */
regmap_update_bits
(
regmap
,
PCM512x_PLL_REF
,
PCM512x_SREF
,
PCM512x_SREF_BCK
);
}
return
snd_pcm_hw_constraint_list
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_RATE
,
&
constraints_slave
);
}
static
int
pcm512x_dai_startup
(
struct
snd_pcm_substream
*
substream
,
struct
snd_soc_dai
*
dai
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
switch
(
pcm512x
->
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBM_CFM
:
case
SND_SOC_DAIFMT_CBM_CFS
:
return
pcm512x_dai_startup_master
(
substream
,
dai
);
case
SND_SOC_DAIFMT_CBS_CFS
:
return
pcm512x_dai_startup_slave
(
substream
,
dai
);
default:
return
-
EINVAL
;
}
}
static
int
pcm512x_set_bias_level
(
struct
snd_soc_codec
*
codec
,
enum
snd_soc_bias_level
level
)
{
...
...
@@ -340,17 +513,717 @@ static int pcm512x_set_bias_level(struct snd_soc_codec *codec,
return
0
;
}
static
unsigned
long
pcm512x_find_sck
(
struct
snd_soc_dai
*
dai
,
unsigned
long
bclk_rate
)
{
struct
device
*
dev
=
dai
->
dev
;
unsigned
long
sck_rate
;
int
pow2
;
/* 64 MHz <= pll_rate <= 100 MHz, VREF mode */
/* 16 MHz <= sck_rate <= 25 MHz, VREF mode */
/* select sck_rate as a multiple of bclk_rate but still with
* 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
);
for
(;
pow2
;
pow2
>>=
1
)
{
sck_rate
=
rounddown
(
25000000
,
bclk_rate
*
pow2
);
if
(
sck_rate
>=
16000000
)
break
;
}
if
(
!
pow2
)
{
dev_err
(
dev
,
"Impossible to generate a suitable SCK
\n
"
);
return
0
;
}
dev_dbg
(
dev
,
"sck_rate %lu
\n
"
,
sck_rate
);
return
sck_rate
;
}
/* pll_rate = pllin_rate * R * J.D / P
* 1 <= R <= 16
* 1 <= J <= 63
* 0 <= D <= 9999
* 1 <= P <= 15
* 64 MHz <= pll_rate <= 100 MHz
* if D == 0
* 1 MHz <= pllin_rate / P <= 20 MHz
* else if D > 0
* 6.667 MHz <= pllin_rate / P <= 20 MHz
* 4 <= J <= 11
* R = 1
*/
static
int
pcm512x_find_pll_coeff
(
struct
snd_soc_dai
*
dai
,
unsigned
long
pllin_rate
,
unsigned
long
pll_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
common
;
int
R
,
J
,
D
,
P
;
unsigned
long
K
;
/* 10000 * J.D */
unsigned
long
num
;
unsigned
long
den
;
common
=
gcd
(
pll_rate
,
pllin_rate
);
dev_dbg
(
dev
,
"pll %lu pllin %lu common %lu
\n
"
,
pll_rate
,
pllin_rate
,
common
);
num
=
pll_rate
/
common
;
den
=
pllin_rate
/
common
;
/* 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
);
}
dev_dbg
(
dev
,
"num / den = %lu / %lu
\n
"
,
num
,
den
);
P
=
den
;
if
(
den
<=
15
&&
num
<=
16
*
63
&&
1000000
<=
pllin_rate
/
P
&&
pllin_rate
/
P
<=
20000000
)
{
/* Try the case with D = 0 */
D
=
0
;
/* factor 'num' into J and R, such that R <= 16 and J <= 63 */
for
(
R
=
16
;
R
;
R
--
)
{
if
(
num
%
R
)
continue
;
J
=
num
/
R
;
if
(
J
==
0
||
J
>
63
)
continue
;
dev_dbg
(
dev
,
"R * J / P = %d * %d / %d
\n
"
,
R
,
J
,
P
);
pcm512x
->
real_pll
=
pll_rate
;
goto
done
;
}
/* no luck */
}
R
=
1
;
if
(
num
>
0xffffffffUL
/
10000
)
goto
fallback
;
/* Try to find an exact pll_rate using the D > 0 case */
common
=
gcd
(
10000
*
num
,
den
);
num
=
10000
*
num
/
common
;
den
/=
common
;
dev_dbg
(
dev
,
"num %lu den %lu common %lu
\n
"
,
num
,
den
,
common
);
for
(
P
=
den
;
P
<=
15
;
P
++
)
{
if
(
pllin_rate
/
P
<
6667000
||
200000000
<
pllin_rate
/
P
)
continue
;
if
(
num
*
P
%
den
)
continue
;
K
=
num
*
P
/
den
;
/* J == 12 is ok if D == 0 */
if
(
K
<
40000
||
K
>
120000
)
continue
;
J
=
K
/
10000
;
D
=
K
%
10000
;
dev_dbg
(
dev
,
"J.D / P = %d.%04d / %d
\n
"
,
J
,
D
,
P
);
pcm512x
->
real_pll
=
pll_rate
;
goto
done
;
}
/* Fall back to an approximate pll_rate */
fallback:
/* find smallest possible P */
P
=
DIV_ROUND_UP
(
pllin_rate
,
20000000
);
if
(
!
P
)
P
=
1
;
else
if
(
P
>
15
)
{
dev_err
(
dev
,
"Need a slower clock as pll-input
\n
"
);
return
-
EINVAL
;
}
if
(
pllin_rate
/
P
<
6667000
)
{
dev_err
(
dev
,
"Need a faster clock as pll-input
\n
"
);
return
-
EINVAL
;
}
K
=
DIV_ROUND_CLOSEST_ULL
(
10000ULL
*
pll_rate
*
P
,
pllin_rate
);
if
(
K
<
40000
)
K
=
40000
;
/* J == 12 is ok if D == 0 */
if
(
K
>
120000
)
K
=
120000
;
J
=
K
/
10000
;
D
=
K
%
10000
;
dev_dbg
(
dev
,
"J.D / P ~ %d.%04d / %d
\n
"
,
J
,
D
,
P
);
pcm512x
->
real_pll
=
DIV_ROUND_DOWN_ULL
((
u64
)
K
*
pllin_rate
,
10000
*
P
);
done:
pcm512x
->
pll_r
=
R
;
pcm512x
->
pll_j
=
J
;
pcm512x
->
pll_d
=
D
;
pcm512x
->
pll_p
=
P
;
return
0
;
}
static
unsigned
long
pcm512x_pllin_dac_rate
(
struct
snd_soc_dai
*
dai
,
unsigned
long
osr_rate
,
unsigned
long
pllin_rate
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
long
dac_rate
;
if
(
!
pcm512x
->
pll_out
)
return
0
;
/* no PLL to bypass, force SCK as DAC input */
if
(
pllin_rate
%
osr_rate
)
return
0
;
/* futile, quit early */
/* run DAC no faster than 6144000 Hz */
for
(
dac_rate
=
rounddown
(
6144000
,
osr_rate
);
dac_rate
;
dac_rate
-=
osr_rate
)
{
if
(
pllin_rate
/
dac_rate
>
128
)
return
0
;
/* DAC divider would be too big */
if
(
!
(
pllin_rate
%
dac_rate
))
return
dac_rate
;
dac_rate
-=
osr_rate
;
}
return
0
;
}
static
int
pcm512x_set_dividers
(
struct
snd_soc_dai
*
dai
,
struct
snd_pcm_hw_params
*
params
)
{
struct
device
*
dev
=
dai
->
dev
;
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
unsigned
long
pllin_rate
=
0
;
unsigned
long
pll_rate
;
unsigned
long
sck_rate
;
unsigned
long
mck_rate
;
unsigned
long
bclk_rate
;
unsigned
long
sample_rate
;
unsigned
long
osr_rate
;
unsigned
long
dacsrc_rate
;
int
bclk_div
;
int
lrclk_div
;
int
dsp_div
;
int
dac_div
;
unsigned
long
dac_rate
;
int
ncp_div
;
int
osr_div
;
int
ret
;
int
idac
;
int
fssp
;
int
gpio
;
lrclk_div
=
snd_soc_params_to_frame_size
(
params
);
if
(
lrclk_div
==
0
)
{
dev_err
(
dev
,
"No LRCLK?
\n
"
);
return
-
EINVAL
;
}
if
(
!
pcm512x
->
pll_out
)
{
sck_rate
=
clk_get_rate
(
pcm512x
->
sclk
);
bclk_div
=
params
->
rate_den
*
64
/
lrclk_div
;
bclk_rate
=
DIV_ROUND_CLOSEST
(
sck_rate
,
bclk_div
);
mck_rate
=
sck_rate
;
}
else
{
ret
=
snd_soc_params_to_bclk
(
params
);
if
(
ret
<
0
)
{
dev_err
(
dev
,
"Failed to find suitable BCLK: %d
\n
"
,
ret
);
return
ret
;
}
if
(
ret
==
0
)
{
dev_err
(
dev
,
"No BCLK?
\n
"
);
return
-
EINVAL
;
}
bclk_rate
=
ret
;
pllin_rate
=
clk_get_rate
(
pcm512x
->
sclk
);
sck_rate
=
pcm512x_find_sck
(
dai
,
bclk_rate
);
if
(
!
sck_rate
)
return
-
EINVAL
;
pll_rate
=
4
*
sck_rate
;
ret
=
pcm512x_find_pll_coeff
(
dai
,
pllin_rate
,
pll_rate
);
if
(
ret
!=
0
)
return
ret
;
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_PLL_COEFF_0
,
pcm512x
->
pll_p
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write PLL P: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_PLL_COEFF_1
,
pcm512x
->
pll_j
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write PLL J: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_PLL_COEFF_2
,
pcm512x
->
pll_d
>>
8
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write PLL D msb: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_PLL_COEFF_3
,
pcm512x
->
pll_d
&
0xff
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write PLL D lsb: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_PLL_COEFF_4
,
pcm512x
->
pll_r
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write PLL R: %d
\n
"
,
ret
);
return
ret
;
}
mck_rate
=
pcm512x
->
real_pll
;
bclk_div
=
DIV_ROUND_CLOSEST
(
sck_rate
,
bclk_rate
);
}
if
(
bclk_div
>
128
)
{
dev_err
(
dev
,
"Failed to find BCLK divider
\n
"
);
return
-
EINVAL
;
}
/* the actual rate */
sample_rate
=
sck_rate
/
bclk_div
/
lrclk_div
;
osr_rate
=
16
*
sample_rate
;
/* run DSP no faster than 50 MHz */
dsp_div
=
mck_rate
>
50000000
?
2
:
1
;
dac_rate
=
pcm512x_pllin_dac_rate
(
dai
,
osr_rate
,
pllin_rate
);
if
(
dac_rate
)
{
/* the desired clock rate is "compatible" with the pll input
* clock, so use that clock as dac input instead of the pll
* output clock since the pll will introduce jitter and thus
* noise.
*/
dev_dbg
(
dev
,
"using pll input as dac input
\n
"
);
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_DAC_REF
,
PCM512x_SDAC
,
PCM512x_SDAC_GPIO
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set gpio as dacref: %d
\n
"
,
ret
);
return
ret
;
}
gpio
=
PCM512x_GREF_GPIO1
+
pcm512x
->
pll_in
-
1
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_GPIO_DACIN
,
PCM512x_GREF
,
gpio
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set gpio %d as dacin: %d
\n
"
,
pcm512x
->
pll_in
,
ret
);
return
ret
;
}
dacsrc_rate
=
pllin_rate
;
}
else
{
/* run DAC no faster than 6144000 Hz */
unsigned
long
dac_mul
=
6144000
/
osr_rate
;
unsigned
long
sck_mul
=
sck_rate
/
osr_rate
;
for
(;
dac_mul
;
dac_mul
--
)
{
if
(
!
(
sck_mul
%
dac_mul
))
break
;
}
if
(
!
dac_mul
)
{
dev_err
(
dev
,
"Failed to find DAC rate
\n
"
);
return
-
EINVAL
;
}
dac_rate
=
dac_mul
*
osr_rate
;
dev_dbg
(
dev
,
"dac_rate %lu sample_rate %lu
\n
"
,
dac_rate
,
sample_rate
);
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_DAC_REF
,
PCM512x_SDAC
,
PCM512x_SDAC_SCK
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set sck as dacref: %d
\n
"
,
ret
);
return
ret
;
}
dacsrc_rate
=
sck_rate
;
}
dac_div
=
DIV_ROUND_CLOSEST
(
dacsrc_rate
,
dac_rate
);
if
(
dac_div
>
128
)
{
dev_err
(
dev
,
"Failed to find DAC divider
\n
"
);
return
-
EINVAL
;
}
ncp_div
=
DIV_ROUND_CLOSEST
(
dacsrc_rate
/
dac_div
,
1536000
);
if
(
ncp_div
>
128
||
dacsrc_rate
/
dac_div
/
ncp_div
>
2048000
)
{
/* run NCP no faster than 2048000 Hz, but why? */
ncp_div
=
DIV_ROUND_UP
(
dacsrc_rate
/
dac_div
,
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
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write DSP divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_DAC_CLKDIV
,
dac_div
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write DAC divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_NCP_CLKDIV
,
ncp_div
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write NCP divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_OSR_CLKDIV
,
osr_div
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write OSR divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_MASTER_CLKDIV_1
,
bclk_div
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write BCLK divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_MASTER_CLKDIV_2
,
lrclk_div
-
1
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write LRCLK divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_IDAC_1
,
idac
>>
8
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write IDAC msb divider: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_IDAC_2
,
idac
&
0xff
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to write IDAC lsb divider: %d
\n
"
,
ret
);
return
ret
;
}
if
(
sample_rate
<=
48000
)
fssp
=
PCM512x_FSSP_48KHZ
;
else
if
(
sample_rate
<=
96000
)
fssp
=
PCM512x_FSSP_96KHZ
;
else
if
(
sample_rate
<=
192000
)
fssp
=
PCM512x_FSSP_192KHZ
;
else
fssp
=
PCM512x_FSSP_384KHZ
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_FS_SPEED_MODE
,
PCM512x_FSSP
,
fssp
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set fs speed: %d
\n
"
,
ret
);
return
ret
;
}
dev_dbg
(
codec
->
dev
,
"DSP divider %d
\n
"
,
dsp_div
);
dev_dbg
(
codec
->
dev
,
"DAC divider %d
\n
"
,
dac_div
);
dev_dbg
(
codec
->
dev
,
"NCP divider %d
\n
"
,
ncp_div
);
dev_dbg
(
codec
->
dev
,
"OSR divider %d
\n
"
,
osr_div
);
dev_dbg
(
codec
->
dev
,
"BCK divider %d
\n
"
,
bclk_div
);
dev_dbg
(
codec
->
dev
,
"LRCK divider %d
\n
"
,
lrclk_div
);
dev_dbg
(
codec
->
dev
,
"IDAC %d
\n
"
,
idac
);
dev_dbg
(
codec
->
dev
,
"1<<FSSP %d
\n
"
,
1
<<
fssp
);
return
0
;
}
static
int
pcm512x_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
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
int
alen
;
int
gpio
;
int
clock_output
;
int
master_mode
;
int
ret
;
dev_dbg
(
codec
->
dev
,
"hw_params %u Hz, %u channels
\n
"
,
params_rate
(
params
),
params_channels
(
params
));
switch
(
snd_pcm_format_width
(
params_format
(
params
)))
{
case
16
:
alen
=
PCM512x_ALEN_16
;
break
;
case
20
:
alen
=
PCM512x_ALEN_20
;
break
;
case
24
:
alen
=
PCM512x_ALEN_24
;
break
;
case
32
:
alen
=
PCM512x_ALEN_32
;
break
;
default:
dev_err
(
codec
->
dev
,
"Bad frame size: %d
\n
"
,
snd_pcm_format_width
(
params_format
(
params
)));
return
-
EINVAL
;
}
switch
(
pcm512x
->
fmt
&
SND_SOC_DAIFMT_MASTER_MASK
)
{
case
SND_SOC_DAIFMT_CBS_CFS
:
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_BCLK_LRCLK_CFG
,
PCM512x_BCKP
|
PCM512x_BCKO
|
PCM512x_LRKO
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable slave mode: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_DCAS
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable clock divider autoset: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
case
SND_SOC_DAIFMT_CBM_CFM
:
clock_output
=
PCM512x_BCKO
|
PCM512x_LRKO
;
master_mode
=
PCM512x_RLRK
|
PCM512x_RBCK
;
break
;
case
SND_SOC_DAIFMT_CBM_CFS
:
clock_output
=
PCM512x_BCKO
;
master_mode
=
PCM512x_RBCK
;
break
;
default:
return
-
EINVAL
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_I2S_1
,
PCM512x_ALEN
,
alen
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set frame size: %d
\n
"
,
ret
);
return
ret
;
}
if
(
pcm512x
->
pll_out
)
{
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_FLEX_A
,
0x11
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set FLEX_A: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_write
(
pcm512x
->
regmap
,
PCM512x_FLEX_B
,
0xff
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set FLEX_B: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_IDFS
|
PCM512x_IDBK
|
PCM512x_IDSK
|
PCM512x_IDCH
|
PCM512x_IDCM
|
PCM512x_DCAS
|
PCM512x_IPLK
,
PCM512x_IDFS
|
PCM512x_IDBK
|
PCM512x_IDSK
|
PCM512x_IDCH
|
PCM512x_DCAS
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to ignore auto-clock failures: %d
\n
"
,
ret
);
return
ret
;
}
}
else
{
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_IDFS
|
PCM512x_IDBK
|
PCM512x_IDSK
|
PCM512x_IDCH
|
PCM512x_IDCM
|
PCM512x_DCAS
|
PCM512x_IPLK
,
PCM512x_IDFS
|
PCM512x_IDBK
|
PCM512x_IDSK
|
PCM512x_IDCH
|
PCM512x_DCAS
|
PCM512x_IPLK
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to ignore auto-clock failures: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_PLL_EN
,
PCM512x_PLLE
,
0
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to disable pll: %d
\n
"
,
ret
);
return
ret
;
}
}
ret
=
pcm512x_set_dividers
(
dai
,
params
);
if
(
ret
!=
0
)
return
ret
;
if
(
pcm512x
->
pll_out
)
{
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_PLL_REF
,
PCM512x_SREF
,
PCM512x_SREF_GPIO
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set gpio as pllref: %d
\n
"
,
ret
);
return
ret
;
}
gpio
=
PCM512x_GREF_GPIO1
+
pcm512x
->
pll_in
-
1
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_GPIO_PLLIN
,
PCM512x_GREF
,
gpio
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to set gpio %d as pllin: %d
\n
"
,
pcm512x
->
pll_in
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_PLL_EN
,
PCM512x_PLLE
,
PCM512x_PLLE
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable pll: %d
\n
"
,
ret
);
return
ret
;
}
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_BCLK_LRCLK_CFG
,
PCM512x_BCKP
|
PCM512x_BCKO
|
PCM512x_LRKO
,
clock_output
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable clock output: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_MASTER_MODE
,
PCM512x_RLRK
|
PCM512x_RBCK
,
master_mode
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable master mode: %d
\n
"
,
ret
);
return
ret
;
}
if
(
pcm512x
->
pll_out
)
{
gpio
=
PCM512x_G1OE
<<
(
pcm512x
->
pll_out
-
1
);
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_GPIO_EN
,
gpio
,
gpio
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable gpio %d: %d
\n
"
,
pcm512x
->
pll_out
,
ret
);
return
ret
;
}
gpio
=
PCM512x_GPIO_OUTPUT_1
+
pcm512x
->
pll_out
-
1
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
gpio
,
PCM512x_GxSL
,
PCM512x_GxSL_PLLCK
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to output pll on %d: %d
\n
"
,
ret
,
pcm512x
->
pll_out
);
return
ret
;
}
gpio
=
PCM512x_G1OE
<<
(
4
-
1
);
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_GPIO_EN
,
gpio
,
gpio
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to enable gpio %d: %d
\n
"
,
4
,
ret
);
return
ret
;
}
gpio
=
PCM512x_GPIO_OUTPUT_1
+
4
-
1
;
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
gpio
,
PCM512x_GxSL
,
PCM512x_GxSL_PLLLK
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to output pll lock on %d: %d
\n
"
,
ret
,
4
);
return
ret
;
}
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_SYNCHRONIZE
,
PCM512x_RQSY
,
PCM512x_RQSY_HALT
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to halt clocks: %d
\n
"
,
ret
);
return
ret
;
}
ret
=
regmap_update_bits
(
pcm512x
->
regmap
,
PCM512x_SYNCHRONIZE
,
PCM512x_RQSY
,
PCM512x_RQSY_RESUME
);
if
(
ret
!=
0
)
{
dev_err
(
codec
->
dev
,
"Failed to resume clocks: %d
\n
"
,
ret
);
return
ret
;
}
return
0
;
}
static
int
pcm512x_set_fmt
(
struct
snd_soc_dai
*
dai
,
unsigned
int
fmt
)
{
struct
snd_soc_codec
*
codec
=
dai
->
codec
;
struct
pcm512x_priv
*
pcm512x
=
snd_soc_codec_get_drvdata
(
codec
);
pcm512x
->
fmt
=
fmt
;
return
0
;
}
static
const
struct
snd_soc_dai_ops
pcm512x_dai_ops
=
{
.
startup
=
pcm512x_dai_startup
,
.
hw_params
=
pcm512x_hw_params
,
.
set_fmt
=
pcm512x_set_fmt
,
};
static
struct
snd_soc_dai_driver
pcm512x_dai
=
{
.
name
=
"pcm512x-hifi"
,
.
playback
=
{
.
stream_name
=
"Playback"
,
.
channels_min
=
2
,
.
channels_max
=
2
,
.
rates
=
SNDRV_PCM_RATE_8000_192000
,
.
rates
=
SNDRV_PCM_RATE_CONTINUOUS
,
.
rate_min
=
8000
,
.
rate_max
=
384000
,
.
formats
=
SNDRV_PCM_FMTBIT_S16_LE
|
SNDRV_PCM_FMTBIT_S24_LE
|
SNDRV_PCM_FMTBIT_S32_LE
},
.
ops
=
&
pcm512x_dai_ops
,
};
static
struct
snd_soc_codec_driver
pcm512x_codec_driver
=
{
...
...
@@ -448,21 +1321,9 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
}
pcm512x
->
sclk
=
devm_clk_get
(
dev
,
NULL
);
if
(
IS_ERR
(
pcm512x
->
sclk
))
{
if
(
PTR_ERR
(
pcm512x
->
sclk
)
==
-
EPROBE_DEFER
)
return
-
EPROBE_DEFER
;
dev_info
(
dev
,
"No SCLK, using BCLK: %ld
\n
"
,
PTR_ERR
(
pcm512x
->
sclk
));
/* Disable reporting of missing SCLK as an error */
regmap_update_bits
(
regmap
,
PCM512x_ERROR_DETECT
,
PCM512x_IDCH
,
PCM512x_IDCH
);
/* Switch PLL input to BCLK */
regmap_update_bits
(
regmap
,
PCM512x_PLL_REF
,
PCM512x_SREF
,
PCM512x_SREF
);
}
else
{
if
(
!
IS_ERR
(
pcm512x
->
sclk
))
{
ret
=
clk_prepare_enable
(
pcm512x
->
sclk
);
if
(
ret
!=
0
)
{
dev_err
(
dev
,
"Failed to enable SCLK: %d
\n
"
,
ret
);
...
...
@@ -483,6 +1344,43 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap)
pm_runtime_enable
(
dev
);
pm_runtime_idle
(
dev
);
#ifdef CONFIG_OF
if
(
dev
->
of_node
)
{
const
struct
device_node
*
np
=
dev
->
of_node
;
u32
val
;
if
(
of_property_read_u32
(
np
,
"pll-in"
,
&
val
)
>=
0
)
{
if
(
val
>
6
)
{
dev_err
(
dev
,
"Invalid pll-in
\n
"
);
ret
=
-
EINVAL
;
goto
err_clk
;
}
pcm512x
->
pll_in
=
val
;
}
if
(
of_property_read_u32
(
np
,
"pll-out"
,
&
val
)
>=
0
)
{
if
(
val
>
6
)
{
dev_err
(
dev
,
"Invalid pll-out
\n
"
);
ret
=
-
EINVAL
;
goto
err_clk
;
}
pcm512x
->
pll_out
=
val
;
}
if
(
!
pcm512x
->
pll_in
!=
!
pcm512x
->
pll_out
)
{
dev_err
(
dev
,
"Error: both pll-in and pll-out, or none
\n
"
);
ret
=
-
EINVAL
;
goto
err_clk
;
}
if
(
pcm512x
->
pll_in
&&
pcm512x
->
pll_in
==
pcm512x
->
pll_out
)
{
dev_err
(
dev
,
"Error: pll-in == pll-out
\n
"
);
ret
=
-
EINVAL
;
goto
err_clk
;
}
}
#endif
ret
=
snd_soc_register_codec
(
dev
,
&
pcm512x_codec_driver
,
&
pcm512x_dai
,
1
);
if
(
ret
!=
0
)
{
...
...
sound/soc/codecs/pcm512x.h
View file @
7d590e46
...
...
@@ -37,6 +37,10 @@
#define PCM512x_DSP_GPIO_INPUT (PCM512x_PAGE_BASE(0) + 10)
#define PCM512x_MASTER_MODE (PCM512x_PAGE_BASE(0) + 12)
#define PCM512x_PLL_REF (PCM512x_PAGE_BASE(0) + 13)
#define PCM512x_DAC_REF (PCM512x_PAGE_BASE(0) + 14)
#define PCM512x_GPIO_DACIN (PCM512x_PAGE_BASE(0) + 16)
#define PCM512x_GPIO_PLLIN (PCM512x_PAGE_BASE(0) + 18)
#define PCM512x_SYNCHRONIZE (PCM512x_PAGE_BASE(0) + 19)
#define PCM512x_PLL_COEFF_0 (PCM512x_PAGE_BASE(0) + 20)
#define PCM512x_PLL_COEFF_1 (PCM512x_PAGE_BASE(0) + 21)
#define PCM512x_PLL_COEFF_2 (PCM512x_PAGE_BASE(0) + 22)
...
...
@@ -77,6 +81,7 @@
#define PCM512x_RATE_DET_2 (PCM512x_PAGE_BASE(0) + 92)
#define PCM512x_RATE_DET_3 (PCM512x_PAGE_BASE(0) + 93)
#define PCM512x_RATE_DET_4 (PCM512x_PAGE_BASE(0) + 94)
#define PCM512x_CLOCK_STATUS (PCM512x_PAGE_BASE(0) + 95)
#define PCM512x_ANALOG_MUTE_DET (PCM512x_PAGE_BASE(0) + 108)
#define PCM512x_GPIN (PCM512x_PAGE_BASE(0) + 119)
#define PCM512x_DIGITAL_MUTE_DET (PCM512x_PAGE_BASE(0) + 120)
...
...
@@ -91,7 +96,10 @@
#define PCM512x_CRAM_CTRL (PCM512x_PAGE_BASE(44) + 1)
#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(44) + 1)
#define PCM512x_FLEX_A (PCM512x_PAGE_BASE(253) + 63)
#define PCM512x_FLEX_B (PCM512x_PAGE_BASE(253) + 64)
#define PCM512x_MAX_REGISTER (PCM512x_PAGE_BASE(253) + 64)
/* Page 0, Register 1 - reset */
#define PCM512x_RSTR (1 << 0)
...
...
@@ -108,8 +116,8 @@
#define PCM512x_RQML_SHIFT 4
/* Page 0, Register 4 - PLL */
#define PCM512x_PL
C
E (1 << 0)
#define PCM512x_
RLC
E_SHIFT 0
#define PCM512x_PL
L
E (1 << 0)
#define PCM512x_
PLL
E_SHIFT 0
#define PCM512x_PLCK (1 << 4)
#define PCM512x_PLCK_SHIFT 4
...
...
@@ -119,8 +127,66 @@
#define PCM512x_DEMP (1 << 4)
#define PCM512x_DEMP_SHIFT 4
/* Page 0, Register 8 - GPIO output enable */
#define PCM512x_G1OE (1 << 0)
#define PCM512x_G2OE (1 << 1)
#define PCM512x_G3OE (1 << 2)
#define PCM512x_G4OE (1 << 3)
#define PCM512x_G5OE (1 << 4)
#define PCM512x_G6OE (1 << 5)
/* Page 0, Register 9 - BCK, LRCLK configuration */
#define PCM512x_LRKO (1 << 0)
#define PCM512x_LRKO_SHIFT 0
#define PCM512x_BCKO (1 << 4)
#define PCM512x_BCKO_SHIFT 4
#define PCM512x_BCKP (1 << 5)
#define PCM512x_BCKP_SHIFT 5
/* Page 0, Register 12 - Master mode BCK, LRCLK reset */
#define PCM512x_RLRK (1 << 0)
#define PCM512x_RLRK_SHIFT 0
#define PCM512x_RBCK (1 << 1)
#define PCM512x_RBCK_SHIFT 1
/* Page 0, Register 13 - PLL reference */
#define PCM512x_SREF (1 << 4)
#define PCM512x_SREF (7 << 4)
#define PCM512x_SREF_SHIFT 4
#define PCM512x_SREF_SCK (0 << 4)
#define PCM512x_SREF_BCK (1 << 4)
#define PCM512x_SREF_GPIO (3 << 4)
/* Page 0, Register 14 - DAC reference */
#define PCM512x_SDAC (7 << 4)
#define PCM512x_SDAC_SHIFT 4
#define PCM512x_SDAC_MCK (0 << 4)
#define PCM512x_SDAC_PLL (1 << 4)
#define PCM512x_SDAC_SCK (3 << 4)
#define PCM512x_SDAC_BCK (4 << 4)
#define PCM512x_SDAC_GPIO (5 << 4)
/* Page 0, Register 16, 18 - GPIO source for DAC, PLL */
#define PCM512x_GREF (7 << 0)
#define PCM512x_GREF_SHIFT 0
#define PCM512x_GREF_GPIO1 (0 << 0)
#define PCM512x_GREF_GPIO2 (1 << 0)
#define PCM512x_GREF_GPIO3 (2 << 0)
#define PCM512x_GREF_GPIO4 (3 << 0)
#define PCM512x_GREF_GPIO5 (4 << 0)
#define PCM512x_GREF_GPIO6 (5 << 0)
/* Page 0, Register 19 - synchronize */
#define PCM512x_RQSY (1 << 0)
#define PCM512x_RQSY_RESUME (0 << 0)
#define PCM512x_RQSY_HALT (1 << 0)
/* Page 0, Register 34 - fs speed mode */
#define PCM512x_FSSP (3 << 0)
#define PCM512x_FSSP_SHIFT 0
#define PCM512x_FSSP_48KHZ (0 << 0)
#define PCM512x_FSSP_96KHZ (1 << 0)
#define PCM512x_FSSP_192KHZ (2 << 0)
#define PCM512x_FSSP_384KHZ (3 << 0)
/* Page 0, Register 37 - Error detection */
#define PCM512x_IPLK (1 << 0)
...
...
@@ -131,6 +197,20 @@
#define PCM512x_IDBK (1 << 5)
#define PCM512x_IDFS (1 << 6)
/* Page 0, Register 40 - I2S configuration */
#define PCM512x_ALEN (3 << 0)
#define PCM512x_ALEN_SHIFT 0
#define PCM512x_ALEN_16 (0 << 0)
#define PCM512x_ALEN_20 (1 << 0)
#define PCM512x_ALEN_24 (2 << 0)
#define PCM512x_ALEN_32 (3 << 0)
#define PCM512x_AFMT (3 << 4)
#define PCM512x_AFMT_SHIFT 4
#define PCM512x_AFMT_I2S (0 << 4)
#define PCM512x_AFMT_DSP (1 << 4)
#define PCM512x_AFMT_RTJ (2 << 4)
#define PCM512x_AFMT_LTJ (3 << 4)
/* Page 0, Register 42 - DAC routing */
#define PCM512x_AUPR_SHIFT 0
#define PCM512x_AUPL_SHIFT 4
...
...
@@ -152,7 +232,26 @@
/* Page 0, Register 65 - Digital mute enables */
#define PCM512x_ACTL_SHIFT 2
#define PCM512x_AMLE_SHIFT 1
#define PCM512x_AMLR_SHIFT 0
#define PCM512x_AMRE_SHIFT 0
/* Page 0, Register 80-85, GPIO output selection */
#define PCM512x_GxSL (31 << 0)
#define PCM512x_GxSL_SHIFT 0
#define PCM512x_GxSL_OFF (0 << 0)
#define PCM512x_GxSL_DSP (1 << 0)
#define PCM512x_GxSL_REG (2 << 0)
#define PCM512x_GxSL_AMUTB (3 << 0)
#define PCM512x_GxSL_AMUTL (4 << 0)
#define PCM512x_GxSL_AMUTR (5 << 0)
#define PCM512x_GxSL_CLKI (6 << 0)
#define PCM512x_GxSL_SDOUT (7 << 0)
#define PCM512x_GxSL_ANMUL (8 << 0)
#define PCM512x_GxSL_ANMUR (9 << 0)
#define PCM512x_GxSL_PLLLK (10 << 0)
#define PCM512x_GxSL_CPCLK (11 << 0)
#define PCM512x_GxSL_UV0_7 (14 << 0)
#define PCM512x_GxSL_UV0_3 (15 << 0)
#define PCM512x_GxSL_PLLCK (16 << 0)
/* Page 1, Register 2 - analog volume control */
#define PCM512x_RAGN_SHIFT 0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment