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
Kirill Smelkov
linux
Commits
a9c3c7e0
Commit
a9c3c7e0
authored
Dec 25, 2008
by
Takashi Iwai
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/pcxhr-update' into to-push
parents
cc491085
ade9b2fb
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
1995 additions
and
521 deletions
+1995
-521
sound/pci/pcxhr/Makefile
sound/pci/pcxhr/Makefile
+1
-1
sound/pci/pcxhr/pcxhr.c
sound/pci/pcxhr/pcxhr.c
+382
-170
sound/pci/pcxhr/pcxhr.h
sound/pci/pcxhr/pcxhr.h
+51
-25
sound/pci/pcxhr/pcxhr_core.c
sound/pci/pcxhr/pcxhr_core.c
+186
-105
sound/pci/pcxhr/pcxhr_core.h
sound/pci/pcxhr/pcxhr_core.h
+4
-1
sound/pci/pcxhr/pcxhr_hwdep.c
sound/pci/pcxhr/pcxhr_hwdep.c
+113
-45
sound/pci/pcxhr/pcxhr_mix22.c
sound/pci/pcxhr/pcxhr_mix22.c
+820
-0
sound/pci/pcxhr/pcxhr_mix22.h
sound/pci/pcxhr/pcxhr_mix22.h
+56
-0
sound/pci/pcxhr/pcxhr_mixer.c
sound/pci/pcxhr/pcxhr_mixer.c
+382
-174
No files found.
sound/pci/pcxhr/Makefile
View file @
a9c3c7e0
snd-pcxhr-objs
:=
pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o
snd-pcxhr-objs
:=
pcxhr.o pcxhr_hwdep.o pcxhr_mixer.o pcxhr_core.o
pcxhr_mix22.o
obj-$(CONFIG_SND_PCXHR)
+=
snd-pcxhr.o
sound/pci/pcxhr/pcxhr.c
View file @
a9c3c7e0
...
...
@@ -40,18 +40,20 @@
#include "pcxhr_mixer.h"
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
#include "pcxhr_mix22.h"
#define DRIVER_NAME "pcxhr"
MODULE_AUTHOR
(
"Markus Bollinger <bollinger@digigram.com>"
);
MODULE_AUTHOR
(
"Markus Bollinger <bollinger@digigram.com>, "
"Marc Titinger <titinger@digigram.com>"
);
MODULE_DESCRIPTION
(
"Digigram "
DRIVER_NAME
" "
PCXHR_DRIVER_VERSION_STRING
);
MODULE_LICENSE
(
"GPL"
);
MODULE_SUPPORTED_DEVICE
(
"{{Digigram,"
DRIVER_NAME
"}}"
);
static
int
index
[
SNDRV_CARDS
]
=
SNDRV_DEFAULT_IDX
;
/* Index 0-MAX */
static
char
*
id
[
SNDRV_CARDS
]
=
SNDRV_DEFAULT_STR
;
/* ID for this card */
static
int
enable
[
SNDRV_CARDS
]
=
SNDRV_DEFAULT_ENABLE_PNP
;
/* Enable this card */
static
int
mono
[
SNDRV_CARDS
];
/* capture in
mono only */
static
int
enable
[
SNDRV_CARDS
]
=
SNDRV_DEFAULT_ENABLE_PNP
;
/* Enable this card */
static
int
mono
[
SNDRV_CARDS
];
/* capture
mono only */
module_param_array
(
index
,
int
,
NULL
,
0444
);
MODULE_PARM_DESC
(
index
,
"Index value for Digigram "
DRIVER_NAME
" soundcard"
);
...
...
@@ -67,18 +69,58 @@ enum {
PCI_ID_PCX882HR
,
PCI_ID_VX881HR
,
PCI_ID_PCX881HR
,
PCI_ID_VX882E
,
PCI_ID_PCX882E
,
PCI_ID_VX881E
,
PCI_ID_PCX881E
,
PCI_ID_VX1222HR
,
PCI_ID_PCX1222HR
,
PCI_ID_VX1221HR
,
PCI_ID_PCX1221HR
,
PCI_ID_VX1222E
,
PCI_ID_PCX1222E
,
PCI_ID_VX1221E
,
PCI_ID_PCX1221E
,
PCI_ID_VX222HR
,
PCI_ID_VX222E
,
PCI_ID_PCX22HR
,
PCI_ID_PCX22E
,
PCI_ID_VX222HRMIC
,
PCI_ID_VX222E_MIC
,
PCI_ID_PCX924HR
,
PCI_ID_PCX924E
,
PCI_ID_PCX924HRMIC
,
PCI_ID_PCX924E_MIC
,
PCI_ID_LAST
};
static
struct
pci_device_id
pcxhr_ids
[]
=
{
{
0x10b5
,
0x9656
,
0x1369
,
0xb001
,
0
,
0
,
PCI_ID_VX882HR
,
},
/* VX882HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb101
,
0
,
0
,
PCI_ID_PCX882HR
,
},
/* PCX882HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb201
,
0
,
0
,
PCI_ID_VX881HR
,
},
/* VX881HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb301
,
0
,
0
,
PCI_ID_PCX881HR
,
},
/* PCX881HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb501
,
0
,
0
,
PCI_ID_PCX1222HR
,
},
/* PCX1222HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb701
,
0
,
0
,
PCI_ID_PCX1221HR
,
},
/* PCX1221HR */
{
0x10b5
,
0x9656
,
0x1369
,
0xb001
,
0
,
0
,
PCI_ID_VX882HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb101
,
0
,
0
,
PCI_ID_PCX882HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb201
,
0
,
0
,
PCI_ID_VX881HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb301
,
0
,
0
,
PCI_ID_PCX881HR
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb021
,
0
,
0
,
PCI_ID_VX882E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb121
,
0
,
0
,
PCI_ID_PCX882E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb221
,
0
,
0
,
PCI_ID_VX881E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb321
,
0
,
0
,
PCI_ID_PCX881E
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb401
,
0
,
0
,
PCI_ID_VX1222HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb501
,
0
,
0
,
PCI_ID_PCX1222HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb601
,
0
,
0
,
PCI_ID_VX1221HR
,
},
{
0x10b5
,
0x9656
,
0x1369
,
0xb701
,
0
,
0
,
PCI_ID_PCX1221HR
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb421
,
0
,
0
,
PCI_ID_VX1222E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb521
,
0
,
0
,
PCI_ID_PCX1222E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb621
,
0
,
0
,
PCI_ID_VX1221E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xb721
,
0
,
0
,
PCI_ID_PCX1221E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xba01
,
0
,
0
,
PCI_ID_VX222HR
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xba21
,
0
,
0
,
PCI_ID_VX222E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbd01
,
0
,
0
,
PCI_ID_PCX22HR
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbd21
,
0
,
0
,
PCI_ID_PCX22E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbc01
,
0
,
0
,
PCI_ID_VX222HRMIC
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbc21
,
0
,
0
,
PCI_ID_VX222E_MIC
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbb01
,
0
,
0
,
PCI_ID_PCX924HR
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbb21
,
0
,
0
,
PCI_ID_PCX924E
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbf01
,
0
,
0
,
PCI_ID_PCX924HRMIC
,
},
{
0x10b5
,
0x9056
,
0x1369
,
0xbf21
,
0
,
0
,
PCI_ID_PCX924E_MIC
,
},
{
0
,
}
};
...
...
@@ -88,27 +130,55 @@ struct board_parameters {
char
*
board_name
;
short
playback_chips
;
short
capture_chips
;
short
fw_file_set
;
short
firmware_num
;
};
static
struct
board_parameters
pcxhr_board_params
[]
=
{
[
PCI_ID_VX882HR
]
=
{
"VX882HR"
,
4
,
4
,
41
,
},
[
PCI_ID_PCX882HR
]
=
{
"PCX882HR"
,
4
,
4
,
41
,
},
[
PCI_ID_VX881HR
]
=
{
"VX881HR"
,
4
,
4
,
41
,
},
[
PCI_ID_PCX881HR
]
=
{
"PCX881HR"
,
4
,
4
,
41
,
},
[
PCI_ID_PCX1222HR
]
=
{
"PCX1222HR"
,
6
,
1
,
42
,
},
[
PCI_ID_PCX1221HR
]
=
{
"PCX1221HR"
,
6
,
1
,
42
,
},
[
PCI_ID_VX882HR
]
=
{
"VX882HR"
,
4
,
4
,
0
,
41
},
[
PCI_ID_PCX882HR
]
=
{
"PCX882HR"
,
4
,
4
,
0
,
41
},
[
PCI_ID_VX881HR
]
=
{
"VX881HR"
,
4
,
4
,
0
,
41
},
[
PCI_ID_PCX881HR
]
=
{
"PCX881HR"
,
4
,
4
,
0
,
41
},
[
PCI_ID_VX882E
]
=
{
"VX882e"
,
4
,
4
,
1
,
41
},
[
PCI_ID_PCX882E
]
=
{
"PCX882e"
,
4
,
4
,
1
,
41
},
[
PCI_ID_VX881E
]
=
{
"VX881e"
,
4
,
4
,
1
,
41
},
[
PCI_ID_PCX881E
]
=
{
"PCX881e"
,
4
,
4
,
1
,
41
},
[
PCI_ID_VX1222HR
]
=
{
"VX1222HR"
,
6
,
1
,
2
,
42
},
[
PCI_ID_PCX1222HR
]
=
{
"PCX1222HR"
,
6
,
1
,
2
,
42
},
[
PCI_ID_VX1221HR
]
=
{
"VX1221HR"
,
6
,
1
,
2
,
42
},
[
PCI_ID_PCX1221HR
]
=
{
"PCX1221HR"
,
6
,
1
,
2
,
42
},
[
PCI_ID_VX1222E
]
=
{
"VX1222e"
,
6
,
1
,
3
,
42
},
[
PCI_ID_PCX1222E
]
=
{
"PCX1222e"
,
6
,
1
,
3
,
42
},
[
PCI_ID_VX1221E
]
=
{
"VX1221e"
,
6
,
1
,
3
,
42
},
[
PCI_ID_PCX1221E
]
=
{
"PCX1221e"
,
6
,
1
,
3
,
42
},
[
PCI_ID_VX222HR
]
=
{
"VX222HR"
,
1
,
1
,
4
,
44
},
[
PCI_ID_VX222E
]
=
{
"VX222e"
,
1
,
1
,
4
,
44
},
[
PCI_ID_PCX22HR
]
=
{
"PCX22HR"
,
1
,
0
,
4
,
44
},
[
PCI_ID_PCX22E
]
=
{
"PCX22e"
,
1
,
0
,
4
,
44
},
[
PCI_ID_VX222HRMIC
]
=
{
"VX222HR-Mic"
,
1
,
1
,
5
,
44
},
[
PCI_ID_VX222E_MIC
]
=
{
"VX222e-Mic"
,
1
,
1
,
5
,
44
},
[
PCI_ID_PCX924HR
]
=
{
"PCX924HR"
,
1
,
1
,
5
,
44
},
[
PCI_ID_PCX924E
]
=
{
"PCX924e"
,
1
,
1
,
5
,
44
},
[
PCI_ID_PCX924HRMIC
]
=
{
"PCX924HR-Mic"
,
1
,
1
,
5
,
44
},
[
PCI_ID_PCX924E_MIC
]
=
{
"PCX924e-Mic"
,
1
,
1
,
5
,
44
},
};
/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
/* VX222HR, VX222e, PCX22HR and PCX22e */
#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
/* some boards do not support 192kHz on digital AES input plugs */
#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
(x->fw_file_set == 0) || \
(x->fw_file_set == 2))
static
int
pcxhr_pll_freq_register
(
unsigned
int
freq
,
unsigned
int
*
pllreg
,
unsigned
int
*
realfreq
)
{
unsigned
int
reg
;
if
(
freq
<
6900
||
freq
>
110
25
0
)
if
(
freq
<
6900
||
freq
>
110
00
0
)
return
-
EINVAL
;
reg
=
(
28224000
*
10
)
/
freq
;
reg
=
(
reg
+
5
)
/
10
;
reg
=
(
28224000
*
2
)
/
freq
;
reg
=
(
reg
-
1
)
/
2
;
if
(
reg
<
0x200
)
*
pllreg
=
reg
+
0x800
;
else
if
(
reg
<
0x400
)
...
...
@@ -121,7 +191,7 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
reg
&=
~
3
;
}
if
(
realfreq
)
*
realfreq
=
(
(
28224000
*
10
)
/
reg
+
5
)
/
10
;
*
realfreq
=
(
28224000
/
(
reg
+
1
))
;
return
0
;
}
...
...
@@ -151,11 +221,6 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
#define PCXHR_FREQ_AES_3 0x03
#define PCXHR_FREQ_AES_4 0x0d
#define PCXHR_MODIFY_CLOCK_S_BIT 0x04
#define PCXHR_IRQ_TIMER_FREQ 92000
#define PCXHR_IRQ_TIMER_PERIOD 48
static
int
pcxhr_get_clock_reg
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
,
unsigned
int
*
reg
,
unsigned
int
*
freq
)
{
...
...
@@ -196,19 +261,32 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
<
0
)
{
snd_printk
(
KERN_ERR
"error CMD_ACCESS_IO_WRITE
for PLL register : %x!
\n
"
,
err
);
"error CMD_ACCESS_IO_WRITE
"
"for PLL register : %x!
\n
"
,
err
);
return
err
;
}
}
break
;
case
PCXHR_CLOCK_TYPE_WORD_CLOCK
:
val
=
PCXHR_FREQ_WORD_CLOCK
;
break
;
case
PCXHR_CLOCK_TYPE_AES_SYNC
:
val
=
PCXHR_FREQ_SYNC_AES
;
break
;
case
PCXHR_CLOCK_TYPE_AES_1
:
val
=
PCXHR_FREQ_AES_1
;
break
;
case
PCXHR_CLOCK_TYPE_AES_2
:
val
=
PCXHR_FREQ_AES_2
;
break
;
case
PCXHR_CLOCK_TYPE_AES_3
:
val
=
PCXHR_FREQ_AES_3
;
break
;
case
PCXHR_CLOCK_TYPE_AES_4
:
val
=
PCXHR_FREQ_AES_4
;
break
;
default
:
return
-
EINVAL
;
case
PCXHR_CLOCK_TYPE_WORD_CLOCK
:
val
=
PCXHR_FREQ_WORD_CLOCK
;
break
;
case
PCXHR_CLOCK_TYPE_AES_SYNC
:
val
=
PCXHR_FREQ_SYNC_AES
;
break
;
case
PCXHR_CLOCK_TYPE_AES_1
:
val
=
PCXHR_FREQ_AES_1
;
break
;
case
PCXHR_CLOCK_TYPE_AES_2
:
val
=
PCXHR_FREQ_AES_2
;
break
;
case
PCXHR_CLOCK_TYPE_AES_3
:
val
=
PCXHR_FREQ_AES_3
;
break
;
case
PCXHR_CLOCK_TYPE_AES_4
:
val
=
PCXHR_FREQ_AES_4
;
break
;
default:
return
-
EINVAL
;
}
*
reg
=
val
;
*
freq
=
realfreq
;
...
...
@@ -216,14 +294,13 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
}
int
pcxhr_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
)
static
int
pcxhr_sub_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
,
int
*
changed
)
{
unsigned
int
val
,
realfreq
,
speed
;
struct
pcxhr_rmh
rmh
;
int
err
,
changed
;
if
(
rate
==
0
)
return
0
;
/* nothing to do */
int
err
;
err
=
pcxhr_get_clock_reg
(
mgr
,
rate
,
&
val
,
&
realfreq
);
if
(
err
)
...
...
@@ -239,6 +316,10 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
if
(
mgr
->
codec_speed
!=
speed
)
{
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_WRITE
);
/* mute outputs */
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_MUTE_OUT
;
if
(
DSP_EXT_CMD_SET
(
mgr
))
{
rmh
.
cmd
[
1
]
=
1
;
rmh
.
cmd_len
=
2
;
}
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
return
err
;
...
...
@@ -253,9 +334,11 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
}
/* set the new frequency */
snd_printdd
(
"clock register : set %x
\n
"
,
val
);
err
=
pcxhr_write_io_num_reg_cont
(
mgr
,
PCXHR_FREQ_REG_MASK
,
val
,
&
changed
);
err
=
pcxhr_write_io_num_reg_cont
(
mgr
,
PCXHR_FREQ_REG_MASK
,
val
,
changed
);
if
(
err
)
return
err
;
mgr
->
sample_rate_real
=
realfreq
;
mgr
->
cur_clock_type
=
mgr
->
use_clock_type
;
...
...
@@ -263,12 +346,42 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
if
(
mgr
->
codec_speed
!=
speed
)
{
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
/* unmute outputs */
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_MUTE_OUT
;
if
(
DSP_EXT_CMD_SET
(
mgr
))
{
rmh
.
cmd
[
1
]
=
1
;
rmh
.
cmd_len
=
2
;
}
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
return
err
;
mgr
->
codec_speed
=
speed
;
/* save new codec speed */
}
snd_printdd
(
"pcxhr_sub_set_clock to %dHz (realfreq=%d)
\n
"
,
rate
,
realfreq
);
return
0
;
}
#define PCXHR_MODIFY_CLOCK_S_BIT 0x04
#define PCXHR_IRQ_TIMER_FREQ 92000
#define PCXHR_IRQ_TIMER_PERIOD 48
int
pcxhr_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
)
{
struct
pcxhr_rmh
rmh
;
int
err
,
changed
;
if
(
rate
==
0
)
return
0
;
/* nothing to do */
if
(
mgr
->
is_hr_stereo
)
err
=
hr222_sub_set_clock
(
mgr
,
rate
,
&
changed
);
else
err
=
pcxhr_sub_set_clock
(
mgr
,
rate
,
&
changed
);
if
(
err
)
return
err
;
if
(
changed
)
{
pcxhr_init_rmh
(
&
rmh
,
CMD_MODIFY_CLOCK
);
rmh
.
cmd
[
0
]
|=
PCXHR_MODIFY_CLOCK_S_BIT
;
/* resync fifos */
...
...
@@ -282,12 +395,12 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
if
(
err
)
return
err
;
}
snd_printdd
(
"pcxhr_set_clock to %dHz (realfreq=%d)
\n
"
,
rate
,
realfreq
);
return
0
;
}
int
pcxhr_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
static
int
pcxhr_sub_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
)
{
struct
pcxhr_rmh
rmh
;
...
...
@@ -295,13 +408,26 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
int
err
,
rate
;
switch
(
clock_type
)
{
case
PCXHR_CLOCK_TYPE_WORD_CLOCK
:
reg
=
REG_STATUS_WORD_CLOCK
;
break
;
case
PCXHR_CLOCK_TYPE_AES_SYNC
:
reg
=
REG_STATUS_AES_SYNC
;
break
;
case
PCXHR_CLOCK_TYPE_AES_1
:
reg
=
REG_STATUS_AES_1
;
break
;
case
PCXHR_CLOCK_TYPE_AES_2
:
reg
=
REG_STATUS_AES_2
;
break
;
case
PCXHR_CLOCK_TYPE_AES_3
:
reg
=
REG_STATUS_AES_3
;
break
;
case
PCXHR_CLOCK_TYPE_AES_4
:
reg
=
REG_STATUS_AES_4
;
break
;
default
:
return
-
EINVAL
;
case
PCXHR_CLOCK_TYPE_WORD_CLOCK
:
reg
=
REG_STATUS_WORD_CLOCK
;
break
;
case
PCXHR_CLOCK_TYPE_AES_SYNC
:
reg
=
REG_STATUS_AES_SYNC
;
break
;
case
PCXHR_CLOCK_TYPE_AES_1
:
reg
=
REG_STATUS_AES_1
;
break
;
case
PCXHR_CLOCK_TYPE_AES_2
:
reg
=
REG_STATUS_AES_2
;
break
;
case
PCXHR_CLOCK_TYPE_AES_3
:
reg
=
REG_STATUS_AES_3
;
break
;
case
PCXHR_CLOCK_TYPE_AES_4
:
reg
=
REG_STATUS_AES_4
;
break
;
default:
return
-
EINVAL
;
}
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
rmh
.
cmd_len
=
2
;
...
...
@@ -336,6 +462,18 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
}
int
pcxhr_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
)
{
if
(
mgr
->
is_hr_stereo
)
return
hr222_get_external_clock
(
mgr
,
clock_type
,
sample_rate
);
else
return
pcxhr_sub_get_external_clock
(
mgr
,
clock_type
,
sample_rate
);
}
/*
* start or stop playback/capture substream
*/
...
...
@@ -350,7 +488,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
start
=
1
;
else
{
if
(
stream
->
status
!=
PCXHR_STREAM_STATUS_SCHEDULE_STOP
)
{
snd_printk
(
KERN_ERR
"ERROR pcxhr_set_stream_state CANNOT be stopped
\n
"
);
snd_printk
(
KERN_ERR
"ERROR pcxhr_set_stream_state "
"CANNOT be stopped
\n
"
);
return
-
EINVAL
;
}
start
=
0
;
...
...
@@ -363,7 +502,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
stream
->
timer_buf_periods
=
0
;
stream
->
timer_is_synced
=
0
;
stream_mask
=
stream
->
pipe
->
is_capture
?
1
:
1
<<
stream
->
substream
->
number
;
stream_mask
=
stream
->
pipe
->
is_capture
?
1
:
1
<<
stream
->
substream
->
number
;
pcxhr_init_rmh
(
&
rmh
,
start
?
CMD_START_STREAM
:
CMD_STOP_STREAM
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
stream
->
pipe
->
is_capture
,
...
...
@@ -373,8 +513,10 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
)
snd_printk
(
KERN_ERR
"ERROR pcxhr_set_stream_state err=%x;
\n
"
,
err
);
stream
->
status
=
start
?
PCXHR_STREAM_STATUS_STARTED
:
PCXHR_STREAM_STATUS_STOPPED
;
snd_printk
(
KERN_ERR
"ERROR pcxhr_set_stream_state err=%x;
\n
"
,
err
);
stream
->
status
=
start
?
PCXHR_STREAM_STATUS_STARTED
:
PCXHR_STREAM_STATUS_STOPPED
;
return
err
;
}
...
...
@@ -399,13 +541,15 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
header
=
HEADER_FMT_BASE_LIN
;
break
;
case
SNDRV_PCM_FORMAT_S16_LE
:
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_16BITS
|
HEADER_FMT_INTEL
;
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_16BITS
|
HEADER_FMT_INTEL
;
break
;
case
SNDRV_PCM_FORMAT_S16_BE
:
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_16BITS
;
break
;
case
SNDRV_PCM_FORMAT_S24_3LE
:
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_24BITS
|
HEADER_FMT_INTEL
;
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_24BITS
|
HEADER_FMT_INTEL
;
break
;
case
SNDRV_PCM_FORMAT_S24_3BE
:
header
=
HEADER_FMT_BASE_LIN
|
HEADER_FMT_24BITS
;
...
...
@@ -414,7 +558,8 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
header
=
HEADER_FMT_BASE_FLOAT
|
HEADER_FMT_INTEL
;
break
;
default:
snd_printk
(
KERN_ERR
"error pcxhr_set_format() : unknown format
\n
"
);
snd_printk
(
KERN_ERR
"error pcxhr_set_format() : unknown format
\n
"
);
return
-
EINVAL
;
}
chip
=
snd_pcm_substream_chip
(
stream
->
substream
);
...
...
@@ -432,14 +577,31 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
is_capture
=
stream
->
pipe
->
is_capture
;
stream_num
=
is_capture
?
0
:
stream
->
substream
->
number
;
pcxhr_init_rmh
(
&
rmh
,
is_capture
?
CMD_FORMAT_STREAM_IN
:
CMD_FORMAT_STREAM_OUT
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
stream
->
pipe
->
first_audio
,
stream_num
,
0
);
if
(
is_capture
)
pcxhr_init_rmh
(
&
rmh
,
is_capture
?
CMD_FORMAT_STREAM_IN
:
CMD_FORMAT_STREAM_OUT
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
stream
->
pipe
->
first_audio
,
stream_num
,
0
);
if
(
is_capture
)
{
/* bug with old dsp versions: */
/* bit 12 also sets the format of the playback stream */
if
(
DSP_EXT_CMD_SET
(
chip
->
mgr
))
rmh
.
cmd
[
0
]
|=
1
<<
10
;
else
rmh
.
cmd
[
0
]
|=
1
<<
12
;
}
rmh
.
cmd
[
1
]
=
0
;
rmh
.
cmd
[
2
]
=
header
>>
8
;
rmh
.
cmd
[
3
]
=
(
header
&
0xff
)
<<
16
;
rmh
.
cmd_len
=
4
;
rmh
.
cmd_len
=
2
;
if
(
DSP_EXT_CMD_SET
(
chip
->
mgr
))
{
/* add channels and set bit 19 if channels>2 */
rmh
.
cmd
[
1
]
=
stream
->
channels
;
if
(
!
is_capture
)
{
/* playback : add channel mask to command */
rmh
.
cmd
[
2
]
=
(
stream
->
channels
==
1
)
?
0x01
:
0x03
;
rmh
.
cmd_len
=
3
;
}
}
rmh
.
cmd
[
rmh
.
cmd_len
++
]
=
header
>>
8
;
rmh
.
cmd
[
rmh
.
cmd_len
++
]
=
(
header
&
0xff
)
<<
16
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
)
snd_printk
(
KERN_ERR
"ERROR pcxhr_set_format err=%x;
\n
"
,
err
);
...
...
@@ -456,30 +618,38 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
is_capture
=
(
subs
->
stream
==
SNDRV_PCM_STREAM_CAPTURE
);
stream_num
=
is_capture
?
0
:
subs
->
number
;
snd_printdd
(
"pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%zx) subs(%d)
\n
"
,
snd_printdd
(
"pcxhr_update_r_buffer(pcm%c%d) : "
"addr(%p) bytes(%zx) subs(%d)
\n
"
,
is_capture
?
'c'
:
'p'
,
chip
->
chip_idx
,
(
void
*
)(
long
)
subs
->
runtime
->
dma_addr
,
subs
->
runtime
->
dma_bytes
,
subs
->
number
);
pcxhr_init_rmh
(
&
rmh
,
CMD_UPDATE_R_BUFFERS
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
stream
->
pipe
->
first_audio
,
stream_num
,
0
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
stream
->
pipe
->
first_audio
,
stream_num
,
0
);
/* max buffer size is 2 MByte */
snd_BUG_ON
(
subs
->
runtime
->
dma_bytes
>=
0x200000
);
rmh
.
cmd
[
1
]
=
subs
->
runtime
->
dma_bytes
*
8
;
/* size in bits */
rmh
.
cmd
[
2
]
=
subs
->
runtime
->
dma_addr
>>
24
;
/* most significant byte */
rmh
.
cmd
[
2
]
|=
1
<<
19
;
/* this is a circular buffer */
rmh
.
cmd
[
3
]
=
subs
->
runtime
->
dma_addr
&
MASK_DSP_WORD
;
/* least 3 significant bytes */
/* size in bits */
rmh
.
cmd
[
1
]
=
subs
->
runtime
->
dma_bytes
*
8
;
/* most significant byte */
rmh
.
cmd
[
2
]
=
subs
->
runtime
->
dma_addr
>>
24
;
/* this is a circular buffer */
rmh
.
cmd
[
2
]
|=
1
<<
19
;
/* least 3 significant bytes */
rmh
.
cmd
[
3
]
=
subs
->
runtime
->
dma_addr
&
MASK_DSP_WORD
;
rmh
.
cmd_len
=
4
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
)
snd_printk
(
KERN_ERR
"ERROR CMD_UPDATE_R_BUFFERS err=%x;
\n
"
,
err
);
snd_printk
(
KERN_ERR
"ERROR CMD_UPDATE_R_BUFFERS err=%x;
\n
"
,
err
);
return
err
;
}
#if 0
static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, snd_pcm_uframes_t *sample_count)
static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
snd_pcm_uframes_t *sample_count)
{
struct pcxhr_rmh rmh;
int err;
...
...
@@ -533,8 +703,8 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
for
(
j
=
0
;
j
<
chip
->
nb_streams_play
;
j
++
)
{
if
(
pcxhr_stream_scheduled_get_pipe
(
&
chip
->
playback_stream
[
j
],
&
pipe
))
{
playback_mask
|=
(
1
<<
pipe
->
first_audio
);
break
;
/* add only once, as all playback
streams of
* one chip use the same pipe
break
;
/* add only once, as all playback
*
streams of
one chip use the same pipe
*/
}
}
...
...
@@ -545,19 +715,21 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
return
;
}
snd_printdd
(
"pcxhr_trigger_tasklet : playback_mask=%x capture_mask=%x
\n
"
,
snd_printdd
(
"pcxhr_trigger_tasklet : "
"playback_mask=%x capture_mask=%x
\n
"
,
playback_mask
,
capture_mask
);
/* synchronous stop of all the pipes concerned */
err
=
pcxhr_set_pipe_state
(
mgr
,
playback_mask
,
capture_mask
,
0
);
if
(
err
)
{
mutex_unlock
(
&
mgr
->
setup_mutex
);
snd_printk
(
KERN_ERR
"pcxhr_trigger_tasklet : error stop pipes (P%x C%x)
\n
"
,
snd_printk
(
KERN_ERR
"pcxhr_trigger_tasklet : "
"error stop pipes (P%x C%x)
\n
"
,
playback_mask
,
capture_mask
);
return
;
}
/*
unfortunately
the dsp lost format and buffer info with the stop pipe */
/* the dsp lost format and buffer info with the stop pipe */
for
(
i
=
0
;
i
<
mgr
->
num_cards
;
i
++
)
{
struct
pcxhr_stream
*
stream
;
chip
=
mgr
->
chip
[
i
];
...
...
@@ -596,12 +768,15 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
err
=
pcxhr_set_pipe_state
(
mgr
,
playback_mask
,
capture_mask
,
1
);
if
(
err
)
{
mutex_unlock
(
&
mgr
->
setup_mutex
);
snd_printk
(
KERN_ERR
"pcxhr_trigger_tasklet : error start pipes (P%x C%x)
\n
"
,
snd_printk
(
KERN_ERR
"pcxhr_trigger_tasklet : "
"error start pipes (P%x C%x)
\n
"
,
playback_mask
,
capture_mask
);
return
;
}
/* put the streams into the running state now (increment pointer by interrupt) */
/* put the streams into the running state now
* (increment pointer by interrupt)
*/
spin_lock_irqsave
(
&
mgr
->
lock
,
flags
);
for
(
i
=
0
;
i
<
mgr
->
num_cards
;
i
++
)
{
struct
pcxhr_stream
*
stream
;
...
...
@@ -615,7 +790,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
stream
=
&
chip
->
playback_stream
[
j
];
if
(
stream
->
status
==
PCXHR_STREAM_STATUS_STARTED
)
{
/* playback will already have advanced ! */
stream
->
timer_period_frag
+=
PCXHR_GRANULARITY
;
stream
->
timer_period_frag
+=
mgr
->
granularity
;
stream
->
status
=
PCXHR_STREAM_STATUS_RUNNING
;
}
}
...
...
@@ -697,12 +872,14 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
pcxhr_init_rmh
(
&
rmh
,
CMD_SET_TIMER_INTERRUPT
);
if
(
start
)
{
mgr
->
dsp_time_last
=
PCXHR_DSP_TIME_INVALID
;
/* last dsp time invalid */
rmh
.
cmd
[
0
]
|=
PCXHR_GRANULARITY
;
/* last dsp time invalid */
mgr
->
dsp_time_last
=
PCXHR_DSP_TIME_INVALID
;
rmh
.
cmd
[
0
]
|=
mgr
->
granularity
;
}
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
<
0
)
snd_printk
(
KERN_ERR
"error pcxhr_hardware_timer err(%x)
\n
"
,
err
);
snd_printk
(
KERN_ERR
"error pcxhr_hardware_timer err(%x)
\n
"
,
err
);
return
err
;
}
...
...
@@ -713,38 +890,16 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
{
struct
snd_pcxhr
*
chip
=
snd_pcm_substream_chip
(
subs
);
struct
pcxhr_mgr
*
mgr
=
chip
->
mgr
;
/*
struct pcxhr_stream *stream = (pcxhr_stream_t*)subs->runtime->private_data;
*/
int
err
=
0
;
snd_printdd
(
"pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)
\n
"
,
subs
->
runtime
->
period_size
,
subs
->
runtime
->
periods
,
subs
->
runtime
->
buffer_size
);
/*
if(subs->runtime->period_size <= PCXHR_GRANULARITY) {
snd_printk(KERN_ERR "pcxhr_prepare : error period_size too small (%x)\n",
(unsigned int)subs->runtime->period_size);
return -EINVAL;
}
*/
mutex_lock
(
&
mgr
->
setup_mutex
);
do
{
/* if the stream was stopped before, format and buffer were reset */
/*
if(stream->status == PCXHR_STREAM_STATUS_STOPPED) {
err = pcxhr_set_format(stream);
if(err) break;
err = pcxhr_update_r_buffer(stream);
if(err) break;
}
*/
/* only the first stream can choose the sample rate */
/* the further opened streams will be limited to its frequency (see open) */
/* set the clock only once (first stream) */
if
(
mgr
->
sample_rate
!=
subs
->
runtime
->
rate
)
{
err
=
pcxhr_set_clock
(
mgr
,
subs
->
runtime
->
rate
);
...
...
@@ -787,22 +942,9 @@ static int pcxhr_hw_params(struct snd_pcm_substream *subs,
stream
->
channels
=
channels
;
stream
->
format
=
format
;
/* set the format to the board */
/*
err = pcxhr_set_format(stream);
if(err) {
mutex_unlock(&mgr->setup_mutex);
return err;
}
*/
/* allocate buffer */
err
=
snd_pcm_lib_malloc_pages
(
subs
,
params_buffer_bytes
(
hw
));
/*
if (err > 0) {
err = pcxhr_update_r_buffer(stream);
}
*/
mutex_unlock
(
&
mgr
->
setup_mutex
);
return
err
;
...
...
@@ -820,14 +962,18 @@ static int pcxhr_hw_free(struct snd_pcm_substream *subs)
*/
static
struct
snd_pcm_hardware
pcxhr_caps
=
{
.
info
=
(
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_SYNC_START
|
0
/*SNDRV_PCM_INFO_PAUSE*/
),
.
formats
=
(
SNDRV_PCM_FMTBIT_U8
|
SNDRV_PCM_FMTBIT_S16_LE
|
SNDRV_PCM_FMTBIT_S16_BE
|
SNDRV_PCM_FMTBIT_S24_3LE
|
SNDRV_PCM_FMTBIT_S24_3BE
|
SNDRV_PCM_FMTBIT_FLOAT_LE
),
.
rates
=
SNDRV_PCM_RATE_CONTINUOUS
|
SNDRV_PCM_RATE_8000_192000
,
.
info
=
(
SNDRV_PCM_INFO_MMAP
|
SNDRV_PCM_INFO_INTERLEAVED
|
SNDRV_PCM_INFO_MMAP_VALID
|
SNDRV_PCM_INFO_SYNC_START
),
.
formats
=
(
SNDRV_PCM_FMTBIT_U8
|
SNDRV_PCM_FMTBIT_S16_LE
|
SNDRV_PCM_FMTBIT_S16_BE
|
SNDRV_PCM_FMTBIT_S24_3LE
|
SNDRV_PCM_FMTBIT_S24_3BE
|
SNDRV_PCM_FMTBIT_FLOAT_LE
),
.
rates
=
(
SNDRV_PCM_RATE_CONTINUOUS
|
SNDRV_PCM_RATE_8000_192000
),
.
rate_min
=
8000
,
.
rate_max
=
192000
,
.
channels_min
=
1
,
...
...
@@ -847,6 +993,7 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
struct
pcxhr_mgr
*
mgr
=
chip
->
mgr
;
struct
snd_pcm_runtime
*
runtime
=
subs
->
runtime
;
struct
pcxhr_stream
*
stream
;
int
err
;
mutex_lock
(
&
mgr
->
setup_mutex
);
...
...
@@ -874,6 +1021,18 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
return
-
EBUSY
;
}
/* float format support is in some cases buggy on stereo cards */
if
(
mgr
->
is_hr_stereo
)
runtime
->
hw
.
formats
&=
~
SNDRV_PCM_FMTBIT_FLOAT_LE
;
/* buffer-size should better be multiple of period-size */
err
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
);
if
(
err
<
0
)
{
mutex_unlock
(
&
mgr
->
setup_mutex
);
return
err
;
}
/* if a sample rate is already used or fixed by external clock,
* the stream cannot change
*/
...
...
@@ -889,7 +1048,8 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
mutex_unlock
(
&
mgr
->
setup_mutex
);
return
-
EBUSY
;
}
runtime
->
hw
.
rate_min
=
runtime
->
hw
.
rate_max
=
external_rate
;
runtime
->
hw
.
rate_min
=
external_rate
;
runtime
->
hw
.
rate_max
=
external_rate
;
}
}
...
...
@@ -899,9 +1059,11 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
runtime
->
private_data
=
stream
;
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_BUFFER_BYTES
,
4
);
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
4
);
/* better get a divisor of granularity values (96 or 192) */
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_BUFFER_SIZE
,
32
);
snd_pcm_hw_constraint_step
(
runtime
,
0
,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE
,
32
);
snd_pcm_set_sync
(
subs
);
mgr
->
ref_count_rate
++
;
...
...
@@ -919,7 +1081,8 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
mutex_lock
(
&
mgr
->
setup_mutex
);
snd_printdd
(
"pcxhr_close chip%d subs%d
\n
"
,
chip
->
chip_idx
,
subs
->
number
);
snd_printdd
(
"pcxhr_close chip%d subs%d
\n
"
,
chip
->
chip_idx
,
subs
->
number
);
/* sample rate released */
if
(
--
mgr
->
ref_count_rate
==
0
)
{
...
...
@@ -1016,7 +1179,8 @@ static int pcxhr_chip_dev_free(struct snd_device *device)
/*
*/
static
int
__devinit
pcxhr_create
(
struct
pcxhr_mgr
*
mgr
,
struct
snd_card
*
card
,
int
idx
)
static
int
__devinit
pcxhr_create
(
struct
pcxhr_mgr
*
mgr
,
struct
snd_card
*
card
,
int
idx
)
{
int
err
;
struct
snd_pcxhr
*
chip
;
...
...
@@ -1040,7 +1204,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
if
(
idx
<
mgr
->
capture_chips
)
{
if
(
mgr
->
mono_capture
)
chip
->
nb_streams_capt
=
2
;
/* 2 mono streams
(left+right)
*/
chip
->
nb_streams_capt
=
2
;
/* 2 mono streams */
else
chip
->
nb_streams_capt
=
1
;
/* or 1 stereo stream */
}
...
...
@@ -1057,7 +1221,8 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
}
/* proc interface */
static
void
pcxhr_proc_info
(
struct
snd_info_entry
*
entry
,
struct
snd_info_buffer
*
buffer
)
static
void
pcxhr_proc_info
(
struct
snd_info_entry
*
entry
,
struct
snd_info_buffer
*
buffer
)
{
struct
snd_pcxhr
*
chip
=
entry
->
private_data
;
struct
pcxhr_mgr
*
mgr
=
chip
->
mgr
;
...
...
@@ -1070,8 +1235,10 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
short
ver_maj
=
(
mgr
->
dsp_version
>>
16
)
&
0xff
;
short
ver_min
=
(
mgr
->
dsp_version
>>
8
)
&
0xff
;
short
ver_build
=
mgr
->
dsp_version
&
0xff
;
snd_iprintf
(
buffer
,
"module version %s
\n
"
,
PCXHR_DRIVER_VERSION_STRING
);
snd_iprintf
(
buffer
,
"dsp version %d.%d.%d
\n
"
,
ver_maj
,
ver_min
,
ver_build
);
snd_iprintf
(
buffer
,
"module version %s
\n
"
,
PCXHR_DRIVER_VERSION_STRING
);
snd_iprintf
(
buffer
,
"dsp version %d.%d.%d
\n
"
,
ver_maj
,
ver_min
,
ver_build
);
if
(
mgr
->
board_has_analog
)
snd_iprintf
(
buffer
,
"analog io available
\n
"
);
else
...
...
@@ -1085,18 +1252,22 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
if
(
ref
>
0
)
{
if
(
mgr
->
sample_rate_real
!=
0
&&
mgr
->
sample_rate_real
!=
48000
)
{
ref
=
(
ref
*
48000
)
/
mgr
->
sample_rate_real
;
if
(
mgr
->
sample_rate_real
>=
PCXHR_IRQ_TIMER_FREQ
)
ref
=
(
ref
*
48000
)
/
mgr
->
sample_rate_real
;
if
(
mgr
->
sample_rate_real
>=
PCXHR_IRQ_TIMER_FREQ
)
ref
*=
2
;
}
cur
=
100
-
(
100
*
cur
)
/
ref
;
snd_iprintf
(
buffer
,
"cpu load %d%%
\n
"
,
cur
);
snd_iprintf
(
buffer
,
"buffer pool %d/%d
kWords
\n
"
,
snd_iprintf
(
buffer
,
"buffer pool %d/%d
\n
"
,
rmh
.
stat
[
2
],
rmh
.
stat
[
3
]);
}
}
snd_iprintf
(
buffer
,
"dma granularity : %d
\n
"
,
PCXHR_GRANULARITY
);
snd_iprintf
(
buffer
,
"dsp time errors : %d
\n
"
,
mgr
->
dsp_time_err
);
snd_iprintf
(
buffer
,
"dma granularity : %d
\n
"
,
mgr
->
granularity
);
snd_iprintf
(
buffer
,
"dsp time errors : %d
\n
"
,
mgr
->
dsp_time_err
);
snd_iprintf
(
buffer
,
"dsp async pipe xrun errors : %d
\n
"
,
mgr
->
async_err_pipe_xrun
);
snd_iprintf
(
buffer
,
"dsp async stream xrun errors : %d
\n
"
,
...
...
@@ -1111,33 +1282,52 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
rmh
.
cmd_idx
=
CMD_LAST_INDEX
;
if
(
!
pcxhr_send_msg
(
mgr
,
&
rmh
)
)
{
int
i
;
if
(
rmh
.
stat_len
>
8
)
rmh
.
stat_len
=
8
;
for
(
i
=
0
;
i
<
rmh
.
stat_len
;
i
++
)
snd_iprintf
(
buffer
,
"debug[%02d] = %06x
\n
"
,
i
,
rmh
.
stat
[
i
]);
snd_iprintf
(
buffer
,
"debug[%02d] = %06x
\n
"
,
i
,
rmh
.
stat
[
i
]);
}
}
else
snd_iprintf
(
buffer
,
"no firmware loaded
\n
"
);
snd_iprintf
(
buffer
,
"
\n
"
);
}
static
void
pcxhr_proc_sync
(
struct
snd_info_entry
*
entry
,
struct
snd_info_buffer
*
buffer
)
static
void
pcxhr_proc_sync
(
struct
snd_info_entry
*
entry
,
struct
snd_info_buffer
*
buffer
)
{
struct
snd_pcxhr
*
chip
=
entry
->
private_data
;
struct
pcxhr_mgr
*
mgr
=
chip
->
mgr
;
static
char
*
texts
[
7
]
=
{
"Internal"
,
"Word"
,
"AES Sync"
,
"AES 1"
,
"AES 2"
,
"AES 3"
,
"AES 4"
static
const
char
*
textsHR22
[
3
]
=
{
"Internal"
,
"AES Sync"
,
"AES 1"
};
static
const
char
*
textsPCXHR
[
7
]
=
{
"Internal"
,
"Word"
,
"AES Sync"
,
"AES 1"
,
"AES 2"
,
"AES 3"
,
"AES 4"
};
const
char
**
texts
;
int
max_clock
;
if
(
mgr
->
is_hr_stereo
)
{
texts
=
textsHR22
;
max_clock
=
HR22_CLOCK_TYPE_MAX
;
}
else
{
texts
=
textsPCXHR
;
max_clock
=
PCXHR_CLOCK_TYPE_MAX
;
}
snd_iprintf
(
buffer
,
"
\n
%s
\n
"
,
mgr
->
longname
);
snd_iprintf
(
buffer
,
"Current Sample Clock
\t
: %s
\n
"
,
texts
[
mgr
->
cur_clock_type
]);
snd_iprintf
(
buffer
,
"Current Sample Rate
\t
= %d
\n
"
,
mgr
->
sample_rate_real
);
snd_iprintf
(
buffer
,
"Current Sample Clock
\t
: %s
\n
"
,
texts
[
mgr
->
cur_clock_type
]);
snd_iprintf
(
buffer
,
"Current Sample Rate
\t
= %d
\n
"
,
mgr
->
sample_rate_real
);
/* commands available when embedded DSP is running */
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_DSP_MAIN_INDEX
))
{
int
i
,
err
,
sample_rate
;
for
(
i
=
PCXHR_CLOCK_TYPE_WORD_CLOCK
;
i
<
(
3
+
mgr
->
capture_chips
)
;
i
++
)
{
for
(
i
=
1
;
i
<=
max_clock
;
i
++
)
{
err
=
pcxhr_get_external_clock
(
mgr
,
i
,
&
sample_rate
);
if
(
err
)
break
;
snd_iprintf
(
buffer
,
"%s Clock
\t\t
= %d
\n
"
,
texts
[
i
],
sample_rate
);
snd_iprintf
(
buffer
,
"%s Clock
\t\t
= %d
\n
"
,
texts
[
i
],
sample_rate
);
}
}
else
snd_iprintf
(
buffer
,
"no firmware loaded
\n
"
);
...
...
@@ -1195,7 +1385,8 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
/*
* probe function - creates the card manager
*/
static
int
__devinit
pcxhr_probe
(
struct
pci_dev
*
pci
,
const
struct
pci_device_id
*
pci_id
)
static
int
__devinit
pcxhr_probe
(
struct
pci_dev
*
pci
,
const
struct
pci_device_id
*
pci_id
)
{
static
int
dev
;
struct
pcxhr_mgr
*
mgr
;
...
...
@@ -1218,7 +1409,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
/* check if we can restrict PCI DMA transfers to 32 bits */
if
(
pci_set_dma_mask
(
pci
,
DMA_32BIT_MASK
)
<
0
)
{
snd_printk
(
KERN_ERR
"architecture does not support 32bit PCI busmaster DMA
\n
"
);
snd_printk
(
KERN_ERR
"architecture does not support "
"32bit PCI busmaster DMA
\n
"
);
pci_disable_device
(
pci
);
return
-
ENXIO
;
}
...
...
@@ -1235,11 +1427,25 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
pci_disable_device
(
pci
);
return
-
ENODEV
;
}
card_name
=
pcxhr_board_params
[
pci_id
->
driver_data
].
board_name
;
mgr
->
playback_chips
=
pcxhr_board_params
[
pci_id
->
driver_data
].
playback_chips
;
mgr
->
capture_chips
=
pcxhr_board_params
[
pci_id
->
driver_data
].
capture_chips
;
mgr
->
firmware_num
=
pcxhr_board_params
[
pci_id
->
driver_data
].
firmware_num
;
card_name
=
pcxhr_board_params
[
pci_id
->
driver_data
].
board_name
;
mgr
->
playback_chips
=
pcxhr_board_params
[
pci_id
->
driver_data
].
playback_chips
;
mgr
->
capture_chips
=
pcxhr_board_params
[
pci_id
->
driver_data
].
capture_chips
;
mgr
->
fw_file_set
=
pcxhr_board_params
[
pci_id
->
driver_data
].
fw_file_set
;
mgr
->
firmware_num
=
pcxhr_board_params
[
pci_id
->
driver_data
].
firmware_num
;
mgr
->
mono_capture
=
mono
[
dev
];
mgr
->
is_hr_stereo
=
(
mgr
->
playback_chips
==
1
);
mgr
->
board_has_aes1
=
PCXHR_BOARD_HAS_AES1
(
mgr
);
mgr
->
board_aes_in_192k
=
!
PCXHR_BOARD_AESIN_NO_192K
(
mgr
);
if
(
mgr
->
is_hr_stereo
)
mgr
->
granularity
=
PCXHR_GRANULARITY_HR22
;
else
mgr
->
granularity
=
PCXHR_GRANULARITY
;
/* resource assignment */
if
((
err
=
pci_request_regions
(
pci
,
card_name
))
<
0
)
{
...
...
@@ -1262,7 +1468,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
mgr
->
irq
=
pci
->
irq
;
sprintf
(
mgr
->
shortname
,
"Digigram %s"
,
card_name
);
sprintf
(
mgr
->
longname
,
"%s at 0x%lx & 0x%lx, 0x%lx irq %i"
,
mgr
->
shortname
,
sprintf
(
mgr
->
longname
,
"%s at 0x%lx & 0x%lx, 0x%lx irq %i"
,
mgr
->
shortname
,
mgr
->
port
[
0
],
mgr
->
port
[
1
],
mgr
->
port
[
2
],
mgr
->
irq
);
/* ISR spinlock */
...
...
@@ -1273,10 +1480,14 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
mutex_init
(
&
mgr
->
setup_mutex
);
/* init taslket */
tasklet_init
(
&
mgr
->
msg_taskq
,
pcxhr_msg_tasklet
,
(
unsigned
long
)
mgr
);
tasklet_init
(
&
mgr
->
trigger_taskq
,
pcxhr_trigger_tasklet
,
(
unsigned
long
)
mgr
);
tasklet_init
(
&
mgr
->
msg_taskq
,
pcxhr_msg_tasklet
,
(
unsigned
long
)
mgr
);
tasklet_init
(
&
mgr
->
trigger_taskq
,
pcxhr_trigger_tasklet
,
(
unsigned
long
)
mgr
);
mgr
->
prmh
=
kmalloc
(
sizeof
(
*
mgr
->
prmh
)
+
sizeof
(
u32
)
*
(
PCXHR_SIZE_MAX_LONG_STATUS
-
PCXHR_SIZE_MAX_STATUS
),
sizeof
(
u32
)
*
(
PCXHR_SIZE_MAX_LONG_STATUS
-
PCXHR_SIZE_MAX_STATUS
),
GFP_KERNEL
);
if
(
!
mgr
->
prmh
)
{
pcxhr_free
(
mgr
);
...
...
@@ -1297,7 +1508,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
else
idx
=
index
[
dev
]
+
i
;
snprintf
(
tmpid
,
sizeof
(
tmpid
),
"%s-%d"
,
id
[
dev
]
?
id
[
dev
]
:
card_name
,
i
);
snprintf
(
tmpid
,
sizeof
(
tmpid
),
"%s-%d"
,
id
[
dev
]
?
id
[
dev
]
:
card_name
,
i
);
card
=
snd_card_new
(
idx
,
tmpid
,
THIS_MODULE
,
0
);
if
(
!
card
)
{
...
...
sound/pci/pcxhr/pcxhr.h
View file @
a9c3c7e0
...
...
@@ -27,15 +27,18 @@
#include <linux/mutex.h>
#include <sound/pcm.h>
#define PCXHR_DRIVER_VERSION 0x000
804
/* 0.8.4
*/
#define PCXHR_DRIVER_VERSION_STRING "0.
8.4"
/* 0.8.4
*/
#define PCXHR_DRIVER_VERSION 0x000
905
/* 0.9.5
*/
#define PCXHR_DRIVER_VERSION_STRING "0.
9.5"
/* 0.9.5
*/
#define PCXHR_MAX_CARDS 6
#define PCXHR_PLAYBACK_STREAMS 4
#define PCXHR_GRANULARITY 96
/* transfer granularity (should be min 96 and multiple of 48) */
#define PCXHR_GRANULARITY_MIN 96
/* transfer granularity of pipes and the dsp time (MBOX4) */
#define PCXHR_GRANULARITY 96
/* min 96 and multiple of 48 */
/* transfer granularity of pipes and the dsp time (MBOX4) */
#define PCXHR_GRANULARITY_MIN 96
/* TODO : granularity could be 64 or 128 */
#define PCXHR_GRANULARITY_HR22 192
/* granularity for stereo cards */
struct
snd_pcxhr
;
struct
pcxhr_mgr
;
...
...
@@ -51,6 +54,11 @@ enum pcxhr_clock_type {
PCXHR_CLOCK_TYPE_AES_2
,
PCXHR_CLOCK_TYPE_AES_3
,
PCXHR_CLOCK_TYPE_AES_4
,
PCXHR_CLOCK_TYPE_MAX
=
PCXHR_CLOCK_TYPE_AES_4
,
HR22_CLOCK_TYPE_INTERNAL
=
PCXHR_CLOCK_TYPE_INTERNAL
,
HR22_CLOCK_TYPE_AES_SYNC
,
HR22_CLOCK_TYPE_AES_1
,
HR22_CLOCK_TYPE_MAX
=
HR22_CLOCK_TYPE_AES_1
,
};
struct
pcxhr_mgr
{
...
...
@@ -61,6 +69,8 @@ struct pcxhr_mgr {
int
irq
;
int
granularity
;
/* card access with 1 mem bar and 2 io bar's */
unsigned
long
port
[
3
];
...
...
@@ -83,11 +93,16 @@ struct pcxhr_mgr {
/* hardware interface */
unsigned
int
dsp_loaded
;
/* bit flags of loaded dsp indices */
unsigned
int
dsp_version
;
/* read from embedded once firmware is loaded */
int
board_has_analog
;
/* if 0 the board is digital only */
int
mono_capture
;
/* if 1 the board does mono capture */
int
playback_chips
;
/* 4 or 6 */
int
capture_chips
;
/* 4 or 1 */
int
firmware_num
;
/* 41 or 42 */
int
playback_chips
;
int
capture_chips
;
int
fw_file_set
;
int
firmware_num
;
int
is_hr_stereo
:
1
;
int
board_has_aes1
:
1
;
/* if 1 board has AES1 plug and SRC */
int
board_has_analog
:
1
;
/* if 0 the board is digital only */
int
board_has_mic
:
1
;
/* if 1 the board has microphone input */
int
board_aes_in_192k
:
1
;
/* if 1 the aes input plugs do support 192kHz */
int
mono_capture
:
1
;
/* if 1 the board does mono capture */
struct
snd_dma_buffer
hostport
;
...
...
@@ -106,6 +121,9 @@ struct pcxhr_mgr {
int
async_err_stream_xrun
;
int
async_err_pipe_xrun
;
int
async_err_other_last
;
unsigned
char
xlx_cfg
;
/* copy of PCXHR_XLX_CFG register */
unsigned
char
xlx_selmic
;
/* copy of PCXHR_XLX_SELMIC register */
};
...
...
@@ -156,22 +174,28 @@ struct snd_pcxhr {
struct
snd_pcm
*
pcm
;
/* PCM */
struct
pcxhr_pipe
playback_pipe
;
/* 1 stereo pipe only */
struct
pcxhr_pipe
capture_pipe
[
2
];
/* 1 stereo pipe
or 2 mono pipes */
struct
pcxhr_pipe
capture_pipe
[
2
];
/* 1 stereo
or 2 mono pipes */
struct
pcxhr_stream
playback_stream
[
PCXHR_PLAYBACK_STREAMS
];
struct
pcxhr_stream
capture_stream
[
2
];
/* 1 stereo
stream
or 2 mono streams */
struct
pcxhr_stream
capture_stream
[
2
];
/* 1 stereo or 2 mono streams */
int
nb_streams_play
;
int
nb_streams_capt
;
int
analog_playback_active
[
2
];
/* Mixer : Master Playback active (!mute)
*/
int
analog_playback_active
[
2
];
/* Mixer : Master Playback !mute
*/
int
analog_playback_volume
[
2
];
/* Mixer : Master Playback Volume */
int
analog_capture_volume
[
2
];
/* Mixer : Master Capture Volume */
int
digital_playback_active
[
PCXHR_PLAYBACK_STREAMS
][
2
];
/* Mixer : Digital Playback Active [streams][stereo]*/
int
digital_playback_volume
[
PCXHR_PLAYBACK_STREAMS
][
2
];
/* Mixer : Digital Playback Volume [streams][stereo]*/
int
digital_capture_volume
[
2
];
/* Mixer : Digital Capture Volume [stereo]
*/
int
digital_playback_active
[
PCXHR_PLAYBACK_STREAMS
][
2
];
int
digital_playback_volume
[
PCXHR_PLAYBACK_STREAMS
][
2
];
int
digital_capture_volume
[
2
];
/* Mixer : Digital Capture Volume
*/
int
monitoring_active
[
2
];
/* Mixer : Monitoring Active */
int
monitoring_volume
[
2
];
/* Mixer : Monitoring Volume */
int
audio_capture_source
;
/* Mixer : Audio Capture Source */
int
mic_volume
;
/* used by cards with MIC only */
int
mic_boost
;
/* used by cards with MIC only */
int
mic_active
;
/* used by cards with MIC only */
int
analog_capture_active
;
/* used by cards with MIC only */
int
phantom_power
;
/* used by cards with MIC only */
unsigned
char
aes_bits
[
5
];
/* Mixer : IEC958_AES bits */
};
...
...
@@ -184,6 +208,8 @@ struct pcxhr_hostport
/* exported */
int
pcxhr_create_pcm
(
struct
snd_pcxhr
*
chip
);
int
pcxhr_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
);
int
pcxhr_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
);
int
pcxhr_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
);
#endif
/* __SOUND_PCXHR_H */
sound/pci/pcxhr/pcxhr_core.c
View file @
a9c3c7e0
...
...
@@ -132,13 +132,15 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
*
read
=
PCXHR_INPB
(
mgr
,
reg
);
if
((
*
read
&
mask
)
==
bit
)
{
if
(
i
>
100
)
snd_printdd
(
"ATTENTION! check_reg(%x) loopcount=%d
\n
"
,
snd_printdd
(
"ATTENTION! check_reg(%x) "
"loopcount=%d
\n
"
,
reg
,
i
);
return
0
;
}
i
++
;
}
while
(
time_after_eq
(
end_time
,
jiffies
));
snd_printk
(
KERN_ERR
"pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=0x%x
\n
"
,
snd_printk
(
KERN_ERR
"pcxhr_check_reg_bit: timeout, reg=%x, mask=0x%x, val=%x
\n
"
,
reg
,
mask
,
*
read
);
return
-
EIO
;
}
...
...
@@ -159,18 +161,22 @@ static int pcxhr_check_reg_bit(struct pcxhr_mgr *mgr, unsigned int reg,
#define PCXHR_IT_TEST_XILINX (0x0000003C | PCXHR_MASK_IT_HF1 | \
PCXHR_MASK_IT_MANAGE_HF5)
#define PCXHR_IT_DOWNLOAD_BOOT (0x0000000C | PCXHR_MASK_IT_HF1 | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_RESET_BOARD_FUNC (0x0000000C | PCXHR_MASK_IT_HF0 | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT_EXTRA)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT_EXTRA)
#define PCXHR_IT_DOWNLOAD_DSP (0x0000000C | \
PCXHR_MASK_IT_MANAGE_HF5 | PCXHR_MASK_IT_WAIT)
PCXHR_MASK_IT_MANAGE_HF5 | \
PCXHR_MASK_IT_WAIT)
#define PCXHR_IT_DEBUG (0x0000005A | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_SEMAPHORE (0x0000005C | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_MESSAGE (0x00000074 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_RESET_CHK (0x00000076 | PCXHR_MASK_IT_NO_HF0_HF1)
#define PCXHR_IT_UPDATE_RBUFFER (0x00000078 | PCXHR_MASK_IT_NO_HF0_HF1)
static
int
pcxhr_send_it_dsp
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
itdsp
,
int
atomic
)
static
int
pcxhr_send_it_dsp
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
itdsp
,
int
atomic
)
{
int
err
;
unsigned
char
reg
;
...
...
@@ -178,17 +184,21 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
if
(
itdsp
&
PCXHR_MASK_IT_MANAGE_HF5
)
{
/* clear hf5 bit */
PCXHR_OUTPL
(
mgr
,
PCXHR_PLX_MBOX0
,
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX0
)
&
~
PCXHR_MBOX0_HF5
);
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX0
)
&
~
PCXHR_MBOX0_HF5
);
}
if
((
itdsp
&
PCXHR_MASK_IT_NO_HF0_HF1
)
==
0
)
{
reg
=
PCXHR_ICR_HI08_RREQ
|
PCXHR_ICR_HI08_TREQ
|
PCXHR_ICR_HI08_HDRQ
;
reg
=
(
PCXHR_ICR_HI08_RREQ
|
PCXHR_ICR_HI08_TREQ
|
PCXHR_ICR_HI08_HDRQ
);
if
(
itdsp
&
PCXHR_MASK_IT_HF0
)
reg
|=
PCXHR_ICR_HI08_HF0
;
if
(
itdsp
&
PCXHR_MASK_IT_HF1
)
reg
|=
PCXHR_ICR_HI08_HF1
;
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_ICR
,
reg
);
}
reg
=
(
unsigned
char
)(((
itdsp
&
PCXHR_MASK_EXTRA_INFO
)
>>
1
)
|
PCXHR_CVR_HI08_HC
);
reg
=
(
unsigned
char
)(((
itdsp
&
PCXHR_MASK_EXTRA_INFO
)
>>
1
)
|
PCXHR_CVR_HI08_HC
);
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_CVR
,
reg
);
if
(
itdsp
&
PCXHR_MASK_IT_WAIT
)
{
if
(
atomic
)
...
...
@@ -211,10 +221,14 @@ static int pcxhr_send_it_dsp(struct pcxhr_mgr *mgr, unsigned int itdsp, int atom
}
if
(
itdsp
&
PCXHR_MASK_IT_MANAGE_HF5
)
{
/* wait for hf5 bit */
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_PLX_MBOX0
,
PCXHR_MBOX0_HF5
,
PCXHR_MBOX0_HF5
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_PLX_MBOX0
,
PCXHR_MBOX0_HF5
,
PCXHR_MBOX0_HF5
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"pcxhr_send_it_dsp : TIMEOUT HF5
\n
"
);
snd_printk
(
KERN_ERR
"pcxhr_send_it_dsp : TIMEOUT HF5
\n
"
);
return
err
;
}
}
...
...
@@ -263,7 +277,8 @@ void pcxhr_enable_dsp(struct pcxhr_mgr *mgr)
/*
* load the xilinx image
*/
int
pcxhr_load_xilinx_binary
(
struct
pcxhr_mgr
*
mgr
,
const
struct
firmware
*
xilinx
,
int
second
)
int
pcxhr_load_xilinx_binary
(
struct
pcxhr_mgr
*
mgr
,
const
struct
firmware
*
xilinx
,
int
second
)
{
unsigned
int
i
;
unsigned
int
chipsc
;
...
...
@@ -274,7 +289,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
/* test first xilinx */
chipsc
=
PCXHR_INPL
(
mgr
,
PCXHR_PLX_CHIPSC
);
/* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
/* this bit will always be 1; no possibility to test presence of first xilinx */
/* this bit will always be 1;
* no possibility to test presence of first xilinx
*/
if
(
second
)
{
if
((
chipsc
&
PCXHR_CHIPSC_GPI_USERI
)
==
0
)
{
snd_printk
(
KERN_ERR
"error loading first xilinx
\n
"
);
...
...
@@ -290,7 +307,8 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
data
=
*
image
;
mask
=
0x80
;
while
(
mask
)
{
chipsc
&=
~
(
PCXHR_CHIPSC_DATA_CLK
|
PCXHR_CHIPSC_DATA_IN
);
chipsc
&=
~
(
PCXHR_CHIPSC_DATA_CLK
|
PCXHR_CHIPSC_DATA_IN
);
if
(
data
&
mask
)
chipsc
|=
PCXHR_CHIPSC_DATA_IN
;
PCXHR_OUTPL
(
mgr
,
PCXHR_PLX_CHIPSC
,
chipsc
);
...
...
@@ -330,15 +348,20 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
data
=
dsp
->
data
+
i
;
if
(
i
==
0
)
{
/* test data header consistency */
len
=
(
unsigned
int
)((
data
[
0
]
<<
16
)
+
(
data
[
1
]
<<
8
)
+
data
[
2
]);
if
(
len
&&
dsp
->
size
!=
(
len
+
2
)
*
3
)
len
=
(
unsigned
int
)((
data
[
0
]
<<
16
)
+
(
data
[
1
]
<<
8
)
+
data
[
2
]);
if
(
len
&&
(
dsp
->
size
!=
(
len
+
2
)
*
3
))
return
-
EINVAL
;
}
/* wait DSP ready for new transfer */
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_TIMEOUT_DSP
,
&
dummy
);
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_TIMEOUT_DSP
,
&
dummy
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"dsp loading error at position %d
\n
"
,
i
);
snd_printk
(
KERN_ERR
"dsp loading error at position %d
\n
"
,
i
);
return
err
;
}
/* send host data */
...
...
@@ -357,7 +380,8 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
/*
* load the eeprom image
*/
int
pcxhr_load_eeprom_binary
(
struct
pcxhr_mgr
*
mgr
,
const
struct
firmware
*
eeprom
)
int
pcxhr_load_eeprom_binary
(
struct
pcxhr_mgr
*
mgr
,
const
struct
firmware
*
eeprom
)
{
int
err
;
unsigned
char
reg
;
...
...
@@ -365,7 +389,9 @@ int pcxhr_load_eeprom_binary(struct pcxhr_mgr *mgr, const struct firmware *eepro
/* init value of the ICR register */
reg
=
PCXHR_ICR_HI08_RREQ
|
PCXHR_ICR_HI08_TREQ
|
PCXHR_ICR_HI08_HDRQ
;
if
(
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX0
)
&
PCXHR_MBOX0_BOOT_HERE
)
{
/* no need to load the eeprom binary, but init the HI08 interface */
/* no need to load the eeprom binary,
* but init the HI08 interface
*/
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_ICR
,
reg
|
PCXHR_ICR_HI08_INIT
);
msleep
(
PCXHR_WAIT_DEFAULT
);
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_ICR
,
reg
);
...
...
@@ -429,8 +455,10 @@ int pcxhr_load_dsp_binary(struct pcxhr_mgr *mgr, const struct firmware *dsp)
if
(
err
)
return
err
;
/* wait for chk bit */
return
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_CHK
,
PCXHR_ISR_HI08_CHK
,
PCXHR_TIMEOUT_DSP
,
&
dummy
);
return
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_CHK
,
PCXHR_ISR_HI08_CHK
,
PCXHR_TIMEOUT_DSP
,
&
dummy
);
}
...
...
@@ -443,8 +471,8 @@ struct pcxhr_cmd_info {
/* RMH status type */
enum
{
RMH_SSIZE_FIXED
=
0
,
/* status size fix (st_length = 0..x) */
RMH_SSIZE_ARG
=
1
,
/* status size given in the LSB byte
(used with st_length = 1)
*/
RMH_SSIZE_MASK
=
2
,
/* status size given in bitmask
(used with st_length = 1)
*/
RMH_SSIZE_ARG
=
1
,
/* status size given in the LSB byte */
RMH_SSIZE_MASK
=
2
,
/* status size given in bitmask */
};
/*
...
...
@@ -474,7 +502,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = {
[
CMD_UPDATE_R_BUFFERS
]
=
{
0x840000
,
0
,
RMH_SSIZE_FIXED
},
[
CMD_FORMAT_STREAM_OUT
]
=
{
0x860000
,
0
,
RMH_SSIZE_FIXED
},
[
CMD_FORMAT_STREAM_IN
]
=
{
0x870000
,
0
,
RMH_SSIZE_FIXED
},
[
CMD_STREAM_SAMPLE_COUNT
]
=
{
0x902000
,
2
,
RMH_SSIZE_FIXED
},
/* stat_len = nb_streams * 2 */
[
CMD_STREAM_SAMPLE_COUNT
]
=
{
0x902000
,
2
,
RMH_SSIZE_FIXED
},
[
CMD_AUDIO_LEVEL_ADJUST
]
=
{
0xc22000
,
0
,
RMH_SSIZE_FIXED
},
};
...
...
@@ -524,10 +552,13 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
for
(
i
=
0
;
i
<
rmh
->
stat_len
;
i
++
)
{
/* wait for receiver full */
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"ERROR RMH stat: ISR:RXDF=1 (ISR = %x; i=%d )
\n
"
,
snd_printk
(
KERN_ERR
"ERROR RMH stat: "
"ISR:RXDF=1 (ISR = %x; i=%d )
\n
"
,
reg
,
i
);
return
err
;
}
...
...
@@ -537,10 +568,10 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data
|=
PCXHR_INPB
(
mgr
,
PCXHR_DSP_TXL
);
/* need to update rmh->stat_len on the fly ?? */
if
(
i
==
0
)
{
if
(
!
i
)
{
if
(
rmh
->
dsp_stat
!=
RMH_SSIZE_FIXED
)
{
if
(
rmh
->
dsp_stat
==
RMH_SSIZE_ARG
)
{
rmh
->
stat_len
=
(
u16
)(
data
&
0x0000ff
)
+
1
;
rmh
->
stat_len
=
(
data
&
0x0000ff
)
+
1
;
data
&=
0xffff00
;
}
else
{
/* rmh->dsp_stat == RMH_SSIZE_MASK */
...
...
@@ -562,7 +593,8 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
rmh
->
stat
[
i
]
=
data
;
}
if
(
rmh
->
stat_len
>
max_stat_len
)
{
snd_printdd
(
"PCXHR : rmh->stat_len=%x too big
\n
"
,
rmh
->
stat_len
);
snd_printdd
(
"PCXHR : rmh->stat_len=%x too big
\n
"
,
rmh
->
stat_len
);
rmh
->
stat_len
=
max_stat_len
;
}
return
0
;
...
...
@@ -605,7 +637,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data
&=
0xff7fff
;
/* MASK_1_WORD_COMMAND */
#ifdef CONFIG_SND_DEBUG_VERBOSE
if
(
rmh
->
cmd_idx
<
CMD_LAST_INDEX
)
snd_printdd
(
"MSG cmd[0]=%x (%s)
\n
"
,
data
,
cmd_names
[
rmh
->
cmd_idx
]);
snd_printdd
(
"MSG cmd[0]=%x (%s)
\n
"
,
data
,
cmd_names
[
rmh
->
cmd_idx
]);
#endif
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_TRDY
,
...
...
@@ -619,8 +652,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
if
(
rmh
->
cmd_len
>
1
)
{
/* send length */
data
=
rmh
->
cmd_len
-
1
;
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_ISR_HI08_TRDY
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
if
(
err
)
return
err
;
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_TXH
,
(
data
>>
16
)
&
0xFF
);
...
...
@@ -653,8 +688,10 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
/* test status ISR */
if
(
reg
&
PCXHR_ISR_HI08_ERR
)
{
/* ERROR, wait for receiver full */
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
err
=
pcxhr_check_reg_bit
(
mgr
,
PCXHR_DSP_ISR
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_ISR_HI08_RXDF
,
PCXHR_TIMEOUT_DSP
,
&
reg
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"ERROR RMH: ISR:RXDF=1 (ISR = %x)
\n
"
,
reg
);
return
err
;
...
...
@@ -663,7 +700,8 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
data
=
PCXHR_INPB
(
mgr
,
PCXHR_DSP_TXH
)
<<
16
;
data
|=
PCXHR_INPB
(
mgr
,
PCXHR_DSP_TXM
)
<<
8
;
data
|=
PCXHR_INPB
(
mgr
,
PCXHR_DSP_TXL
);
snd_printk
(
KERN_ERR
"ERROR RMH(%d): 0x%x
\n
"
,
rmh
->
cmd_idx
,
data
);
snd_printk
(
KERN_ERR
"ERROR RMH(%d): 0x%x
\n
"
,
rmh
->
cmd_idx
,
data
);
err
=
-
EINVAL
;
}
else
{
/* read the response data */
...
...
@@ -732,8 +770,9 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh)
static
inline
int
pcxhr_pipes_running
(
struct
pcxhr_mgr
*
mgr
)
{
int
start_mask
=
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX2
);
/* least segnificant 12 bits are the pipe states for the playback audios */
/* next 12 bits are the pipe states for the capture audios
/* least segnificant 12 bits are the pipe states
* for the playback audios
* next 12 bits are the pipe states for the capture audios
* (PCXHR_PIPE_STATE_CAPTURE_OFFSET)
*/
start_mask
&=
0xffffff
;
...
...
@@ -744,7 +783,8 @@ static inline int pcxhr_pipes_running(struct pcxhr_mgr *mgr)
#define PCXHR_PIPE_STATE_CAPTURE_OFFSET 12
#define MAX_WAIT_FOR_DSP 20
static
int
pcxhr_prepair_pipe_start
(
struct
pcxhr_mgr
*
mgr
,
int
audio_mask
,
int
*
retry
)
static
int
pcxhr_prepair_pipe_start
(
struct
pcxhr_mgr
*
mgr
,
int
audio_mask
,
int
*
retry
)
{
struct
pcxhr_rmh
rmh
;
int
err
;
...
...
@@ -766,11 +806,14 @@ static int pcxhr_prepair_pipe_start(struct pcxhr_mgr *mgr, int audio_mask, int *
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"error pipe start (CMD_CAN_START_PIPE) err=%x!
\n
"
,
"error pipe start "
"(CMD_CAN_START_PIPE) err=%x!
\n
"
,
err
);
return
err
;
}
/* if the pipe couldn't be prepaired for start, retry it later */
/* if the pipe couldn't be prepaired for start,
* retry it later
*/
if
(
rmh
.
stat
[
0
]
==
0
)
*
retry
|=
(
1
<<
audio
);
}
...
...
@@ -801,8 +844,8 @@ static int pcxhr_stop_pipes(struct pcxhr_mgr *mgr, int audio_mask)
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"error pipe stop
(CMD_STOP_PIPE) err=%x!
\n
"
,
err
);
"error pipe stop
"
"(CMD_STOP_PIPE) err=%x!
\n
"
,
err
);
return
err
;
}
}
...
...
@@ -822,15 +865,16 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
if
(
audio_mask
&
1
)
{
pcxhr_init_rmh
(
&
rmh
,
CMD_CONF_PIPE
);
if
(
audio
<
PCXHR_PIPE_STATE_CAPTURE_OFFSET
)
pcxhr_set_pipe_cmd_params
(
&
rmh
,
0
,
0
,
0
,
1
<<
audio
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
0
,
0
,
0
,
1
<<
audio
);
else
pcxhr_set_pipe_cmd_params
(
&
rmh
,
1
,
0
,
0
,
1
<<
(
audio
-
PCXHR_PIPE_STATE_CAPTURE_OFFSET
));
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"error pipe start
(CMD_CONF_PIPE) err=%x!
\n
"
,
err
);
"error pipe start
"
"(CMD_CONF_PIPE) err=%x!
\n
"
,
err
);
return
err
;
}
}
...
...
@@ -841,7 +885,9 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
pcxhr_init_rmh
(
&
rmh
,
CMD_SEND_IRQA
);
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
{
snd_printk
(
KERN_ERR
"error pipe start (CMD_SEND_IRQA) err=%x!
\n
"
,
err
);
snd_printk
(
KERN_ERR
"error pipe start (CMD_SEND_IRQA) err=%x!
\n
"
,
err
);
return
err
;
}
return
0
;
...
...
@@ -849,7 +895,8 @@ static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask)
int
pcxhr_set_pipe_state
(
struct
pcxhr_mgr
*
mgr
,
int
playback_mask
,
int
capture_mask
,
int
start
)
int
pcxhr_set_pipe_state
(
struct
pcxhr_mgr
*
mgr
,
int
playback_mask
,
int
capture_mask
,
int
start
)
{
int
state
,
i
,
err
;
int
audio_mask
;
...
...
@@ -858,21 +905,23 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
struct
timeval
my_tv1
,
my_tv2
;
do_gettimeofday
(
&
my_tv1
);
#endif
audio_mask
=
(
playback_mask
|
(
capture_mask
<<
PCXHR_PIPE_STATE_CAPTURE_OFFSET
));
audio_mask
=
(
playback_mask
|
(
capture_mask
<<
PCXHR_PIPE_STATE_CAPTURE_OFFSET
));
/* current pipe state (playback + record) */
state
=
pcxhr_pipes_running
(
mgr
);
snd_printdd
(
"pcxhr_set_pipe_state %s (mask %x current %x)
\n
"
,
start
?
"START"
:
"STOP"
,
audio_mask
,
state
);
if
(
start
)
{
audio_mask
&=
~
state
;
/* start only pipes that are not yet started */
/* start only pipes that are not yet started */
audio_mask
&=
~
state
;
state
=
audio_mask
;
for
(
i
=
0
;
i
<
MAX_WAIT_FOR_DSP
;
i
++
)
{
err
=
pcxhr_prepair_pipe_start
(
mgr
,
state
,
&
state
);
if
(
err
)
return
err
;
if
(
state
==
0
)
break
;
/* success, all pipes prepaired
for start
*/
mdelay
(
1
);
/* otherwise
wait 1 millisecond and retry */
break
;
/* success, all pipes prepaired */
mdelay
(
1
);
/*
wait 1 millisecond and retry */
}
}
else
{
audio_mask
&=
state
;
/* stop only pipes that are started */
...
...
@@ -891,7 +940,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m
if
((
state
&
audio_mask
)
==
(
start
?
audio_mask
:
0
))
break
;
if
(
++
i
>=
MAX_WAIT_FOR_DSP
*
100
)
{
snd_printk
(
KERN_ERR
"error pipe start/stop
(ED_NO_RESPONSE_AT_IRQA)
\n
"
);
snd_printk
(
KERN_ERR
"error pipe start/stop
\n
"
);
return
-
EBUSY
;
}
udelay
(
10
);
/* wait 10 microseconds */
...
...
@@ -918,7 +967,8 @@ int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,
spin_lock_irqsave
(
&
mgr
->
msg_lock
,
flags
);
if
((
mgr
->
io_num_reg_cont
&
mask
)
==
value
)
{
snd_printdd
(
"IO_NUM_REG_CONT mask %x already is set to %x
\n
"
,
mask
,
value
);
snd_printdd
(
"IO_NUM_REG_CONT mask %x already is set to %x
\n
"
,
mask
,
value
);
if
(
changed
)
*
changed
=
0
;
spin_unlock_irqrestore
(
&
mgr
->
msg_lock
,
flags
);
...
...
@@ -971,7 +1021,8 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,
err
=
((
err
>>
12
)
&
0xfff
);
if
(
!
err
)
return
0
;
snd_printdd
(
"CMD_ASYNC : Error %s %s Pipe %d err=%x
\n
"
,
err_src_name
[
err_src
],
snd_printdd
(
"CMD_ASYNC : Error %s %s Pipe %d err=%x
\n
"
,
err_src_name
[
err_src
],
is_capture
?
"Record"
:
"Play"
,
pipe
,
err
);
if
(
err
==
0xe01
)
mgr
->
async_err_stream_xrun
++
;
...
...
@@ -996,6 +1047,13 @@ void pcxhr_msg_tasklet(unsigned long arg)
snd_printdd
(
"TASKLET : PCXHR_IRQ_TIME_CODE event occured
\n
"
);
if
(
mgr
->
src_it_dsp
&
PCXHR_IRQ_NOTIFY
)
snd_printdd
(
"TASKLET : PCXHR_IRQ_NOTIFY event occured
\n
"
);
if
(
mgr
->
src_it_dsp
&
(
PCXHR_IRQ_FREQ_CHANGE
|
PCXHR_IRQ_TIME_CODE
))
{
/* clear events FREQ_CHANGE and TIME_CODE */
pcxhr_init_rmh
(
prmh
,
CMD_TEST_IT
);
err
=
pcxhr_send_msg
(
mgr
,
prmh
);
snd_printdd
(
"CMD_TEST_IT : err=%x, stat=%x
\n
"
,
err
,
prmh
->
stat
[
0
]);
}
if
(
mgr
->
src_it_dsp
&
PCXHR_IRQ_ASYNC
)
{
snd_printdd
(
"TASKLET : PCXHR_IRQ_ASYNC event occured
\n
"
);
...
...
@@ -1005,18 +1063,22 @@ void pcxhr_msg_tasklet(unsigned long arg)
prmh
->
stat_len
=
PCXHR_SIZE_MAX_LONG_STATUS
;
err
=
pcxhr_send_msg
(
mgr
,
prmh
);
if
(
err
)
snd_printk
(
KERN_ERR
"ERROR pcxhr_msg_tasklet=%x;
\n
"
,
err
);
snd_printk
(
KERN_ERR
"ERROR pcxhr_msg_tasklet=%x;
\n
"
,
err
);
i
=
1
;
while
(
i
<
prmh
->
stat_len
)
{
int
nb_audio
=
(
prmh
->
stat
[
i
]
>>
FIELD_SIZE
)
&
MASK_FIRST_FIELD
;
int
nb_stream
=
(
prmh
->
stat
[
i
]
>>
(
2
*
FIELD_SIZE
))
&
MASK_FIRST_FIELD
;
int
nb_audio
=
((
prmh
->
stat
[
i
]
>>
FIELD_SIZE
)
&
MASK_FIRST_FIELD
);
int
nb_stream
=
((
prmh
->
stat
[
i
]
>>
(
2
*
FIELD_SIZE
))
&
MASK_FIRST_FIELD
);
int
pipe
=
prmh
->
stat
[
i
]
&
MASK_FIRST_FIELD
;
int
is_capture
=
prmh
->
stat
[
i
]
&
0x400000
;
u32
err2
;
if
(
prmh
->
stat
[
i
]
&
0x800000
)
{
/* if BIT_END */
snd_printdd
(
"TASKLET : End%sPipe %d
\n
"
,
is_capture
?
"Record"
:
"Play"
,
pipe
);
is_capture
?
"Record"
:
"Play"
,
pipe
);
}
i
++
;
err2
=
prmh
->
stat
[
i
]
?
prmh
->
stat
[
i
]
:
prmh
->
stat
[
i
+
1
];
...
...
@@ -1072,18 +1134,21 @@ static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,
hw_sample_count
+=
(
u_int64_t
)
rmh
.
stat
[
1
];
snd_printdd
(
"stream %c%d : abs samples real(%ld) timer(%ld)
\n
"
,
stream
->
pipe
->
is_capture
?
'C'
:
'P'
,
stream
->
substream
->
number
,
stream
->
pipe
->
is_capture
?
'C'
:
'P'
,
stream
->
substream
->
number
,
(
long
unsigned
int
)
hw_sample_count
,
(
long
unsigned
int
)(
stream
->
timer_abs_periods
+
stream
->
timer_period_frag
+
PCXHR_GRANULARITY
));
stream
->
timer_period_frag
+
mgr
->
granularity
));
return
hw_sample_count
;
}
static
void
pcxhr_update_timer_pos
(
struct
pcxhr_mgr
*
mgr
,
struct
pcxhr_stream
*
stream
,
int
samples_to_add
)
struct
pcxhr_stream
*
stream
,
int
samples_to_add
)
{
if
(
stream
->
substream
&&
(
stream
->
status
==
PCXHR_STREAM_STATUS_RUNNING
))
{
if
(
stream
->
substream
&&
(
stream
->
status
==
PCXHR_STREAM_STATUS_RUNNING
))
{
u_int64_t
new_sample_count
;
int
elapsed
=
0
;
int
hardware_read
=
0
;
...
...
@@ -1092,20 +1157,22 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
if
(
samples_to_add
<
0
)
{
stream
->
timer_is_synced
=
0
;
/* add default if no hardware_read possible */
samples_to_add
=
PCXHR_GRANULARITY
;
samples_to_add
=
mgr
->
granularity
;
}
if
(
!
stream
->
timer_is_synced
)
{
if
(
stream
->
timer_abs_periods
!=
0
||
stream
->
timer_period_frag
+
PCXHR_GRANULARITY
>=
runtime
->
period_size
)
{
new_sample_count
=
pcxhr_stream_read_position
(
mgr
,
stream
);
if
((
stream
->
timer_abs_periods
!=
0
)
||
((
stream
->
timer_period_frag
+
samples_to_add
)
>=
runtime
->
period_size
))
{
new_sample_count
=
pcxhr_stream_read_position
(
mgr
,
stream
);
hardware_read
=
1
;
if
(
new_sample_count
>=
PCXHR_GRANULARITY_MIN
)
{
/* sub security offset because of jitter and
* finer granularity of dsp time (MBOX4)
if
(
new_sample_count
>=
mgr
->
granularity
)
{
/* sub security offset because of
* jitter and finer granularity of
* dsp time (MBOX4)
*/
new_sample_count
-=
PCXHR_GRANULARITY_MIN
;
new_sample_count
-=
mgr
->
granularity
;
stream
->
timer_is_synced
=
1
;
}
}
...
...
@@ -1128,12 +1195,15 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
stream
->
timer_buf_periods
=
0
;
stream
->
timer_abs_periods
=
new_elapse_pos
;
}
if
(
new_sample_count
>=
stream
->
timer_abs_periods
)
stream
->
timer_period_frag
=
(
u_int32_t
)(
new_sample_count
-
if
(
new_sample_count
>=
stream
->
timer_abs_periods
)
{
stream
->
timer_period_frag
=
(
u_int32_t
)(
new_sample_count
-
stream
->
timer_abs_periods
);
else
snd_printk
(
KERN_ERR
"ERROR new_sample_count too small ??? %lx
\n
"
,
}
else
{
snd_printk
(
KERN_ERR
"ERROR new_sample_count too small ??? %ld
\n
"
,
(
long
unsigned
int
)
new_sample_count
);
}
if
(
elapsed
)
{
spin_unlock
(
&
mgr
->
lock
);
...
...
@@ -1143,7 +1213,6 @@ static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,
}
}
irqreturn_t
pcxhr_interrupt
(
int
irq
,
void
*
dev_id
)
{
struct
pcxhr_mgr
*
mgr
=
dev_id
;
...
...
@@ -1156,7 +1225,8 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
reg
=
PCXHR_INPL
(
mgr
,
PCXHR_PLX_IRQCS
);
if
(
!
(
reg
&
PCXHR_IRQCS_ACTIVE_PCIDB
))
{
spin_unlock
(
&
mgr
->
lock
);
return
IRQ_NONE
;
/* this device did not cause the interrupt */
/* this device did not cause the interrupt */
return
IRQ_NONE
;
}
/* clear interrupt */
...
...
@@ -1167,10 +1237,12 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
if
(
reg
&
PCXHR_IRQ_TIMER
)
{
int
timer_toggle
=
reg
&
PCXHR_IRQ_TIMER
;
/* is a 24 bit counter */
int
dsp_time_new
=
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX4
)
&
PCXHR_DSP_TIME_MASK
;
int
dsp_time_new
=
PCXHR_INPL
(
mgr
,
PCXHR_PLX_MBOX4
)
&
PCXHR_DSP_TIME_MASK
;
int
dsp_time_diff
=
dsp_time_new
-
mgr
->
dsp_time_last
;
if
(
dsp_time_diff
<
0
&&
mgr
->
dsp_time_last
!=
PCXHR_DSP_TIME_INVALID
)
{
if
((
dsp_time_diff
<
0
)
&&
(
mgr
->
dsp_time_last
!=
PCXHR_DSP_TIME_INVALID
))
{
snd_printdd
(
"ERROR DSP TIME old(%d) new(%d) -> "
"resynchronize all streams
\n
"
,
mgr
->
dsp_time_last
,
dsp_time_new
);
...
...
@@ -1178,40 +1250,49 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id)
}
#ifdef CONFIG_SND_DEBUG_VERBOSE
if
(
dsp_time_diff
==
0
)
snd_printdd
(
"ERROR DSP TIME NO DIFF time(%d)
\n
"
,
dsp_time_new
);
else
if
(
dsp_time_diff
>=
(
2
*
PCXHR_GRANULARITY
))
snd_printdd
(
"ERROR DSP TIME NO DIFF time(%d)
\n
"
,
dsp_time_new
);
else
if
(
dsp_time_diff
>=
(
2
*
mgr
->
granularity
))
snd_printdd
(
"ERROR DSP TIME TOO BIG old(%d) add(%d)
\n
"
,
mgr
->
dsp_time_last
,
dsp_time_new
-
mgr
->
dsp_time_last
);
mgr
->
dsp_time_last
,
dsp_time_new
-
mgr
->
dsp_time_last
);
else
if
(
dsp_time_diff
%
mgr
->
granularity
)
snd_printdd
(
"ERROR DSP TIME increased by %d
\n
"
,
dsp_time_diff
);
#endif
mgr
->
dsp_time_last
=
dsp_time_new
;
if
(
timer_toggle
==
mgr
->
timer_toggle
)
if
(
timer_toggle
==
mgr
->
timer_toggle
)
{
snd_printdd
(
"ERROR TIMER TOGGLE
\n
"
);
mgr
->
dsp_time_err
++
;
}
mgr
->
timer_toggle
=
timer_toggle
;
reg
&=
~
PCXHR_IRQ_TIMER
;
for
(
i
=
0
;
i
<
mgr
->
num_cards
;
i
++
)
{
chip
=
mgr
->
chip
[
i
];
for
(
j
=
0
;
j
<
chip
->
nb_streams_capt
;
j
++
)
pcxhr_update_timer_pos
(
mgr
,
&
chip
->
capture_stream
[
j
],
pcxhr_update_timer_pos
(
mgr
,
&
chip
->
capture_stream
[
j
],
dsp_time_diff
);
}
for
(
i
=
0
;
i
<
mgr
->
num_cards
;
i
++
)
{
chip
=
mgr
->
chip
[
i
];
for
(
j
=
0
;
j
<
chip
->
nb_streams_play
;
j
++
)
pcxhr_update_timer_pos
(
mgr
,
&
chip
->
playback_stream
[
j
],
pcxhr_update_timer_pos
(
mgr
,
&
chip
->
playback_stream
[
j
],
dsp_time_diff
);
}
}
/* other irq's handled in the tasklet */
if
(
reg
&
PCXHR_IRQ_MASK
)
{
/* as we didn't request any notifications, some kind of xrun error
*
will probably occured
if
(
reg
&
PCXHR_IRQ_ASYNC
)
{
/* as we didn't request any async notifications,
* some kind of xrun error
will probably occured
*/
/* better resynchronize all streams next interrupt : */
mgr
->
dsp_time_last
=
PCXHR_DSP_TIME_INVALID
;
}
mgr
->
src_it_dsp
=
reg
;
tasklet_schedule
(
&
mgr
->
msg_taskq
);
}
...
...
sound/pci/pcxhr/pcxhr_core.h
View file @
a9c3c7e0
...
...
@@ -65,7 +65,7 @@ enum {
CMD_RESYNC_AUDIO_INPUTS
,
/* cmd_len = 1 stat_len = 0 */
CMD_GET_DSP_RESOURCES
,
/* cmd_len = 1 stat_len = 4 */
CMD_SET_TIMER_INTERRUPT
,
/* cmd_len = 1 stat_len = 0 */
CMD_RES_PIPE
,
/* cmd_len
=
2 stat_len = 0 */
CMD_RES_PIPE
,
/* cmd_len
>=
2 stat_len = 0 */
CMD_FREE_PIPE
,
/* cmd_len = 1 stat_len = 0 */
CMD_CONF_PIPE
,
/* cmd_len = 2 stat_len = 0 */
CMD_STOP_PIPE
,
/* cmd_len = 1 stat_len = 0 */
...
...
@@ -96,6 +96,8 @@ void pcxhr_init_rmh(struct pcxhr_rmh *rmh, int cmd);
void
pcxhr_set_pipe_cmd_params
(
struct
pcxhr_rmh
*
rmh
,
int
capture
,
unsigned
int
param1
,
unsigned
int
param2
,
unsigned
int
param3
);
#define DSP_EXT_CMD_SET(x) (x->dsp_version > 0x012800)
/*
send the rmh
*/
...
...
@@ -110,6 +112,7 @@ int pcxhr_send_msg(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh);
#define IO_NUM_REG_STATUS 5
#define IO_NUM_REG_CUER 10
#define IO_NUM_UER_CHIP_REG 11
#define IO_NUM_REG_CONFIG_SRC 12
#define IO_NUM_REG_OUT_ANA_LEVEL 20
#define IO_NUM_REG_IN_ANA_LEVEL 21
...
...
sound/pci/pcxhr/pcxhr_hwdep.c
View file @
a9c3c7e0
...
...
@@ -31,6 +31,7 @@
#include "pcxhr_mixer.h"
#include "pcxhr_hwdep.h"
#include "pcxhr_core.h"
#include "pcxhr_mix22.h"
#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
...
...
@@ -40,10 +41,10 @@
#endif
static
int
pcxhr_sub_init
(
struct
pcxhr_mgr
*
mgr
);
/*
* get basic information and init pcxhr card
*/
static
int
pcxhr_init_board
(
struct
pcxhr_mgr
*
mgr
)
{
int
err
;
...
...
@@ -68,7 +69,7 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
if
((
rmh
.
stat
[
0
]
&
MASK_FIRST_FIELD
)
!=
mgr
->
playback_chips
*
2
)
return
-
EINVAL
;
/* test 8 or 2 phys in */
if
(((
rmh
.
stat
[
0
]
>>
(
2
*
FIELD_SIZE
))
&
MASK_FIRST_FIELD
)
!=
if
(((
rmh
.
stat
[
0
]
>>
(
2
*
FIELD_SIZE
))
&
MASK_FIRST_FIELD
)
<
mgr
->
capture_chips
*
2
)
return
-
EINVAL
;
/* test max nb substream per board */
...
...
@@ -77,20 +78,34 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
/* test max nb substream per pipe */
if
(((
rmh
.
stat
[
1
]
>>
7
)
&
0x5F
)
<
PCXHR_PLAYBACK_STREAMS
)
return
-
EINVAL
;
snd_printdd
(
"supported formats : playback=%x capture=%x
\n
"
,
rmh
.
stat
[
2
],
rmh
.
stat
[
3
]);
pcxhr_init_rmh
(
&
rmh
,
CMD_VERSION
);
/* firmware num for DSP */
rmh
.
cmd
[
0
]
|=
mgr
->
firmware_num
;
/* transfer granularity in samples (should be multiple of 48) */
rmh
.
cmd
[
1
]
=
(
1
<<
23
)
+
PCXHR_GRANULARITY
;
rmh
.
cmd
[
1
]
=
(
1
<<
23
)
+
mgr
->
granularity
;
rmh
.
cmd_len
=
2
;
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
)
return
err
;
snd_printdd
(
"PCXHR DSP version is %d.%d.%d
\n
"
,
(
rmh
.
stat
[
0
]
>>
16
)
&
0xff
,
(
rmh
.
stat
[
0
]
>>
8
)
&
0xff
,
rmh
.
stat
[
0
]
&
0xff
);
snd_printdd
(
"PCXHR DSP version is %d.%d.%d
\n
"
,
(
rmh
.
stat
[
0
]
>>
16
)
&
0xff
,
(
rmh
.
stat
[
0
]
>>
8
)
&
0xff
,
rmh
.
stat
[
0
]
&
0xff
);
mgr
->
dsp_version
=
rmh
.
stat
[
0
];
if
(
mgr
->
is_hr_stereo
)
err
=
hr222_sub_init
(
mgr
);
else
err
=
pcxhr_sub_init
(
mgr
);
return
err
;
}
static
int
pcxhr_sub_init
(
struct
pcxhr_mgr
*
mgr
)
{
int
err
;
struct
pcxhr_rmh
rmh
;
/* get options */
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_STATUS
;
...
...
@@ -100,20 +115,22 @@ static int pcxhr_init_board(struct pcxhr_mgr *mgr)
if
(
err
)
return
err
;
if
((
rmh
.
stat
[
1
]
&
REG_STATUS_OPT_DAUGHTER_MASK
)
==
REG_STATUS_OPT_ANALOG_BOARD
)
mgr
->
board_has_analog
=
1
;
/* analog addon board available */
else
/* analog addon board not available -> no support for instance */
return
-
EINVAL
;
if
((
rmh
.
stat
[
1
]
&
REG_STATUS_OPT_DAUGHTER_MASK
)
==
REG_STATUS_OPT_ANALOG_BOARD
)
mgr
->
board_has_analog
=
1
;
/* analog addon board found */
/* unmute inputs */
err
=
pcxhr_write_io_num_reg_cont
(
mgr
,
REG_CONT_UNMUTE_INPUTS
,
REG_CONT_UNMUTE_INPUTS
,
NULL
);
if
(
err
)
return
err
;
/* unmute outputs */
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
/* a write to IO_NUM_REG_MUTE_OUT mutes! */
/* unmute outputs
(a write to IO_NUM_REG_MUTE_OUT mutes!)
*/
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_MUTE_OUT
;
if
(
DSP_EXT_CMD_SET
(
mgr
))
{
rmh
.
cmd
[
1
]
=
1
;
/* unmute digital plugs */
rmh
.
cmd_len
=
2
;
}
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
return
err
;
}
...
...
@@ -124,19 +141,25 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_DSP_MAIN_INDEX
))
{
/* mute outputs */
if
(
!
mgr
->
is_hr_stereo
)
{
/* a read to IO_NUM_REG_MUTE_OUT register unmutes! */
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_WRITE
);
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_MUTE_OUT
;
pcxhr_send_msg
(
mgr
,
&
rmh
);
/* mute inputs */
pcxhr_write_io_num_reg_cont
(
mgr
,
REG_CONT_UNMUTE_INPUTS
,
0
,
NULL
);
pcxhr_write_io_num_reg_cont
(
mgr
,
REG_CONT_UNMUTE_INPUTS
,
0
,
NULL
);
}
/* stereo cards mute with reset of dsp */
}
/* reset pcxhr dsp */
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_DSP_EPRM_INDEX
))
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_DSP_EPRM_INDEX
))
pcxhr_reset_dsp
(
mgr
);
/* reset second xilinx */
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_XLX_COM_INDEX
))
if
(
mgr
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_XLX_COM_INDEX
))
{
pcxhr_reset_xilinx_com
(
mgr
);
mgr
->
dsp_loaded
=
1
;
}
return
;
}
...
...
@@ -144,7 +167,8 @@ void pcxhr_reset_board(struct pcxhr_mgr *mgr)
/*
* allocate a playback/capture pipe (pcmp0/pcmc0)
*/
static
int
pcxhr_dsp_allocate_pipe
(
struct
pcxhr_mgr
*
mgr
,
struct
pcxhr_pipe
*
pipe
,
static
int
pcxhr_dsp_allocate_pipe
(
struct
pcxhr_mgr
*
mgr
,
struct
pcxhr_pipe
*
pipe
,
int
is_capture
,
int
pin
)
{
int
stream_count
,
audio_count
;
...
...
@@ -161,15 +185,23 @@ static int pcxhr_dsp_allocate_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pi
stream_count
=
PCXHR_PLAYBACK_STREAMS
;
audio_count
=
2
;
/* always stereo */
}
snd_printdd
(
"snd_add_ref_pipe pin(%d) pcm%c0
\n
"
,
pin
,
is_capture
?
'c'
:
'p'
);
snd_printdd
(
"snd_add_ref_pipe pin(%d) pcm%c0
\n
"
,
pin
,
is_capture
?
'c'
:
'p'
);
pipe
->
is_capture
=
is_capture
;
pipe
->
first_audio
=
pin
;
/* define pipe (P_PCM_ONLY_MASK (0x020000) is not necessary) */
pcxhr_init_rmh
(
&
rmh
,
CMD_RES_PIPE
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
pin
,
audio_count
,
stream_count
);
pcxhr_set_pipe_cmd_params
(
&
rmh
,
is_capture
,
pin
,
audio_count
,
stream_count
);
rmh
.
cmd
[
1
]
|=
0x020000
;
/* add P_PCM_ONLY_MASK */
if
(
DSP_EXT_CMD_SET
(
mgr
))
{
/* add channel mask to command */
rmh
.
cmd
[
rmh
.
cmd_len
++
]
=
(
audio_count
==
1
)
?
0x01
:
0x03
;
}
err
=
pcxhr_send_msg
(
mgr
,
&
rmh
);
if
(
err
<
0
)
{
snd_printk
(
KERN_ERR
"error pipe allocation (CMD_RES_PIPE) err=%x!
\n
"
,
err
);
snd_printk
(
KERN_ERR
"error pipe allocation "
"(CMD_RES_PIPE) err=%x!
\n
"
,
err
);
return
err
;
}
pipe
->
status
=
PCXHR_PIPE_DEFINED
;
...
...
@@ -199,10 +231,12 @@ static int pcxhr_dsp_free_pipe( struct pcxhr_mgr *mgr, struct pcxhr_pipe *pipe)
snd_printk(KERN_ERR "error stopping pipe!\n");
/* release the pipe */
pcxhr_init_rmh(&rmh, CMD_FREE_PIPE);
pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio, 0, 0);
pcxhr_set_pipe_cmd_params(&rmh, pipe->is_capture, pipe->first_audio,
0, 0);
err = pcxhr_send_msg(mgr, &rmh);
if (err < 0)
snd_printk(KERN_ERR "error pipe release (CMD_FREE_PIPE) err(%x)\n", err);
snd_printk(KERN_ERR "error pipe release "
"(CMD_FREE_PIPE) err(%x)\n", err);
pipe->status = PCXHR_PIPE_UNDEFINED;
return err;
}
...
...
@@ -248,15 +282,16 @@ static int pcxhr_start_pipes(struct pcxhr_mgr *mgr)
for
(
i
=
0
;
i
<
mgr
->
num_cards
;
i
++
)
{
chip
=
mgr
->
chip
[
i
];
if
(
chip
->
nb_streams_play
)
playback_mask
|=
(
1
<<
chip
->
playback_pipe
.
first_audio
)
;
playback_mask
|=
1
<<
chip
->
playback_pipe
.
first_audio
;
for
(
j
=
0
;
j
<
chip
->
nb_streams_capt
;
j
++
)
capture_mask
|=
(
1
<<
chip
->
capture_pipe
[
j
].
first_audio
)
;
capture_mask
|=
1
<<
chip
->
capture_pipe
[
j
].
first_audio
;
}
return
pcxhr_set_pipe_state
(
mgr
,
playback_mask
,
capture_mask
,
1
);
}
static
int
pcxhr_dsp_load
(
struct
pcxhr_mgr
*
mgr
,
int
index
,
const
struct
firmware
*
dsp
)
static
int
pcxhr_dsp_load
(
struct
pcxhr_mgr
*
mgr
,
int
index
,
const
struct
firmware
*
dsp
)
{
int
err
,
card_index
;
...
...
@@ -330,22 +365,33 @@ static int pcxhr_dsp_load(struct pcxhr_mgr *mgr, int index, const struct firmwar
int
pcxhr_setup_firmware
(
struct
pcxhr_mgr
*
mgr
)
{
static
char
*
fw_files
[
5
]
=
{
"xi_1_882.dat"
,
"xc_1_882.dat"
,
"e321_512.e56"
,
"b321_512.b56"
,
"d321_512.d56"
static
char
*
fw_files
[][
5
]
=
{
[
0
]
=
{
"xlxint.dat"
,
"xlxc882hr.dat"
,
"dspe882.e56"
,
"dspb882hr.b56"
,
"dspd882.d56"
},
[
1
]
=
{
"xlxint.dat"
,
"xlxc882e.dat"
,
"dspe882.e56"
,
"dspb882e.b56"
,
"dspd882.d56"
},
[
2
]
=
{
"xlxint.dat"
,
"xlxc1222hr.dat"
,
"dspe882.e56"
,
"dspb1222hr.b56"
,
"dspd1222.d56"
},
[
3
]
=
{
"xlxint.dat"
,
"xlxc1222e.dat"
,
"dspe882.e56"
,
"dspb1222e.b56"
,
"dspd1222.d56"
},
[
4
]
=
{
NULL
,
"xlxc222.dat"
,
"dspe924.e56"
,
"dspb924.b56"
,
"dspd222.d56"
},
[
5
]
=
{
NULL
,
"xlxc924.dat"
,
"dspe924.e56"
,
"dspb924.b56"
,
"dspd222.d56"
},
};
char
path
[
32
];
const
struct
firmware
*
fw_entry
;
int
i
,
err
;
int
fw_set
=
mgr
->
fw_file_set
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
fw_files
);
i
++
)
{
sprintf
(
path
,
"pcxhr/%s"
,
fw_files
[
i
]);
for
(
i
=
0
;
i
<
5
;
i
++
)
{
if
(
!
fw_files
[
fw_set
][
i
])
continue
;
sprintf
(
path
,
"pcxhr/%s"
,
fw_files
[
fw_set
][
i
]);
if
(
request_firmware
(
&
fw_entry
,
path
,
&
mgr
->
pci
->
dev
))
{
snd_printk
(
KERN_ERR
"pcxhr: can't load firmware %s
\n
"
,
path
);
snd_printk
(
KERN_ERR
"pcxhr: can't load firmware %s
\n
"
,
path
);
return
-
ENOENT
;
}
/* fake hwdep dsp record */
...
...
@@ -358,11 +404,26 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
return
0
;
}
MODULE_FIRMWARE
(
"pcxhr/xi_1_882.dat"
);
MODULE_FIRMWARE
(
"pcxhr/xc_1_882.dat"
);
MODULE_FIRMWARE
(
"pcxhr/e321_512.e56"
);
MODULE_FIRMWARE
(
"pcxhr/b321_512.b56"
);
MODULE_FIRMWARE
(
"pcxhr/d321_512.d56"
);
MODULE_FIRMWARE
(
"pcxhr/xlxint.dat"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc882hr.dat"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc882e.dat"
);
MODULE_FIRMWARE
(
"pcxhr/dspe882.e56"
);
MODULE_FIRMWARE
(
"pcxhr/dspb882hr.b56"
);
MODULE_FIRMWARE
(
"pcxhr/dspb882e.b56"
);
MODULE_FIRMWARE
(
"pcxhr/dspd882.d56"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc1222hr.dat"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc1222e.dat"
);
MODULE_FIRMWARE
(
"pcxhr/dspb1222hr.b56"
);
MODULE_FIRMWARE
(
"pcxhr/dspb1222e.b56"
);
MODULE_FIRMWARE
(
"pcxhr/dspd1222.d56"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc222.dat"
);
MODULE_FIRMWARE
(
"pcxhr/xlxc924.dat"
);
MODULE_FIRMWARE
(
"pcxhr/dspe924.e56"
);
MODULE_FIRMWARE
(
"pcxhr/dspb924.b56"
);
MODULE_FIRMWARE
(
"pcxhr/dspd222.d56"
);
#else
/* old style firmware loading */
...
...
@@ -373,7 +434,8 @@ MODULE_FIRMWARE("pcxhr/d321_512.d56");
static
int
pcxhr_hwdep_dsp_status
(
struct
snd_hwdep
*
hw
,
struct
snd_hwdep_dsp_status
*
info
)
{
strcpy
(
info
->
id
,
"pcxhr"
);
struct
pcxhr_mgr
*
mgr
=
hw
->
private_data
;
sprintf
(
info
->
id
,
"pcxhr%d"
,
mgr
->
fw_file_set
);
info
->
num_dsps
=
PCXHR_FIRMWARE_FILES_MAX_INDEX
;
if
(
hw
->
dsp_loaded
&
(
1
<<
PCXHR_FIRMWARE_DSP_MAIN_INDEX
))
...
...
@@ -393,8 +455,8 @@ static int pcxhr_hwdep_dsp_load(struct snd_hwdep *hw,
fw
.
size
=
dsp
->
length
;
fw
.
data
=
vmalloc
(
fw
.
size
);
if
(
!
fw
.
data
)
{
snd_printk
(
KERN_ERR
"pcxhr: cannot allocate dsp image
(%lu bytes)
\n
"
,
(
unsigned
long
)
fw
.
size
);
snd_printk
(
KERN_ERR
"pcxhr: cannot allocate dsp image
"
"(%lu bytes)
\n
"
,
(
unsigned
long
)
fw
.
size
);
return
-
ENOMEM
;
}
if
(
copy_from_user
((
void
*
)
fw
.
data
,
dsp
->
image
,
dsp
->
length
))
{
...
...
@@ -424,8 +486,11 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
int
err
;
struct
snd_hwdep
*
hw
;
/* only create hwdep interface for first cardX (see "index" module parameter)*/
if
((
err
=
snd_hwdep_new
(
mgr
->
chip
[
0
]
->
card
,
PCXHR_HWDEP_ID
,
0
,
&
hw
))
<
0
)
/* only create hwdep interface for first cardX
* (see "index" module parameter)
*/
err
=
snd_hwdep_new
(
mgr
->
chip
[
0
]
->
card
,
PCXHR_HWDEP_ID
,
0
,
&
hw
);
if
(
err
<
0
)
return
err
;
hw
->
iface
=
SNDRV_HWDEP_IFACE_PCXHR
;
...
...
@@ -435,10 +500,13 @@ int pcxhr_setup_firmware(struct pcxhr_mgr *mgr)
hw
->
ops
.
dsp_status
=
pcxhr_hwdep_dsp_status
;
hw
->
ops
.
dsp_load
=
pcxhr_hwdep_dsp_load
;
hw
->
exclusive
=
1
;
/* stereo cards don't need fw_file_0 -> dsp_loaded = 1 */
hw
->
dsp_loaded
=
mgr
->
is_hr_stereo
?
1
:
0
;
mgr
->
dsp_loaded
=
0
;
sprintf
(
hw
->
name
,
PCXHR_HWDEP_ID
);
if
((
err
=
snd_card_register
(
mgr
->
chip
[
0
]
->
card
))
<
0
)
err
=
snd_card_register
(
mgr
->
chip
[
0
]
->
card
);
if
(
err
<
0
)
return
err
;
return
0
;
}
...
...
sound/pci/pcxhr/pcxhr_mix22.c
0 → 100644
View file @
a9c3c7e0
/*
* Driver for Digigram pcxhr compatible soundcards
*
* mixer interface for stereo cards
*
* Copyright (c) 2004 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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/delay.h>
#include <linux/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/tlv.h>
#include <sound/asoundef.h>
#include "pcxhr.h"
#include "pcxhr_core.h"
#include "pcxhr_mix22.h"
/* registers used on the DSP and Xilinx (port 2) : HR stereo cards only */
#define PCXHR_DSP_RESET 0x20
#define PCXHR_XLX_CFG 0x24
#define PCXHR_XLX_RUER 0x28
#define PCXHR_XLX_DATA 0x2C
#define PCXHR_XLX_STATUS 0x30
#define PCXHR_XLX_LOFREQ 0x34
#define PCXHR_XLX_HIFREQ 0x38
#define PCXHR_XLX_CSUER 0x3C
#define PCXHR_XLX_SELMIC 0x40
#define PCXHR_DSP 2
/* byte access only ! */
#define PCXHR_INPB(mgr, x) inb((mgr)->port[PCXHR_DSP] + (x))
#define PCXHR_OUTPB(mgr, x, data) outb((data), (mgr)->port[PCXHR_DSP] + (x))
/* values for PCHR_DSP_RESET register */
#define PCXHR_DSP_RESET_DSP 0x01
#define PCXHR_DSP_RESET_MUTE 0x02
#define PCXHR_DSP_RESET_CODEC 0x08
/* values for PCHR_XLX_CFG register */
#define PCXHR_CFG_SYNCDSP_MASK 0x80
#define PCXHR_CFG_DEPENDENCY_MASK 0x60
#define PCXHR_CFG_INDEPENDANT_SEL 0x00
#define PCXHR_CFG_MASTER_SEL 0x40
#define PCXHR_CFG_SLAVE_SEL 0x20
#define PCXHR_CFG_DATA_UER1_SEL_MASK 0x10
/* 0 (UER0), 1(UER1) */
#define PCXHR_CFG_DATAIN_SEL_MASK 0x08
/* 0 (ana), 1 (UER) */
#define PCXHR_CFG_SRC_MASK 0x04
/* 0 (Bypass), 1 (SRC Actif) */
#define PCXHR_CFG_CLOCK_UER1_SEL_MASK 0x02
/* 0 (UER0), 1(UER1) */
#define PCXHR_CFG_CLOCKIN_SEL_MASK 0x01
/* 0 (internal), 1 (AES/EBU) */
/* values for PCHR_XLX_DATA register */
#define PCXHR_DATA_CODEC 0x80
#define AKM_POWER_CONTROL_CMD 0xA007
#define AKM_RESET_ON_CMD 0xA100
#define AKM_RESET_OFF_CMD 0xA103
#define AKM_CLOCK_INF_55K_CMD 0xA240
#define AKM_CLOCK_SUP_55K_CMD 0xA24D
#define AKM_MUTE_CMD 0xA38D
#define AKM_UNMUTE_CMD 0xA30D
#define AKM_LEFT_LEVEL_CMD 0xA600
#define AKM_RIGHT_LEVEL_CMD 0xA700
/* values for PCHR_XLX_STATUS register - READ */
#define PCXHR_STAT_SRC_LOCK 0x01
#define PCXHR_STAT_LEVEL_IN 0x02
#define PCXHR_STAT_MIC_CAPS 0x10
/* values for PCHR_XLX_STATUS register - WRITE */
#define PCXHR_STAT_FREQ_SYNC_MASK 0x01
#define PCXHR_STAT_FREQ_UER1_MASK 0x02
#define PCXHR_STAT_FREQ_SAVE_MASK 0x80
/* values for PCHR_XLX_CSUER register */
#define PCXHR_SUER1_BIT_U_READ_MASK 0x80
#define PCXHR_SUER1_BIT_C_READ_MASK 0x40
#define PCXHR_SUER1_DATA_PRESENT_MASK 0x20
#define PCXHR_SUER1_CLOCK_PRESENT_MASK 0x10
#define PCXHR_SUER_BIT_U_READ_MASK 0x08
#define PCXHR_SUER_BIT_C_READ_MASK 0x04
#define PCXHR_SUER_DATA_PRESENT_MASK 0x02
#define PCXHR_SUER_CLOCK_PRESENT_MASK 0x01
#define PCXHR_SUER_BIT_U_WRITE_MASK 0x02
#define PCXHR_SUER_BIT_C_WRITE_MASK 0x01
/* values for PCXHR_XLX_SELMIC register - WRITE */
#define PCXHR_SELMIC_PREAMPLI_OFFSET 2
#define PCXHR_SELMIC_PREAMPLI_MASK 0x0C
#define PCXHR_SELMIC_PHANTOM_ALIM 0x80
static
const
unsigned
char
g_hr222_p_level
[]
=
{
0x00
,
/* [000] -49.5 dB: AKM[000] = -1.#INF dB (mute) */
0x01
,
/* [001] -49.0 dB: AKM[001] = -48.131 dB (diff=0.86920 dB) */
0x01
,
/* [002] -48.5 dB: AKM[001] = -48.131 dB (diff=0.36920 dB) */
0x01
,
/* [003] -48.0 dB: AKM[001] = -48.131 dB (diff=0.13080 dB) */
0x01
,
/* [004] -47.5 dB: AKM[001] = -48.131 dB (diff=0.63080 dB) */
0x01
,
/* [005] -46.5 dB: AKM[001] = -48.131 dB (diff=1.63080 dB) */
0x01
,
/* [006] -47.0 dB: AKM[001] = -48.131 dB (diff=1.13080 dB) */
0x01
,
/* [007] -46.0 dB: AKM[001] = -48.131 dB (diff=2.13080 dB) */
0x01
,
/* [008] -45.5 dB: AKM[001] = -48.131 dB (diff=2.63080 dB) */
0x02
,
/* [009] -45.0 dB: AKM[002] = -42.110 dB (diff=2.88980 dB) */
0x02
,
/* [010] -44.5 dB: AKM[002] = -42.110 dB (diff=2.38980 dB) */
0x02
,
/* [011] -44.0 dB: AKM[002] = -42.110 dB (diff=1.88980 dB) */
0x02
,
/* [012] -43.5 dB: AKM[002] = -42.110 dB (diff=1.38980 dB) */
0x02
,
/* [013] -43.0 dB: AKM[002] = -42.110 dB (diff=0.88980 dB) */
0x02
,
/* [014] -42.5 dB: AKM[002] = -42.110 dB (diff=0.38980 dB) */
0x02
,
/* [015] -42.0 dB: AKM[002] = -42.110 dB (diff=0.11020 dB) */
0x02
,
/* [016] -41.5 dB: AKM[002] = -42.110 dB (diff=0.61020 dB) */
0x02
,
/* [017] -41.0 dB: AKM[002] = -42.110 dB (diff=1.11020 dB) */
0x02
,
/* [018] -40.5 dB: AKM[002] = -42.110 dB (diff=1.61020 dB) */
0x03
,
/* [019] -40.0 dB: AKM[003] = -38.588 dB (diff=1.41162 dB) */
0x03
,
/* [020] -39.5 dB: AKM[003] = -38.588 dB (diff=0.91162 dB) */
0x03
,
/* [021] -39.0 dB: AKM[003] = -38.588 dB (diff=0.41162 dB) */
0x03
,
/* [022] -38.5 dB: AKM[003] = -38.588 dB (diff=0.08838 dB) */
0x03
,
/* [023] -38.0 dB: AKM[003] = -38.588 dB (diff=0.58838 dB) */
0x03
,
/* [024] -37.5 dB: AKM[003] = -38.588 dB (diff=1.08838 dB) */
0x04
,
/* [025] -37.0 dB: AKM[004] = -36.090 dB (diff=0.91040 dB) */
0x04
,
/* [026] -36.5 dB: AKM[004] = -36.090 dB (diff=0.41040 dB) */
0x04
,
/* [027] -36.0 dB: AKM[004] = -36.090 dB (diff=0.08960 dB) */
0x04
,
/* [028] -35.5 dB: AKM[004] = -36.090 dB (diff=0.58960 dB) */
0x05
,
/* [029] -35.0 dB: AKM[005] = -34.151 dB (diff=0.84860 dB) */
0x05
,
/* [030] -34.5 dB: AKM[005] = -34.151 dB (diff=0.34860 dB) */
0x05
,
/* [031] -34.0 dB: AKM[005] = -34.151 dB (diff=0.15140 dB) */
0x05
,
/* [032] -33.5 dB: AKM[005] = -34.151 dB (diff=0.65140 dB) */
0x06
,
/* [033] -33.0 dB: AKM[006] = -32.568 dB (diff=0.43222 dB) */
0x06
,
/* [034] -32.5 dB: AKM[006] = -32.568 dB (diff=0.06778 dB) */
0x06
,
/* [035] -32.0 dB: AKM[006] = -32.568 dB (diff=0.56778 dB) */
0x07
,
/* [036] -31.5 dB: AKM[007] = -31.229 dB (diff=0.27116 dB) */
0x07
,
/* [037] -31.0 dB: AKM[007] = -31.229 dB (diff=0.22884 dB) */
0x08
,
/* [038] -30.5 dB: AKM[008] = -30.069 dB (diff=0.43100 dB) */
0x08
,
/* [039] -30.0 dB: AKM[008] = -30.069 dB (diff=0.06900 dB) */
0x09
,
/* [040] -29.5 dB: AKM[009] = -29.046 dB (diff=0.45405 dB) */
0x09
,
/* [041] -29.0 dB: AKM[009] = -29.046 dB (diff=0.04595 dB) */
0x0a
,
/* [042] -28.5 dB: AKM[010] = -28.131 dB (diff=0.36920 dB) */
0x0a
,
/* [043] -28.0 dB: AKM[010] = -28.131 dB (diff=0.13080 dB) */
0x0b
,
/* [044] -27.5 dB: AKM[011] = -27.303 dB (diff=0.19705 dB) */
0x0b
,
/* [045] -27.0 dB: AKM[011] = -27.303 dB (diff=0.30295 dB) */
0x0c
,
/* [046] -26.5 dB: AKM[012] = -26.547 dB (diff=0.04718 dB) */
0x0d
,
/* [047] -26.0 dB: AKM[013] = -25.852 dB (diff=0.14806 dB) */
0x0e
,
/* [048] -25.5 dB: AKM[014] = -25.208 dB (diff=0.29176 dB) */
0x0e
,
/* [049] -25.0 dB: AKM[014] = -25.208 dB (diff=0.20824 dB) */
0x0f
,
/* [050] -24.5 dB: AKM[015] = -24.609 dB (diff=0.10898 dB) */
0x10
,
/* [051] -24.0 dB: AKM[016] = -24.048 dB (diff=0.04840 dB) */
0x11
,
/* [052] -23.5 dB: AKM[017] = -23.522 dB (diff=0.02183 dB) */
0x12
,
/* [053] -23.0 dB: AKM[018] = -23.025 dB (diff=0.02535 dB) */
0x13
,
/* [054] -22.5 dB: AKM[019] = -22.556 dB (diff=0.05573 dB) */
0x14
,
/* [055] -22.0 dB: AKM[020] = -22.110 dB (diff=0.11020 dB) */
0x15
,
/* [056] -21.5 dB: AKM[021] = -21.686 dB (diff=0.18642 dB) */
0x17
,
/* [057] -21.0 dB: AKM[023] = -20.896 dB (diff=0.10375 dB) */
0x18
,
/* [058] -20.5 dB: AKM[024] = -20.527 dB (diff=0.02658 dB) */
0x1a
,
/* [059] -20.0 dB: AKM[026] = -19.831 dB (diff=0.16866 dB) */
0x1b
,
/* [060] -19.5 dB: AKM[027] = -19.504 dB (diff=0.00353 dB) */
0x1d
,
/* [061] -19.0 dB: AKM[029] = -18.883 dB (diff=0.11716 dB) */
0x1e
,
/* [062] -18.5 dB: AKM[030] = -18.588 dB (diff=0.08838 dB) */
0x20
,
/* [063] -18.0 dB: AKM[032] = -18.028 dB (diff=0.02780 dB) */
0x22
,
/* [064] -17.5 dB: AKM[034] = -17.501 dB (diff=0.00123 dB) */
0x24
,
/* [065] -17.0 dB: AKM[036] = -17.005 dB (diff=0.00475 dB) */
0x26
,
/* [066] -16.5 dB: AKM[038] = -16.535 dB (diff=0.03513 dB) */
0x28
,
/* [067] -16.0 dB: AKM[040] = -16.090 dB (diff=0.08960 dB) */
0x2b
,
/* [068] -15.5 dB: AKM[043] = -15.461 dB (diff=0.03857 dB) */
0x2d
,
/* [069] -15.0 dB: AKM[045] = -15.067 dB (diff=0.06655 dB) */
0x30
,
/* [070] -14.5 dB: AKM[048] = -14.506 dB (diff=0.00598 dB) */
0x33
,
/* [071] -14.0 dB: AKM[051] = -13.979 dB (diff=0.02060 dB) */
0x36
,
/* [072] -13.5 dB: AKM[054] = -13.483 dB (diff=0.01707 dB) */
0x39
,
/* [073] -13.0 dB: AKM[057] = -13.013 dB (diff=0.01331 dB) */
0x3c
,
/* [074] -12.5 dB: AKM[060] = -12.568 dB (diff=0.06778 dB) */
0x40
,
/* [075] -12.0 dB: AKM[064] = -12.007 dB (diff=0.00720 dB) */
0x44
,
/* [076] -11.5 dB: AKM[068] = -11.481 dB (diff=0.01937 dB) */
0x48
,
/* [077] -11.0 dB: AKM[072] = -10.984 dB (diff=0.01585 dB) */
0x4c
,
/* [078] -10.5 dB: AKM[076] = -10.515 dB (diff=0.01453 dB) */
0x51
,
/* [079] -10.0 dB: AKM[081] = -9.961 dB (diff=0.03890 dB) */
0x55
,
/* [080] -9.5 dB: AKM[085] = -9.542 dB (diff=0.04243 dB) */
0x5a
,
/* [081] -9.0 dB: AKM[090] = -9.046 dB (diff=0.04595 dB) */
0x60
,
/* [082] -8.5 dB: AKM[096] = -8.485 dB (diff=0.01462 dB) */
0x66
,
/* [083] -8.0 dB: AKM[102] = -7.959 dB (diff=0.04120 dB) */
0x6c
,
/* [084] -7.5 dB: AKM[108] = -7.462 dB (diff=0.03767 dB) */
0x72
,
/* [085] -7.0 dB: AKM[114] = -6.993 dB (diff=0.00729 dB) */
0x79
,
/* [086] -6.5 dB: AKM[121] = -6.475 dB (diff=0.02490 dB) */
0x80
,
/* [087] -6.0 dB: AKM[128] = -5.987 dB (diff=0.01340 dB) */
0x87
,
/* [088] -5.5 dB: AKM[135] = -5.524 dB (diff=0.02413 dB) */
0x8f
,
/* [089] -5.0 dB: AKM[143] = -5.024 dB (diff=0.02408 dB) */
0x98
,
/* [090] -4.5 dB: AKM[152] = -4.494 dB (diff=0.00607 dB) */
0xa1
,
/* [091] -4.0 dB: AKM[161] = -3.994 dB (diff=0.00571 dB) */
0xaa
,
/* [092] -3.5 dB: AKM[170] = -3.522 dB (diff=0.02183 dB) */
0xb5
,
/* [093] -3.0 dB: AKM[181] = -2.977 dB (diff=0.02277 dB) */
0xbf
,
/* [094] -2.5 dB: AKM[191] = -2.510 dB (diff=0.01014 dB) */
0xcb
,
/* [095] -2.0 dB: AKM[203] = -1.981 dB (diff=0.01912 dB) */
0xd7
,
/* [096] -1.5 dB: AKM[215] = -1.482 dB (diff=0.01797 dB) */
0xe3
,
/* [097] -1.0 dB: AKM[227] = -1.010 dB (diff=0.01029 dB) */
0xf1
,
/* [098] -0.5 dB: AKM[241] = -0.490 dB (diff=0.00954 dB) */
0xff
,
/* [099] +0.0 dB: AKM[255] = +0.000 dB (diff=0.00000 dB) */
};
static
void
hr222_config_akm
(
struct
pcxhr_mgr
*
mgr
,
unsigned
short
data
)
{
unsigned
short
mask
=
0x8000
;
/* activate access to codec registers */
PCXHR_INPB
(
mgr
,
PCXHR_XLX_HIFREQ
);
while
(
mask
)
{
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_DATA
,
data
&
mask
?
PCXHR_DATA_CODEC
:
0
);
mask
>>=
1
;
}
/* termiate access to codec registers */
PCXHR_INPB
(
mgr
,
PCXHR_XLX_RUER
);
}
static
int
hr222_set_hw_playback_level
(
struct
pcxhr_mgr
*
mgr
,
int
idx
,
int
level
)
{
unsigned
short
cmd
;
if
(
idx
>
1
||
level
<
0
||
level
>=
ARRAY_SIZE
(
g_hr222_p_level
))
return
-
EINVAL
;
if
(
idx
==
0
)
cmd
=
AKM_LEFT_LEVEL_CMD
;
else
cmd
=
AKM_RIGHT_LEVEL_CMD
;
/* conversion from PmBoardCodedLevel to AKM nonlinear programming */
cmd
+=
g_hr222_p_level
[
level
];
hr222_config_akm
(
mgr
,
cmd
);
return
0
;
}
static
int
hr222_set_hw_capture_level
(
struct
pcxhr_mgr
*
mgr
,
int
level_l
,
int
level_r
,
int
level_mic
)
{
/* program all input levels at the same time */
unsigned
int
data
;
int
i
;
if
(
!
mgr
->
capture_chips
)
return
-
EINVAL
;
/* no PCX22 */
data
=
((
level_mic
&
0xff
)
<<
24
);
/* micro is mono, but apply */
data
|=
((
level_mic
&
0xff
)
<<
16
);
/* level on both channels */
data
|=
((
level_r
&
0xff
)
<<
8
);
/* line input right channel */
data
|=
(
level_l
&
0xff
);
/* line input left channel */
PCXHR_INPB
(
mgr
,
PCXHR_XLX_DATA
);
/* activate input codec */
/* send 32 bits (4 x 8 bits) */
for
(
i
=
0
;
i
<
32
;
i
++
,
data
<<=
1
)
{
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_DATA
,
(
data
&
0x80000000
)
?
PCXHR_DATA_CODEC
:
0
);
}
PCXHR_INPB
(
mgr
,
PCXHR_XLX_RUER
);
/* close input level codec */
return
0
;
}
static
void
hr222_micro_boost
(
struct
pcxhr_mgr
*
mgr
,
int
level
);
int
hr222_sub_init
(
struct
pcxhr_mgr
*
mgr
)
{
unsigned
char
reg
;
mgr
->
board_has_analog
=
1
;
/* analog always available */
mgr
->
xlx_cfg
=
PCXHR_CFG_SYNCDSP_MASK
;
reg
=
PCXHR_INPB
(
mgr
,
PCXHR_XLX_STATUS
);
if
(
reg
&
PCXHR_STAT_MIC_CAPS
)
mgr
->
board_has_mic
=
1
;
/* microphone available */
snd_printdd
(
"MIC input available = %d
\n
"
,
mgr
->
board_has_mic
);
/* reset codec */
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_RESET
,
PCXHR_DSP_RESET_DSP
);
msleep
(
5
);
PCXHR_OUTPB
(
mgr
,
PCXHR_DSP_RESET
,
PCXHR_DSP_RESET_DSP
|
PCXHR_DSP_RESET_MUTE
|
PCXHR_DSP_RESET_CODEC
);
msleep
(
5
);
/* config AKM */
hr222_config_akm
(
mgr
,
AKM_POWER_CONTROL_CMD
);
hr222_config_akm
(
mgr
,
AKM_CLOCK_INF_55K_CMD
);
hr222_config_akm
(
mgr
,
AKM_UNMUTE_CMD
);
hr222_config_akm
(
mgr
,
AKM_RESET_OFF_CMD
);
/* init micro boost */
hr222_micro_boost
(
mgr
,
0
);
return
0
;
}
/* calc PLL register */
/* TODO : there is a very similar fct in pcxhr.c */
static
int
hr222_pll_freq_register
(
unsigned
int
freq
,
unsigned
int
*
pllreg
,
unsigned
int
*
realfreq
)
{
unsigned
int
reg
;
if
(
freq
<
6900
||
freq
>
219000
)
return
-
EINVAL
;
reg
=
(
28224000
*
2
)
/
freq
;
reg
=
(
reg
-
1
)
/
2
;
if
(
reg
<
0x100
)
*
pllreg
=
reg
+
0xC00
;
else
if
(
reg
<
0x200
)
*
pllreg
=
reg
+
0x800
;
else
if
(
reg
<
0x400
)
*
pllreg
=
reg
&
0x1ff
;
else
if
(
reg
<
0x800
)
{
*
pllreg
=
((
reg
>>
1
)
&
0x1ff
)
+
0x200
;
reg
&=
~
1
;
}
else
{
*
pllreg
=
((
reg
>>
2
)
&
0x1ff
)
+
0x400
;
reg
&=
~
3
;
}
if
(
realfreq
)
*
realfreq
=
(
28224000
/
(
reg
+
1
));
return
0
;
}
int
hr222_sub_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
,
int
*
changed
)
{
unsigned
int
speed
,
pllreg
=
0
;
int
err
;
unsigned
realfreq
=
rate
;
switch
(
mgr
->
use_clock_type
)
{
case
HR22_CLOCK_TYPE_INTERNAL
:
err
=
hr222_pll_freq_register
(
rate
,
&
pllreg
,
&
realfreq
);
if
(
err
)
return
err
;
mgr
->
xlx_cfg
&=
~
(
PCXHR_CFG_CLOCKIN_SEL_MASK
|
PCXHR_CFG_CLOCK_UER1_SEL_MASK
);
break
;
case
HR22_CLOCK_TYPE_AES_SYNC
:
mgr
->
xlx_cfg
|=
PCXHR_CFG_CLOCKIN_SEL_MASK
;
mgr
->
xlx_cfg
&=
~
PCXHR_CFG_CLOCK_UER1_SEL_MASK
;
break
;
case
HR22_CLOCK_TYPE_AES_1
:
if
(
!
mgr
->
board_has_aes1
)
return
-
EINVAL
;
mgr
->
xlx_cfg
|=
(
PCXHR_CFG_CLOCKIN_SEL_MASK
|
PCXHR_CFG_CLOCK_UER1_SEL_MASK
);
break
;
default:
return
-
EINVAL
;
}
hr222_config_akm
(
mgr
,
AKM_MUTE_CMD
);
if
(
mgr
->
use_clock_type
==
HR22_CLOCK_TYPE_INTERNAL
)
{
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_HIFREQ
,
pllreg
>>
8
);
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_LOFREQ
,
pllreg
&
0xff
);
}
/* set clock source */
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_CFG
,
mgr
->
xlx_cfg
);
/* codec speed modes */
speed
=
rate
<
55000
?
0
:
1
;
if
(
mgr
->
codec_speed
!=
speed
)
{
mgr
->
codec_speed
=
speed
;
if
(
speed
==
0
)
hr222_config_akm
(
mgr
,
AKM_CLOCK_INF_55K_CMD
);
else
hr222_config_akm
(
mgr
,
AKM_CLOCK_SUP_55K_CMD
);
}
mgr
->
sample_rate_real
=
realfreq
;
mgr
->
cur_clock_type
=
mgr
->
use_clock_type
;
if
(
changed
)
*
changed
=
1
;
hr222_config_akm
(
mgr
,
AKM_UNMUTE_CMD
);
snd_printdd
(
"set_clock to %dHz (realfreq=%d pllreg=%x)
\n
"
,
rate
,
realfreq
,
pllreg
);
return
0
;
}
int
hr222_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
)
{
int
rate
,
calc_rate
=
0
;
unsigned
int
ticks
;
unsigned
char
mask
,
reg
;
if
(
clock_type
==
HR22_CLOCK_TYPE_AES_SYNC
)
{
mask
=
(
PCXHR_SUER_CLOCK_PRESENT_MASK
|
PCXHR_SUER_DATA_PRESENT_MASK
);
reg
=
PCXHR_STAT_FREQ_SYNC_MASK
;
}
else
if
(
clock_type
==
HR22_CLOCK_TYPE_AES_1
&&
mgr
->
board_has_aes1
)
{
mask
=
(
PCXHR_SUER1_CLOCK_PRESENT_MASK
|
PCXHR_SUER1_DATA_PRESENT_MASK
);
reg
=
PCXHR_STAT_FREQ_UER1_MASK
;
}
else
{
snd_printdd
(
"get_external_clock : type %d not supported
\n
"
,
clock_type
);
return
-
EINVAL
;
/* other clocks not supported */
}
if
((
PCXHR_INPB
(
mgr
,
PCXHR_XLX_CSUER
)
&
mask
)
!=
mask
)
{
snd_printdd
(
"get_external_clock(%d) = 0 Hz
\n
"
,
clock_type
);
*
sample_rate
=
0
;
return
0
;
/* no external clock locked */
}
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_STATUS
,
reg
);
/* calculate freq */
/* save the measured clock frequency */
reg
|=
PCXHR_STAT_FREQ_SAVE_MASK
;
if
(
mgr
->
last_reg_stat
!=
reg
)
{
udelay
(
500
);
/* wait min 2 cycles of lowest freq (8000) */
mgr
->
last_reg_stat
=
reg
;
}
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_STATUS
,
reg
);
/* save */
/* get the frequency */
ticks
=
(
unsigned
int
)
PCXHR_INPB
(
mgr
,
PCXHR_XLX_CFG
);
ticks
=
(
ticks
&
0x03
)
<<
8
;
ticks
|=
(
unsigned
int
)
PCXHR_INPB
(
mgr
,
PCXHR_DSP_RESET
);
if
(
ticks
!=
0
)
calc_rate
=
28224000
/
ticks
;
/* rounding */
if
(
calc_rate
>
184200
)
rate
=
192000
;
else
if
(
calc_rate
>
152200
)
rate
=
176400
;
else
if
(
calc_rate
>
112000
)
rate
=
128000
;
else
if
(
calc_rate
>
92100
)
rate
=
96000
;
else
if
(
calc_rate
>
76100
)
rate
=
88200
;
else
if
(
calc_rate
>
56000
)
rate
=
64000
;
else
if
(
calc_rate
>
46050
)
rate
=
48000
;
else
if
(
calc_rate
>
38050
)
rate
=
44100
;
else
if
(
calc_rate
>
28000
)
rate
=
32000
;
else
if
(
calc_rate
>
23025
)
rate
=
24000
;
else
if
(
calc_rate
>
19025
)
rate
=
22050
;
else
if
(
calc_rate
>
14000
)
rate
=
16000
;
else
if
(
calc_rate
>
11512
)
rate
=
12000
;
else
if
(
calc_rate
>
9512
)
rate
=
11025
;
else
if
(
calc_rate
>
7000
)
rate
=
8000
;
else
rate
=
0
;
snd_printdd
(
"External clock is at %d Hz (measured %d Hz)
\n
"
,
rate
,
calc_rate
);
*
sample_rate
=
rate
;
return
0
;
}
int
hr222_update_analog_audio_level
(
struct
snd_pcxhr
*
chip
,
int
is_capture
,
int
channel
)
{
snd_printdd
(
"hr222_update_analog_audio_level(%s chan=%d)
\n
"
,
is_capture
?
"capture"
:
"playback"
,
channel
);
if
(
is_capture
)
{
int
level_l
,
level_r
,
level_mic
;
/* we have to update all levels */
if
(
chip
->
analog_capture_active
)
{
level_l
=
chip
->
analog_capture_volume
[
0
];
level_r
=
chip
->
analog_capture_volume
[
1
];
}
else
{
level_l
=
HR222_LINE_CAPTURE_LEVEL_MIN
;
level_r
=
HR222_LINE_CAPTURE_LEVEL_MIN
;
}
if
(
chip
->
mic_active
)
level_mic
=
chip
->
mic_volume
;
else
level_mic
=
HR222_MICRO_CAPTURE_LEVEL_MIN
;
return
hr222_set_hw_capture_level
(
chip
->
mgr
,
level_l
,
level_r
,
level_mic
);
}
else
{
int
vol
;
if
(
chip
->
analog_playback_active
[
channel
])
vol
=
chip
->
analog_playback_volume
[
channel
];
else
vol
=
HR222_LINE_PLAYBACK_LEVEL_MIN
;
return
hr222_set_hw_playback_level
(
chip
->
mgr
,
channel
,
vol
);
}
}
/*texts[5] = {"Line", "Digital", "Digi+SRC", "Mic", "Line+Mic"}*/
#define SOURCE_LINE 0
#define SOURCE_DIGITAL 1
#define SOURCE_DIGISRC 2
#define SOURCE_MIC 3
#define SOURCE_LINEMIC 4
int
hr222_set_audio_source
(
struct
snd_pcxhr
*
chip
)
{
int
digital
=
0
;
/* default analog source */
chip
->
mgr
->
xlx_cfg
&=
~
(
PCXHR_CFG_SRC_MASK
|
PCXHR_CFG_DATAIN_SEL_MASK
|
PCXHR_CFG_DATA_UER1_SEL_MASK
);
if
(
chip
->
audio_capture_source
==
SOURCE_DIGISRC
)
{
chip
->
mgr
->
xlx_cfg
|=
PCXHR_CFG_SRC_MASK
;
digital
=
1
;
}
else
{
if
(
chip
->
audio_capture_source
==
SOURCE_DIGITAL
)
digital
=
1
;
}
if
(
digital
)
{
chip
->
mgr
->
xlx_cfg
|=
PCXHR_CFG_DATAIN_SEL_MASK
;
if
(
chip
->
mgr
->
board_has_aes1
)
{
/* get data from the AES1 plug */
chip
->
mgr
->
xlx_cfg
|=
PCXHR_CFG_DATA_UER1_SEL_MASK
;
}
/* chip->mic_active = 0; */
/* chip->analog_capture_active = 0; */
}
else
{
int
update_lvl
=
0
;
chip
->
analog_capture_active
=
0
;
chip
->
mic_active
=
0
;
if
(
chip
->
audio_capture_source
==
SOURCE_LINE
||
chip
->
audio_capture_source
==
SOURCE_LINEMIC
)
{
if
(
chip
->
analog_capture_active
==
0
)
update_lvl
=
1
;
chip
->
analog_capture_active
=
1
;
}
if
(
chip
->
audio_capture_source
==
SOURCE_MIC
||
chip
->
audio_capture_source
==
SOURCE_LINEMIC
)
{
if
(
chip
->
mic_active
==
0
)
update_lvl
=
1
;
chip
->
mic_active
=
1
;
}
if
(
update_lvl
)
{
/* capture: update all 3 mutes/unmutes with one call */
hr222_update_analog_audio_level
(
chip
,
1
,
0
);
}
}
/* set the source infos (max 3 bits modified) */
PCXHR_OUTPB
(
chip
->
mgr
,
PCXHR_XLX_CFG
,
chip
->
mgr
->
xlx_cfg
);
return
0
;
}
int
hr222_iec958_capture_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
*
aes_bits
)
{
unsigned
char
idx
=
(
unsigned
char
)(
aes_idx
*
8
);
unsigned
char
temp
=
0
;
unsigned
char
mask
=
chip
->
mgr
->
board_has_aes1
?
PCXHR_SUER1_BIT_C_READ_MASK
:
PCXHR_SUER_BIT_C_READ_MASK
;
int
i
;
for
(
i
=
0
;
i
<
8
;
i
++
)
{
PCXHR_OUTPB
(
chip
->
mgr
,
PCXHR_XLX_RUER
,
idx
++
);
/* idx < 192 */
temp
<<=
1
;
if
(
PCXHR_INPB
(
chip
->
mgr
,
PCXHR_XLX_CSUER
)
&
mask
)
temp
|=
1
;
}
snd_printdd
(
"read iec958 AES %d byte %d = 0x%x
\n
"
,
chip
->
chip_idx
,
aes_idx
,
temp
);
*
aes_bits
=
temp
;
return
0
;
}
int
hr222_iec958_update_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
aes_bits
)
{
int
i
;
unsigned
char
new_bits
=
aes_bits
;
unsigned
char
old_bits
=
chip
->
aes_bits
[
aes_idx
];
unsigned
char
idx
=
(
unsigned
char
)(
aes_idx
*
8
);
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
((
old_bits
&
0x01
)
!=
(
new_bits
&
0x01
))
{
/* idx < 192 */
PCXHR_OUTPB
(
chip
->
mgr
,
PCXHR_XLX_RUER
,
idx
);
/* write C and U bit */
PCXHR_OUTPB
(
chip
->
mgr
,
PCXHR_XLX_CSUER
,
new_bits
&
0x01
?
PCXHR_SUER_BIT_C_WRITE_MASK
:
0
);
}
idx
++
;
old_bits
>>=
1
;
new_bits
>>=
1
;
}
chip
->
aes_bits
[
aes_idx
]
=
aes_bits
;
return
0
;
}
static
void
hr222_micro_boost
(
struct
pcxhr_mgr
*
mgr
,
int
level
)
{
unsigned
char
boost_mask
;
boost_mask
=
(
unsigned
char
)
(
level
<<
PCXHR_SELMIC_PREAMPLI_OFFSET
);
if
(
boost_mask
&
(
~
PCXHR_SELMIC_PREAMPLI_MASK
))
return
;
/* only values form 0 to 3 accepted */
mgr
->
xlx_selmic
&=
~
PCXHR_SELMIC_PREAMPLI_MASK
;
mgr
->
xlx_selmic
|=
boost_mask
;
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_SELMIC
,
mgr
->
xlx_selmic
);
snd_printdd
(
"hr222_micro_boost : set %x
\n
"
,
boost_mask
);
}
static
void
hr222_phantom_power
(
struct
pcxhr_mgr
*
mgr
,
int
power
)
{
if
(
power
)
mgr
->
xlx_selmic
|=
PCXHR_SELMIC_PHANTOM_ALIM
;
else
mgr
->
xlx_selmic
&=
~
PCXHR_SELMIC_PHANTOM_ALIM
;
PCXHR_OUTPB
(
mgr
,
PCXHR_XLX_SELMIC
,
mgr
->
xlx_selmic
);
snd_printdd
(
"hr222_phantom_power : set %d
\n
"
,
power
);
}
/* mic level */
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_mic_hr222
,
-
9850
,
50
,
650
);
static
int
hr222_mic_vol_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
HR222_MICRO_CAPTURE_LEVEL_MIN
;
/* -98 dB */
/* gains from 9 dB to 31.5 dB not recommended; use micboost instead */
uinfo
->
value
.
integer
.
max
=
HR222_MICRO_CAPTURE_LEVEL_MAX
;
/* +7 dB */
return
0
;
}
static
int
hr222_mic_vol_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
chip
->
mic_volume
;
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
0
;
}
static
int
hr222_mic_vol_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
int
changed
=
0
;
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
if
(
chip
->
mic_volume
!=
ucontrol
->
value
.
integer
.
value
[
0
])
{
changed
=
1
;
chip
->
mic_volume
=
ucontrol
->
value
.
integer
.
value
[
0
];
hr222_update_analog_audio_level
(
chip
,
1
,
0
);
}
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
changed
;
}
static
struct
snd_kcontrol_new
hr222_control_mic_level
=
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
access
=
(
SNDRV_CTL_ELEM_ACCESS_READWRITE
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ
),
.
name
=
"Mic Capture Volume"
,
.
info
=
hr222_mic_vol_info
,
.
get
=
hr222_mic_vol_get
,
.
put
=
hr222_mic_vol_put
,
.
tlv
=
{
.
p
=
db_scale_mic_hr222
},
};
/* mic boost level */
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_micboost_hr222
,
0
,
1800
,
5400
);
static
int
hr222_mic_boost_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
1
;
uinfo
->
value
.
integer
.
min
=
0
;
/* 0 dB */
uinfo
->
value
.
integer
.
max
=
3
;
/* 54 dB */
return
0
;
}
static
int
hr222_mic_boost_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
chip
->
mic_boost
;
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
0
;
}
static
int
hr222_mic_boost_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
int
changed
=
0
;
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
if
(
chip
->
mic_boost
!=
ucontrol
->
value
.
integer
.
value
[
0
])
{
changed
=
1
;
chip
->
mic_boost
=
ucontrol
->
value
.
integer
.
value
[
0
];
hr222_micro_boost
(
chip
->
mgr
,
chip
->
mic_boost
);
}
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
changed
;
}
static
struct
snd_kcontrol_new
hr222_control_mic_boost
=
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
access
=
(
SNDRV_CTL_ELEM_ACCESS_READWRITE
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ
),
.
name
=
"MicBoost Capture Volume"
,
.
info
=
hr222_mic_boost_info
,
.
get
=
hr222_mic_boost_get
,
.
put
=
hr222_mic_boost_put
,
.
tlv
=
{
.
p
=
db_scale_micboost_hr222
},
};
/******************* Phantom power switch *******************/
#define hr222_phantom_power_info snd_ctl_boolean_mono_info
static
int
hr222_phantom_power_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
chip
->
phantom_power
;
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
0
;
}
static
int
hr222_phantom_power_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
int
power
,
changed
=
0
;
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
power
=
!!
ucontrol
->
value
.
integer
.
value
[
0
];
if
(
chip
->
phantom_power
!=
power
)
{
hr222_phantom_power
(
chip
->
mgr
,
power
);
chip
->
phantom_power
=
power
;
changed
=
1
;
}
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
return
changed
;
}
static
struct
snd_kcontrol_new
hr222_phantom_power_switch
=
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
name
=
"Phantom Power Switch"
,
.
info
=
hr222_phantom_power_info
,
.
get
=
hr222_phantom_power_get
,
.
put
=
hr222_phantom_power_put
,
};
int
hr222_add_mic_controls
(
struct
snd_pcxhr
*
chip
)
{
int
err
;
if
(
!
chip
->
mgr
->
board_has_mic
)
return
0
;
/* controls */
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
hr222_control_mic_level
,
chip
));
if
(
err
<
0
)
return
err
;
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
hr222_control_mic_boost
,
chip
));
if
(
err
<
0
)
return
err
;
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
hr222_phantom_power_switch
,
chip
));
return
err
;
}
sound/pci/pcxhr/pcxhr_mix22.h
0 → 100644
View file @
a9c3c7e0
/*
* Driver for Digigram pcxhr compatible soundcards
*
* low level interface with interrupt ans message handling
*
* Copyright (c) 2004 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SOUND_PCXHR_MIX22_H
#define __SOUND_PCXHR_MIX22_H
struct
pcxhr_mgr
;
int
hr222_sub_init
(
struct
pcxhr_mgr
*
mgr
);
int
hr222_sub_set_clock
(
struct
pcxhr_mgr
*
mgr
,
unsigned
int
rate
,
int
*
changed
);
int
hr222_get_external_clock
(
struct
pcxhr_mgr
*
mgr
,
enum
pcxhr_clock_type
clock_type
,
int
*
sample_rate
);
#define HR222_LINE_PLAYBACK_LEVEL_MIN 0
/* -25.5 dB */
#define HR222_LINE_PLAYBACK_ZERO_LEVEL 51
/* 0.0 dB */
#define HR222_LINE_PLAYBACK_LEVEL_MAX 99
/* +24.0 dB */
#define HR222_LINE_CAPTURE_LEVEL_MIN 0
/* -111.5 dB */
#define HR222_LINE_CAPTURE_ZERO_LEVEL 223
/* 0.0 dB */
#define HR222_LINE_CAPTURE_LEVEL_MAX 255
/* +16 dB */
#define HR222_MICRO_CAPTURE_LEVEL_MIN 0
/* -98.5 dB */
#define HR222_MICRO_CAPTURE_LEVEL_MAX 210
/* +6.5 dB */
int
hr222_update_analog_audio_level
(
struct
snd_pcxhr
*
chip
,
int
is_capture
,
int
channel
);
int
hr222_set_audio_source
(
struct
snd_pcxhr
*
chip
);
int
hr222_iec958_capture_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
*
aes_bits
);
int
hr222_iec958_update_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
aes_bits
);
int
hr222_add_mic_controls
(
struct
snd_pcxhr
*
chip
);
#endif
/* __SOUND_PCXHR_MIX22_H */
sound/pci/pcxhr/pcxhr_mixer.c
View file @
a9c3c7e0
...
...
@@ -33,20 +33,24 @@
#include <sound/tlv.h>
#include <sound/asoundef.h>
#include "pcxhr_mixer.h"
#include "pcxhr_mix22.h"
#define PCXHR_LINE_CAPTURE_LEVEL_MIN 0
/* -112.0 dB */
#define PCXHR_LINE_CAPTURE_LEVEL_MAX 255
/* +15.5 dB */
#define PCXHR_LINE_CAPTURE_ZERO_LEVEL 224
/* 0.0 dB ( 0 dBu -> 0 dBFS ) */
#define PCXHR_
ANALOG_CAPTURE_LEVEL_MIN 0
/* -96
.0 dB */
#define PCXHR_
ANALOG_CAPTURE_LEVEL_MAX 255
/* +31.5
dB */
#define PCXHR_
ANALOG_CAPTURE_ZERO_LEVEL 224
/* +16.0 dB ( +31.5 dB - fix level +15.5 dB
) */
#define PCXHR_
LINE_PLAYBACK_LEVEL_MIN 0
/* -104
.0 dB */
#define PCXHR_
LINE_PLAYBACK_LEVEL_MAX 128
/* +24.0
dB */
#define PCXHR_
LINE_PLAYBACK_ZERO_LEVEL 104
/* 0.0 dB ( 0 dBFS -> 0 dBu
) */
#define PCXHR_ANALOG_PLAYBACK_LEVEL_MIN 0
/* -128.0 dB */
#define PCXHR_ANALOG_PLAYBACK_LEVEL_MAX 128
/* 0.0 dB */
#define PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL 104
/* -24.0 dB ( 0.0 dB - fix level +24.0 dB ) */
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_analog_capture
,
-
9600
,
50
,
3150
);
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_analog_capture
,
-
11200
,
50
,
1550
);
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_analog_playback
,
-
10400
,
100
,
2400
);
static
int
pcxhr_update_analog_audio_level
(
struct
snd_pcxhr
*
chip
,
int
is_capture
,
int
channel
)
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_a_hr222_capture
,
-
11150
,
50
,
1600
);
static
const
DECLARE_TLV_DB_SCALE
(
db_scale_a_hr222_playback
,
-
2550
,
50
,
2400
);
static
int
pcxhr_update_analog_audio_level
(
struct
snd_pcxhr
*
chip
,
int
is_capture
,
int
channel
)
{
int
err
,
vol
;
struct
pcxhr_rmh
rmh
;
...
...
@@ -60,15 +64,17 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
if
(
chip
->
analog_playback_active
[
channel
])
vol
=
chip
->
analog_playback_volume
[
channel
];
else
vol
=
PCXHR_ANALOG_PLAYBACK_LEVEL_MIN
;
rmh
.
cmd
[
2
]
=
PCXHR_ANALOG_PLAYBACK_LEVEL_MAX
-
vol
;
/* playback analog levels are inversed */
vol
=
PCXHR_LINE_PLAYBACK_LEVEL_MIN
;
/* playback analog levels are inversed */
rmh
.
cmd
[
2
]
=
PCXHR_LINE_PLAYBACK_LEVEL_MAX
-
vol
;
}
rmh
.
cmd
[
1
]
=
1
<<
((
2
*
chip
->
chip_idx
)
+
channel
);
/* audio mask */
rmh
.
cmd_len
=
3
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
<
0
)
{
snd_printk
(
KERN_DEBUG
"error update_analog_audio_level card(%d) "
"is_capture(%d) err(%x)
\n
"
,
chip
->
chip_idx
,
is_capture
,
err
);
snd_printk
(
KERN_DEBUG
"error update_analog_audio_level card(%d)"
" is_capture(%d) err(%x)
\n
"
,
chip
->
chip_idx
,
is_capture
,
err
);
return
-
EINVAL
;
}
return
0
;
...
...
@@ -80,14 +86,34 @@ static int pcxhr_update_analog_audio_level(struct snd_pcxhr *chip, int is_captur
static
int
pcxhr_analog_vol_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
2
;
if
(
kcontrol
->
private_value
==
0
)
{
/* playback */
uinfo
->
value
.
integer
.
min
=
PCXHR_ANALOG_PLAYBACK_LEVEL_MIN
;
/* -128 dB */
uinfo
->
value
.
integer
.
max
=
PCXHR_ANALOG_PLAYBACK_LEVEL_MAX
;
/* 0 dB */
if
(
chip
->
mgr
->
is_hr_stereo
)
{
uinfo
->
value
.
integer
.
min
=
HR222_LINE_PLAYBACK_LEVEL_MIN
;
/* -25 dB */
uinfo
->
value
.
integer
.
max
=
HR222_LINE_PLAYBACK_LEVEL_MAX
;
/* +24 dB */
}
else
{
uinfo
->
value
.
integer
.
min
=
PCXHR_LINE_PLAYBACK_LEVEL_MIN
;
/*-104 dB */
uinfo
->
value
.
integer
.
max
=
PCXHR_LINE_PLAYBACK_LEVEL_MAX
;
/* +24 dB */
}
}
else
{
/* capture */
uinfo
->
value
.
integer
.
min
=
PCXHR_ANALOG_CAPTURE_LEVEL_MIN
;
/* -96 dB */
uinfo
->
value
.
integer
.
max
=
PCXHR_ANALOG_CAPTURE_LEVEL_MAX
;
/* 31.5 dB */
if
(
chip
->
mgr
->
is_hr_stereo
)
{
uinfo
->
value
.
integer
.
min
=
HR222_LINE_CAPTURE_LEVEL_MIN
;
/*-112 dB */
uinfo
->
value
.
integer
.
max
=
HR222_LINE_CAPTURE_LEVEL_MAX
;
/* +15.5 dB */
}
else
{
uinfo
->
value
.
integer
.
min
=
PCXHR_LINE_CAPTURE_LEVEL_MIN
;
/*-112 dB */
uinfo
->
value
.
integer
.
max
=
PCXHR_LINE_CAPTURE_LEVEL_MAX
;
/* +15.5 dB */
}
}
return
0
;
}
...
...
@@ -123,18 +149,35 @@ static int pcxhr_analog_vol_put(struct snd_kcontrol *kcontrol,
&
chip
->
analog_capture_volume
[
i
]
:
&
chip
->
analog_playback_volume
[
i
];
if
(
is_capture
)
{
if
(
new_volume
<
PCXHR_ANALOG_CAPTURE_LEVEL_MIN
||
new_volume
>
PCXHR_ANALOG_CAPTURE_LEVEL_MAX
)
if
(
chip
->
mgr
->
is_hr_stereo
)
{
if
(
new_volume
<
HR222_LINE_CAPTURE_LEVEL_MIN
||
new_volume
>
HR222_LINE_CAPTURE_LEVEL_MAX
)
continue
;
}
else
{
if
(
new_volume
<
PCXHR_ANALOG_PLAYBACK
_LEVEL_MIN
||
new_volume
>
PCXHR_ANALOG_PLAYBACK
_LEVEL_MAX
)
if
(
new_volume
<
PCXHR_LINE_CAPTURE
_LEVEL_MIN
||
new_volume
>
PCXHR_LINE_CAPTURE
_LEVEL_MAX
)
continue
;
}
}
else
{
if
(
chip
->
mgr
->
is_hr_stereo
)
{
if
(
new_volume
<
HR222_LINE_PLAYBACK_LEVEL_MIN
||
new_volume
>
HR222_LINE_PLAYBACK_LEVEL_MAX
)
continue
;
}
else
{
if
(
new_volume
<
PCXHR_LINE_PLAYBACK_LEVEL_MIN
||
new_volume
>
PCXHR_LINE_PLAYBACK_LEVEL_MAX
)
continue
;
}
}
if
(
*
stored_volume
!=
new_volume
)
{
*
stored_volume
=
new_volume
;
changed
=
1
;
pcxhr_update_analog_audio_level
(
chip
,
is_capture
,
i
);
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_update_analog_audio_level
(
chip
,
is_capture
,
i
);
else
pcxhr_update_analog_audio_level
(
chip
,
is_capture
,
i
);
}
}
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
...
...
@@ -153,6 +196,7 @@ static struct snd_kcontrol_new pcxhr_control_analog_level = {
};
/* shared */
#define pcxhr_sw_info snd_ctl_boolean_stereo_info
static
int
pcxhr_audio_sw_get
(
struct
snd_kcontrol
*
kcontrol
,
...
...
@@ -180,6 +224,9 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
!!
ucontrol
->
value
.
integer
.
value
[
i
];
changed
=
1
;
/* update playback levels */
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_update_analog_audio_level
(
chip
,
0
,
i
);
else
pcxhr_update_analog_audio_level
(
chip
,
0
,
i
);
}
}
...
...
@@ -251,7 +298,8 @@ static int pcxhr_update_playback_stream_level(struct snd_pcxhr* chip, int idx)
#define VALID_AUDIO_IO_MUTE_LEVEL 0x000004
#define VALID_AUDIO_IO_MUTE_MONITOR_1 0x000008
static
int
pcxhr_update_audio_pipe_level
(
struct
snd_pcxhr
*
chip
,
int
capture
,
int
channel
)
static
int
pcxhr_update_audio_pipe_level
(
struct
snd_pcxhr
*
chip
,
int
capture
,
int
channel
)
{
int
err
;
struct
pcxhr_rmh
rmh
;
...
...
@@ -264,18 +312,20 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
pcxhr_init_rmh
(
&
rmh
,
CMD_AUDIO_LEVEL_ADJUST
);
/* add channel mask */
pcxhr_set_pipe_cmd_params
(
&
rmh
,
capture
,
0
,
0
,
1
<<
(
channel
+
pipe
->
first_audio
));
/* TODO : if mask (3 << pipe->first_audio) is used, left and right channel
* will be programmed to the same params
*/
pcxhr_set_pipe_cmd_params
(
&
rmh
,
capture
,
0
,
0
,
1
<<
(
channel
+
pipe
->
first_audio
));
/* TODO : if mask (3 << pipe->first_audio) is used, left and right
*
channel will be programmed to the same params *
/
if
(
capture
)
{
rmh
.
cmd
[
0
]
|=
VALID_AUDIO_IO_DIGITAL_LEVEL
;
/* VALID_AUDIO_IO_MUTE_LEVEL not yet handled (capture pipe level) */
/* VALID_AUDIO_IO_MUTE_LEVEL not yet handled
* (capture pipe level) */
rmh
.
cmd
[
2
]
=
chip
->
digital_capture_volume
[
channel
];
}
else
{
rmh
.
cmd
[
0
]
|=
VALID_AUDIO_IO_MONITOR_LEVEL
|
VALID_AUDIO_IO_MUTE_MONITOR_1
;
/* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL not yet
* handled (playback pipe level)
rmh
.
cmd
[
0
]
|=
VALID_AUDIO_IO_MONITOR_LEVEL
|
VALID_AUDIO_IO_MUTE_MONITOR_1
;
/* VALID_AUDIO_IO_DIGITAL_LEVEL and VALID_AUDIO_IO_MUTE_LEVEL
* not yet handled (playback pipe level)
*/
rmh
.
cmd
[
2
]
=
chip
->
monitoring_volume
[
channel
]
<<
10
;
if
(
chip
->
monitoring_active
[
channel
]
==
0
)
...
...
@@ -284,8 +334,8 @@ static int pcxhr_update_audio_pipe_level(struct snd_pcxhr* chip, int capture, in
rmh
.
cmd_len
=
3
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
<
0
)
{
snd_printk
(
KERN_DEBUG
"error update_audio_level
card(%d) err(%x)
\n
"
,
if
(
err
<
0
)
{
snd_printk
(
KERN_DEBUG
"error update_audio_level
(%d) err=%x
\n
"
,
chip
->
chip_idx
,
err
);
return
-
EINVAL
;
}
...
...
@@ -314,10 +364,10 @@ static int pcxhr_pcm_vol_get(struct snd_kcontrol *kcontrol,
int
is_capture
=
kcontrol
->
private_value
;
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
if
(
is_capture
)
stored_volume
=
chip
->
digital_capture_volume
;
/* digital capture */
else
stored_volume
=
chip
->
digital_playback_volume
[
idx
];
/* digital playback */
if
(
is_capture
)
/* digital capture */
stored_volume
=
chip
->
digital_capture_volume
;
else
/* digital playback */
stored_volume
=
chip
->
digital_playback_volume
[
idx
];
ucontrol
->
value
.
integer
.
value
[
0
]
=
stored_volume
[
0
];
ucontrol
->
value
.
integer
.
value
[
1
]
=
stored_volume
[
1
];
mutex_unlock
(
&
chip
->
mgr
->
mixer_mutex
);
...
...
@@ -384,7 +434,8 @@ static int pcxhr_pcm_sw_get(struct snd_kcontrol *kcontrol,
return
0
;
}
static
int
pcxhr_pcm_sw_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
static
int
pcxhr_pcm_sw_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
int
changed
=
0
;
...
...
@@ -444,8 +495,8 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
if
(
chip
->
monitoring_volume
[
i
]
!=
ucontrol
->
value
.
integer
.
value
[
i
])
{
chip
->
monitoring_volume
[
i
]
=
!!
ucontrol
->
value
.
integer
.
value
[
i
];
if
(
chip
->
monitoring_active
[
i
])
ucontrol
->
value
.
integer
.
value
[
i
];
if
(
chip
->
monitoring_active
[
i
])
/* update monitoring volume and mute */
/* do only when monitoring is unmuted */
pcxhr_update_audio_pipe_level
(
chip
,
0
,
i
);
...
...
@@ -460,7 +511,7 @@ static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
access
=
(
SNDRV_CTL_ELEM_ACCESS_READWRITE
|
SNDRV_CTL_ELEM_ACCESS_TLV_READ
),
.
name
=
"Monitoring Volume"
,
.
name
=
"Monitoring
Playback
Volume"
,
.
info
=
pcxhr_digital_vol_info
,
/* shared */
.
get
=
pcxhr_monitor_vol_get
,
.
put
=
pcxhr_monitor_vol_put
,
...
...
@@ -511,7 +562,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
static
struct
snd_kcontrol_new
pcxhr_control_monitor_sw
=
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_MIXER
,
.
name
=
"Monitoring Switch"
,
.
name
=
"Monitoring
Playback
Switch"
,
.
info
=
pcxhr_sw_info
,
/* shared */
.
get
=
pcxhr_monitor_sw_get
,
.
put
=
pcxhr_monitor_sw_put
...
...
@@ -533,7 +584,7 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
struct
pcxhr_rmh
rmh
;
unsigned
int
mask
,
reg
;
unsigned
int
codec
;
int
err
,
use_src
,
changed
;
int
err
,
changed
;
switch
(
chip
->
chip_idx
)
{
case
0
:
mask
=
PCXHR_SOURCE_AUDIO01_UER
;
codec
=
CS8420_01_CS
;
break
;
...
...
@@ -542,13 +593,10 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
case
3
:
mask
=
PCXHR_SOURCE_AUDIO67_UER
;
codec
=
CS8420_67_CS
;
break
;
default:
return
-
EINVAL
;
}
reg
=
0
;
/* audio source from analog plug */
use_src
=
0
;
/* do not activate codec SRC */
if
(
chip
->
audio_capture_source
!=
0
)
{
reg
=
mask
;
/* audio source from digital plug */
if
(
chip
->
audio_capture_source
==
2
)
use_src
=
1
;
}
else
{
reg
=
0
;
/* audio source from analog plug */
}
/* set the input source */
pcxhr_write_io_num_reg_cont
(
chip
->
mgr
,
mask
,
reg
,
&
changed
);
...
...
@@ -560,29 +608,61 @@ static int pcxhr_set_audio_source(struct snd_pcxhr* chip)
if
(
err
)
return
err
;
}
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_WRITE
);
/* set codec SRC on off */
if
(
chip
->
mgr
->
board_aes_in_192k
)
{
int
i
;
unsigned
int
src_config
=
0xC0
;
/* update all src configs with one call */
for
(
i
=
0
;
(
i
<
4
)
&&
(
i
<
chip
->
mgr
->
capture_chips
);
i
++
)
{
if
(
chip
->
mgr
->
chip
[
i
]
->
audio_capture_source
==
2
)
src_config
|=
(
1
<<
(
3
-
i
));
}
/* set codec SRC on off */
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_WRITE
);
rmh
.
cmd_len
=
2
;
rmh
.
cmd
[
0
]
|=
IO_NUM_REG_CONFIG_SRC
;
rmh
.
cmd
[
1
]
=
src_config
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
}
else
{
int
use_src
=
0
;
if
(
chip
->
audio_capture_source
==
2
)
use_src
=
1
;
/* set codec SRC on off */
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_WRITE
);
rmh
.
cmd_len
=
3
;
rmh
.
cmd
[
0
]
|=
IO_NUM_UER_CHIP_REG
;
rmh
.
cmd
[
1
]
=
codec
;
rmh
.
cmd
[
2
]
=
(
CS8420_DATA_FLOW_CTL
&
CHIP_SIG_AND_MAP_SPI
)
|
(
use_src
?
0x41
:
0x54
);
rmh
.
cmd
[
2
]
=
((
CS8420_DATA_FLOW_CTL
&
CHIP_SIG_AND_MAP_SPI
)
|
(
use_src
?
0x41
:
0x54
));
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
)
if
(
err
)
return
err
;
rmh
.
cmd
[
2
]
=
(
CS8420_CLOCK_SRC_CTL
&
CHIP_SIG_AND_MAP_SPI
)
|
(
use_src
?
0x41
:
0x49
);
rmh
.
cmd
[
2
]
=
((
CS8420_CLOCK_SRC_CTL
&
CHIP_SIG_AND_MAP_SPI
)
|
(
use_src
?
0x41
:
0x49
));
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
}
return
err
;
}
static
int
pcxhr_audio_src_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
static
char
*
texts
[
3
]
=
{
"Analog"
,
"Digital"
,
"Digi+SRC"
};
static
const
char
*
texts
[
5
]
=
{
"Line"
,
"Digital"
,
"Digi+SRC"
,
"Mic"
,
"Line+Mic"
};
int
i
;
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
i
=
2
;
/* no SRC, no Mic available */
if
(
chip
->
mgr
->
board_has_aes1
)
{
i
=
3
;
/* SRC available */
if
(
chip
->
mgr
->
board_has_mic
)
i
=
5
;
/* Mic and MicroMix available */
}
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_ENUMERATED
;
uinfo
->
count
=
1
;
uinfo
->
value
.
enumerated
.
items
=
3
;
if
(
uinfo
->
value
.
enumerated
.
item
>
2
)
uinfo
->
value
.
enumerated
.
item
=
2
;
uinfo
->
value
.
enumerated
.
items
=
i
;
if
(
uinfo
->
value
.
enumerated
.
item
>
(
i
-
1
)
)
uinfo
->
value
.
enumerated
.
item
=
i
-
1
;
strcpy
(
uinfo
->
value
.
enumerated
.
name
,
texts
[
uinfo
->
value
.
enumerated
.
item
]);
return
0
;
...
...
@@ -601,12 +681,20 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
int
ret
=
0
;
if
(
ucontrol
->
value
.
enumerated
.
item
[
0
]
>=
3
)
int
i
=
2
;
/* no SRC, no Mic available */
if
(
chip
->
mgr
->
board_has_aes1
)
{
i
=
3
;
/* SRC available */
if
(
chip
->
mgr
->
board_has_mic
)
i
=
5
;
/* Mic and MicroMix available */
}
if
(
ucontrol
->
value
.
enumerated
.
item
[
0
]
>=
i
)
return
-
EINVAL
;
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
if
(
chip
->
audio_capture_source
!=
ucontrol
->
value
.
enumerated
.
item
[
0
])
{
chip
->
audio_capture_source
=
ucontrol
->
value
.
enumerated
.
item
[
0
];
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_set_audio_source
(
chip
);
else
pcxhr_set_audio_source
(
chip
);
ret
=
1
;
}
...
...
@@ -633,18 +721,39 @@ static struct snd_kcontrol_new pcxhr_control_audio_src = {
* PCXHR_CLOCK_TYPE_AES_2,
* PCXHR_CLOCK_TYPE_AES_3,
* PCXHR_CLOCK_TYPE_AES_4,
* PCXHR_CLOCK_TYPE_MAX = PCXHR_CLOCK_TYPE_AES_4,
* HR22_CLOCK_TYPE_INTERNAL = PCXHR_CLOCK_TYPE_INTERNAL,
* HR22_CLOCK_TYPE_AES_SYNC,
* HR22_CLOCK_TYPE_AES_1,
* HR22_CLOCK_TYPE_MAX = HR22_CLOCK_TYPE_AES_1,
* };
*/
static
int
pcxhr_clock_type_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
static
char
*
texts
[
7
]
=
{
"Internal"
,
"WordClock"
,
"AES Sync"
,
"AES 1"
,
"AES 2"
,
"AES 3"
,
"AES 4"
static
const
char
*
textsPCXHR
[
7
]
=
{
"Internal"
,
"WordClock"
,
"AES Sync"
,
"AES 1"
,
"AES 2"
,
"AES 3"
,
"AES 4"
};
static
const
char
*
textsHR22
[
3
]
=
{
"Internal"
,
"AES Sync"
,
"AES 1"
};
const
char
**
texts
;
struct
pcxhr_mgr
*
mgr
=
snd_kcontrol_chip
(
kcontrol
);
int
clock_items
=
3
+
mgr
->
capture_chips
;
int
clock_items
=
2
;
/* at least Internal and AES Sync clock */
if
(
mgr
->
board_has_aes1
)
{
clock_items
+=
mgr
->
capture_chips
;
/* add AES x */
if
(
!
mgr
->
is_hr_stereo
)
clock_items
+=
1
;
/* add word clock */
}
if
(
mgr
->
is_hr_stereo
)
{
texts
=
textsHR22
;
snd_BUG_ON
(
clock_items
>
(
HR22_CLOCK_TYPE_MAX
+
1
));
}
else
{
texts
=
textsPCXHR
;
snd_BUG_ON
(
clock_items
>
(
PCXHR_CLOCK_TYPE_MAX
+
1
));
}
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_ENUMERATED
;
uinfo
->
count
=
1
;
uinfo
->
value
.
enumerated
.
items
=
clock_items
;
...
...
@@ -667,9 +776,13 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
pcxhr_mgr
*
mgr
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
int
clock_items
=
3
+
mgr
->
capture_chips
;
int
rate
,
ret
=
0
;
unsigned
int
clock_items
=
2
;
/* at least Internal and AES Sync clock */
if
(
mgr
->
board_has_aes1
)
{
clock_items
+=
mgr
->
capture_chips
;
/* add AES x */
if
(
!
mgr
->
is_hr_stereo
)
clock_items
+=
1
;
/* add word clock */
}
if
(
ucontrol
->
value
.
enumerated
.
item
[
0
]
>=
clock_items
)
return
-
EINVAL
;
mutex_lock
(
&
mgr
->
mixer_mutex
);
...
...
@@ -677,7 +790,8 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
mutex_lock
(
&
mgr
->
setup_mutex
);
mgr
->
use_clock_type
=
ucontrol
->
value
.
enumerated
.
item
[
0
];
if
(
mgr
->
use_clock_type
)
pcxhr_get_external_clock
(
mgr
,
mgr
->
use_clock_type
,
&
rate
);
pcxhr_get_external_clock
(
mgr
,
mgr
->
use_clock_type
,
&
rate
);
else
rate
=
mgr
->
sample_rate
;
if
(
rate
)
{
...
...
@@ -747,14 +861,16 @@ static struct snd_kcontrol_new pcxhr_control_clock_rate = {
/*
* IEC958 status bits
*/
static
int
pcxhr_iec958_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
static
int
pcxhr_iec958_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_IEC958
;
uinfo
->
count
=
1
;
return
0
;
}
static
int
pcxhr_iec958_capture_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
*
aes_bits
)
static
int
pcxhr_iec958_capture_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
*
aes_bits
)
{
int
i
,
err
;
unsigned
char
temp
;
...
...
@@ -763,39 +879,61 @@ static int pcxhr_iec958_capture_byte(struct snd_pcxhr *chip, int aes_idx, unsign
pcxhr_init_rmh
(
&
rmh
,
CMD_ACCESS_IO_READ
);
rmh
.
cmd
[
0
]
|=
IO_NUM_UER_CHIP_REG
;
switch
(
chip
->
chip_idx
)
{
case
0
:
rmh
.
cmd
[
1
]
=
CS8420_01_CS
;
break
;
/* use CS8416_01_CS for AES SYNC plug */
/* instead of CS8420_01_CS use CS8416_01_CS for AES SYNC plug */
case
0
:
rmh
.
cmd
[
1
]
=
CS8420_01_CS
;
break
;
case
1
:
rmh
.
cmd
[
1
]
=
CS8420_23_CS
;
break
;
case
2
:
rmh
.
cmd
[
1
]
=
CS8420_45_CS
;
break
;
case
3
:
rmh
.
cmd
[
1
]
=
CS8420_67_CS
;
break
;
default:
return
-
EINVAL
;
}
if
(
chip
->
mgr
->
board_aes_in_192k
)
{
switch
(
aes_idx
)
{
case
0
:
rmh
.
cmd
[
2
]
=
CS8416_CSB0
;
break
;
case
1
:
rmh
.
cmd
[
2
]
=
CS8416_CSB1
;
break
;
case
2
:
rmh
.
cmd
[
2
]
=
CS8416_CSB2
;
break
;
case
3
:
rmh
.
cmd
[
2
]
=
CS8416_CSB3
;
break
;
case
4
:
rmh
.
cmd
[
2
]
=
CS8416_CSB4
;
break
;
default:
return
-
EINVAL
;
}
}
else
{
switch
(
aes_idx
)
{
case
0
:
rmh
.
cmd
[
2
]
=
CS8420_CSB0
;
break
;
/* use CS8416_CSBx for AES SYNC plug */
/* instead of CS8420_CSB0 use CS8416_CSBx for AES SYNC plug */
case
0
:
rmh
.
cmd
[
2
]
=
CS8420_CSB0
;
break
;
case
1
:
rmh
.
cmd
[
2
]
=
CS8420_CSB1
;
break
;
case
2
:
rmh
.
cmd
[
2
]
=
CS8420_CSB2
;
break
;
case
3
:
rmh
.
cmd
[
2
]
=
CS8420_CSB3
;
break
;
case
4
:
rmh
.
cmd
[
2
]
=
CS8420_CSB4
;
break
;
default:
return
-
EINVAL
;
}
rmh
.
cmd
[
1
]
&=
0x0fffff
;
/* size and code the chip id for the fpga */
rmh
.
cmd
[
2
]
&=
CHIP_SIG_AND_MAP_SPI
;
/* chip signature + map for spi read */
}
/* size and code the chip id for the fpga */
rmh
.
cmd
[
1
]
&=
0x0fffff
;
/* chip signature + map for spi read */
rmh
.
cmd
[
2
]
&=
CHIP_SIG_AND_MAP_SPI
;
rmh
.
cmd_len
=
3
;
err
=
pcxhr_send_msg
(
chip
->
mgr
,
&
rmh
);
if
(
err
)
return
err
;
if
(
chip
->
mgr
->
board_aes_in_192k
)
{
temp
=
(
unsigned
char
)
rmh
.
stat
[
1
];
}
else
{
temp
=
0
;
/* reversed bit order (not with CS8416_01_CS) */
for
(
i
=
0
;
i
<
8
;
i
++
)
{
/* attention : reversed bit order (not with CS8416_01_CS) */
temp
<<=
1
;
if
(
rmh
.
stat
[
1
]
&
(
1
<<
i
))
temp
|=
1
;
}
snd_printdd
(
"read iec958 AES %d byte %d = 0x%x
\n
"
,
chip
->
chip_idx
,
aes_idx
,
temp
);
}
snd_printdd
(
"read iec958 AES %d byte %d = 0x%x
\n
"
,
chip
->
chip_idx
,
aes_idx
,
temp
);
*
aes_bits
=
temp
;
return
0
;
}
static
int
pcxhr_iec958_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
static
int
pcxhr_iec958_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_pcxhr
*
chip
=
snd_kcontrol_chip
(
kcontrol
);
unsigned
char
aes_bits
;
...
...
@@ -806,7 +944,12 @@ static int pcxhr_iec958_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
if
(
kcontrol
->
private_value
==
0
)
/* playback */
aes_bits
=
chip
->
aes_bits
[
i
];
else
{
/* capture */
err
=
pcxhr_iec958_capture_byte
(
chip
,
i
,
&
aes_bits
);
if
(
chip
->
mgr
->
is_hr_stereo
)
err
=
hr222_iec958_capture_byte
(
chip
,
i
,
&
aes_bits
);
else
err
=
pcxhr_iec958_capture_byte
(
chip
,
i
,
&
aes_bits
);
if
(
err
)
break
;
}
...
...
@@ -825,7 +968,8 @@ static int pcxhr_iec958_mask_get(struct snd_kcontrol *kcontrol,
return
0
;
}
static
int
pcxhr_iec958_update_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
aes_bits
)
static
int
pcxhr_iec958_update_byte
(
struct
snd_pcxhr
*
chip
,
int
aes_idx
,
unsigned
char
aes_bits
)
{
int
i
,
err
,
cmd
;
unsigned
char
new_bits
=
aes_bits
;
...
...
@@ -835,7 +979,7 @@ static int pcxhr_iec958_update_byte(struct snd_pcxhr *chip, int aes_idx, unsigne
for
(
i
=
0
;
i
<
8
;
i
++
)
{
if
((
old_bits
&
0x01
)
!=
(
new_bits
&
0x01
))
{
cmd
=
chip
->
chip_idx
&
0x03
;
/* chip index 0..3 */
if
(
chip
->
chip_idx
>
3
)
if
(
chip
->
chip_idx
>
3
)
/* new bit used if chip_idx>3 (PCX1222HR) */
cmd
|=
1
<<
22
;
cmd
|=
((
aes_idx
<<
3
)
+
i
)
<<
2
;
/* add bit offset */
...
...
@@ -867,7 +1011,12 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
mutex_lock
(
&
chip
->
mgr
->
mixer_mutex
);
for
(
i
=
0
;
i
<
5
;
i
++
)
{
if
(
ucontrol
->
value
.
iec958
.
status
[
i
]
!=
chip
->
aes_bits
[
i
])
{
pcxhr_iec958_update_byte
(
chip
,
i
,
ucontrol
->
value
.
iec958
.
status
[
i
]);
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_iec958_update_byte
(
chip
,
i
,
ucontrol
->
value
.
iec958
.
status
[
i
]);
else
pcxhr_iec958_update_byte
(
chip
,
i
,
ucontrol
->
value
.
iec958
.
status
[
i
]);
changed
=
1
;
}
}
...
...
@@ -917,29 +1066,53 @@ static void pcxhr_init_audio_levels(struct snd_pcxhr *chip)
/* at boot time the digital volumes are unmuted 0dB */
for
(
j
=
0
;
j
<
PCXHR_PLAYBACK_STREAMS
;
j
++
)
{
chip
->
digital_playback_active
[
j
][
i
]
=
1
;
chip
->
digital_playback_volume
[
j
][
i
]
=
PCXHR_DIGITAL_ZERO_LEVEL
;
chip
->
digital_playback_volume
[
j
][
i
]
=
PCXHR_DIGITAL_ZERO_LEVEL
;
}
/* after boot, only two bits are set on the uer interface */
chip
->
aes_bits
[
0
]
=
IEC958_AES0_PROFESSIONAL
|
IEC958_AES0_PRO_FS_48000
;
/* only for test purpose, remove later */
/* after boot, only two bits are set on the uer
* interface
*/
chip
->
aes_bits
[
0
]
=
(
IEC958_AES0_PROFESSIONAL
|
IEC958_AES0_PRO_FS_48000
);
#ifdef CONFIG_SND_DEBUG
/* analog volumes for playback (is LEVEL_MIN after boot) */
/* analog volumes for playback
* (is LEVEL_MIN after boot)
*/
chip
->
analog_playback_active
[
i
]
=
1
;
chip
->
analog_playback_volume
[
i
]
=
PCXHR_ANALOG_PLAYBACK_ZERO_LEVEL
;
if
(
chip
->
mgr
->
is_hr_stereo
)
chip
->
analog_playback_volume
[
i
]
=
HR222_LINE_PLAYBACK_ZERO_LEVEL
;
else
{
chip
->
analog_playback_volume
[
i
]
=
PCXHR_LINE_PLAYBACK_ZERO_LEVEL
;
pcxhr_update_analog_audio_level
(
chip
,
0
,
i
);
}
#endif
/* test end */
/* stereo cards need to be initialised after boot */
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_update_analog_audio_level
(
chip
,
0
,
i
);
}
if
(
chip
->
nb_streams_capt
)
{
/* at boot time the digital volumes are unmuted 0dB */
chip
->
digital_capture_volume
[
i
]
=
PCXHR_DIGITAL_ZERO_LEVEL
;
/* only for test purpose, remove later */
chip
->
digital_capture_volume
[
i
]
=
PCXHR_DIGITAL_ZERO_LEVEL
;
chip
->
analog_capture_active
=
1
;
#ifdef CONFIG_SND_DEBUG
/* analog volumes for playback (is LEVEL_MIN after boot) */
chip
->
analog_capture_volume
[
i
]
=
PCXHR_ANALOG_CAPTURE_ZERO_LEVEL
;
/* analog volumes for playback
* (is LEVEL_MIN after boot)
*/
if
(
chip
->
mgr
->
is_hr_stereo
)
chip
->
analog_capture_volume
[
i
]
=
HR222_LINE_CAPTURE_ZERO_LEVEL
;
else
{
chip
->
analog_capture_volume
[
i
]
=
PCXHR_LINE_CAPTURE_ZERO_LEVEL
;
pcxhr_update_analog_audio_level
(
chip
,
1
,
i
);
}
#endif
/* test end */
/* stereo cards need to be initialised after boot */
if
(
chip
->
mgr
->
is_hr_stereo
)
hr222_update_analog_audio_level
(
chip
,
1
,
i
);
}
}
...
...
@@ -963,90 +1136,125 @@ int pcxhr_create_mixer(struct pcxhr_mgr *mgr)
temp
=
pcxhr_control_analog_level
;
temp
.
name
=
"Master Playback Volume"
;
temp
.
private_value
=
0
;
/* playback */
if
(
mgr
->
is_hr_stereo
)
temp
.
tlv
.
p
=
db_scale_a_hr222_playback
;
else
temp
.
tlv
.
p
=
db_scale_analog_playback
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
));
if
(
err
<
0
)
return
err
;
/* output mute controls */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_output_switch
,
chip
)))
<
0
)
chip
));
if
(
err
<
0
)
return
err
;
temp
=
snd_pcxhr_pcm_vol
;
temp
.
name
=
"PCM Playback Volume"
;
temp
.
count
=
PCXHR_PLAYBACK_STREAMS
;
temp
.
private_value
=
0
;
/* playback */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
));
if
(
err
<
0
)
return
err
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_pcm_switch
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_pcm_switch
,
chip
));
if
(
err
<
0
)
return
err
;
/* IEC958 controls */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_playback_iec958_mask
,
chip
)))
<
0
)
chip
));
if
(
err
<
0
)
return
err
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_playback_iec958
,
chip
)))
<
0
)
chip
));
if
(
err
<
0
)
return
err
;
}
if
(
chip
->
nb_streams_capt
)
{
/* analog input level control
only on first two chips !
*/
/* analog input level control */
temp
=
pcxhr_control_analog_level
;
temp
.
name
=
"
Master
Capture Volume"
;
temp
.
name
=
"
Line
Capture Volume"
;
temp
.
private_value
=
1
;
/* capture */
if
(
mgr
->
is_hr_stereo
)
temp
.
tlv
.
p
=
db_scale_a_hr222_capture
;
else
temp
.
tlv
.
p
=
db_scale_analog_capture
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
));
if
(
err
<
0
)
return
err
;
temp
=
snd_pcxhr_pcm_vol
;
temp
.
name
=
"PCM Capture Volume"
;
temp
.
count
=
1
;
temp
.
private_value
=
1
;
/* capture */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
temp
,
chip
));
if
(
err
<
0
)
return
err
;
/* Audio source */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_audio_src
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_audio_src
,
chip
));
if
(
err
<
0
)
return
err
;
/* IEC958 controls */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_capture_iec958_mask
,
chip
)))
<
0
)
chip
));
if
(
err
<
0
)
return
err
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_capture_iec958
,
chip
)))
<
0
)
chip
));
if
(
err
<
0
)
return
err
;
if
(
mgr
->
is_hr_stereo
)
{
err
=
hr222_add_mic_controls
(
chip
);
if
(
err
<
0
)
return
err
;
}
}
/* monitoring only if playback and capture device available */
if
(
chip
->
nb_streams_capt
>
0
&&
chip
->
nb_streams_play
>
0
)
{
/* monitoring */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_monitor_vol
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_monitor_vol
,
chip
));
if
(
err
<
0
)
return
err
;
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_monitor_sw
,
chip
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_monitor_sw
,
chip
));
if
(
err
<
0
)
return
err
;
}
if
(
i
==
0
)
{
/* clock mode only one control per pcxhr */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_clock_type
,
mgr
)))
<
0
)
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_clock_type
,
mgr
));
if
(
err
<
0
)
return
err
;
/* non standard control used to scan the external clock presence/frequencies */
if
((
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_clock_rate
,
mgr
)))
<
0
)
/* non standard control used to scan
* the external clock presence/frequencies
*/
err
=
snd_ctl_add
(
chip
->
card
,
snd_ctl_new1
(
&
pcxhr_control_clock_rate
,
mgr
));
if
(
err
<
0
)
return
err
;
}
...
...
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