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
c1a7c40c
Commit
c1a7c40c
authored
Feb 03, 2017
by
Takashi Iwai
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/intel-lpe-audio' into for-next
Lots of cleanups and refactoring of Intel LPE audio driver.
parents
374a5040
91b0cb0c
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1034 additions
and
2679 deletions
+1034
-2679
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_reg.h
+3
-0
drivers/gpu/drm/i915/intel_lpe_audio.c
drivers/gpu/drm/i915/intel_lpe_audio.c
+5
-0
sound/x86/Makefile
sound/x86/Makefile
+1
-3
sound/x86/intel_hdmi_audio.c
sound/x86/intel_hdmi_audio.c
+951
-950
sound/x86/intel_hdmi_audio.h
sound/x86/intel_hdmi_audio.h
+20
-97
sound/x86/intel_hdmi_audio_if.c
sound/x86/intel_hdmi_audio_if.c
+0
-548
sound/x86/intel_hdmi_lpe_audio.c
sound/x86/intel_hdmi_lpe_audio.c
+0
-665
sound/x86/intel_hdmi_lpe_audio.h
sound/x86/intel_hdmi_lpe_audio.h
+54
-416
No files found.
drivers/gpu/drm/i915/i915_reg.h
View file @
c1a7c40c
...
...
@@ -2062,6 +2062,9 @@ enum skl_disp_power_wells {
#define I915_HDMI_LPE_AUDIO_SIZE 0x1000
/* DisplayPort Audio w/ LPE */
#define VLV_AUD_CHICKEN_BIT_REG _MMIO(VLV_DISPLAY_BASE + 0x62F38)
#define VLV_CHICKEN_BIT_DBG_ENABLE (1 << 0)
#define _VLV_AUD_PORT_EN_B_DBG (VLV_DISPLAY_BASE + 0x62F20)
#define _VLV_AUD_PORT_EN_C_DBG (VLV_DISPLAY_BASE + 0x62F30)
#define _VLV_AUD_PORT_EN_D_DBG (VLV_DISPLAY_BASE + 0x62F34)
...
...
drivers/gpu/drm/i915/intel_lpe_audio.c
View file @
c1a7c40c
...
...
@@ -248,6 +248,11 @@ static int lpe_audio_setup(struct drm_i915_private *dev_priv)
goto
err_free_irq
;
}
/* enable chicken bit; at least this is required for Dell Wyse 3040
* with DP outputs (but only sometimes by some reason!)
*/
I915_WRITE
(
VLV_AUD_CHICKEN_BIT_REG
,
VLV_CHICKEN_BIT_DBG_ENABLE
);
return
0
;
err_free_irq:
irq_free_desc
(
dev_priv
->
lpe_audio
.
irq
);
...
...
sound/x86/Makefile
View file @
c1a7c40c
snd-hdmi-lpe-audio-objs
+=
\
intel_hdmi_audio.o
\
intel_hdmi_audio_if.o
\
intel_hdmi_lpe_audio.o
intel_hdmi_audio.o
obj-$(CONFIG_HDMI_LPE_AUDIO)
+=
snd-hdmi-lpe-audio.o
sound/x86/intel_hdmi_audio.c
View file @
c1a7c40c
...
...
@@ -21,29 +21,27 @@
* ALSA driver for Intel HDMI audio
*/
#define pr_fmt(fmt) "had: " fmt
#include <linux/types.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/acpi.h>
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <asm/cacheflush.h>
#include <sound/pcm.h>
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <drm/drm_edid.h>
#include <drm/intel_lpe_audio.h>
#include "intel_hdmi_audio.h"
static
DEFINE_MUTEX
(
had_mutex
);
/*standard module options for ALSA. This module supports only one card*/
static
int
hdmi_card_index
=
SNDRV_DEFAULT_IDX1
;
static
char
*
hdmi_card_id
=
SNDRV_DEFAULT_STR1
;
static
struct
snd_intelhad
*
had_data
;
static
int
underrun_count
;
module_param_named
(
index
,
hdmi_card_index
,
int
,
0444
);
MODULE_PARM_DESC
(
index
,
...
...
@@ -55,7 +53,7 @@ MODULE_PARM_DESC(id,
/*
* ELD SA bits in the CEA Speaker Allocation data block
*/
static
int
eld_speaker_allocation_bits
[]
=
{
static
const
int
eld_speaker_allocation_bits
[]
=
{
[
0
]
=
FL
|
FR
,
[
1
]
=
LFE
,
[
2
]
=
FC
,
...
...
@@ -118,7 +116,7 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
{
.
ca_index
=
0x1f
,
.
speakers
=
{
FRC
,
FLC
,
RR
,
RL
,
FC
,
LFE
,
FR
,
FL
}
},
};
static
struct
channel_map_table
map_tables
[]
=
{
static
const
struct
channel_map_table
map_tables
[]
=
{
{
SNDRV_CHMAP_FL
,
0x00
,
FL
},
{
SNDRV_CHMAP_FR
,
0x01
,
FR
},
{
SNDRV_CHMAP_RL
,
0x04
,
RL
},
...
...
@@ -158,89 +156,101 @@ static const struct snd_pcm_hardware snd_intel_hadstream = {
.
fifo_size
=
HAD_FIFO_SIZE
,
};
/* Register access functions */
int
had_get_hwstate
(
struct
snd_intelhad
*
intelhaddata
)
/* Get the active PCM substream;
* Call had_substream_put() for unreferecing.
* Don't call this inside had_spinlock, as it takes by itself
*/
static
struct
snd_pcm_substream
*
had_substream_get
(
struct
snd_intelhad
*
intelhaddata
)
{
/* Check for device presence -SW state */
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
{
pr_debug
(
"%s:Device not connected:%d
\n
"
,
__func__
,
intelhaddata
->
drv_status
);
return
-
ENODEV
;
}
struct
snd_pcm_substream
*
substream
;
unsigned
long
flags
;
return
0
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flags
);
substream
=
intelhaddata
->
stream_info
.
substream
;
if
(
substream
)
intelhaddata
->
stream_info
.
substream_refcount
++
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
return
substream
;
}
int
had_get_caps
(
enum
had_caps_list
query
,
void
*
caps
)
/* Unref the active PCM substream;
* Don't call this inside had_spinlock, as it takes by itself
*/
static
void
had_substream_put
(
struct
snd_intelhad
*
intelhaddata
)
{
int
retval
;
struct
snd_intelhad
*
intelhaddata
=
had_data
;
retval
=
had_get_hwstate
(
intelhaddata
);
if
(
!
retval
)
retval
=
intelhaddata
->
query_ops
.
hdmi_audio_get_caps
(
query
,
caps
);
unsigned
long
flags
;
return
retval
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flags
);
intelhaddata
->
stream_info
.
substream_refcount
--
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
}
int
had_set_caps
(
enum
had_caps_list
set_element
,
void
*
caps
)
/* Register access functions */
static
inline
void
mid_hdmi_audio_read
(
struct
snd_intelhad
*
ctx
,
u32
reg
,
u32
*
val
)
{
int
retval
;
struct
snd_intelhad
*
intelhaddata
=
had_data
;
retval
=
had_get_hwstate
(
intelhaddata
);
if
(
!
retval
)
retval
=
intelhaddata
->
query_ops
.
hdmi_audio_set_caps
(
set_element
,
caps
);
return
retval
;
*
val
=
ioread32
(
ctx
->
mmio_start
+
ctx
->
had_config_offset
+
reg
);
}
int
had_read_register
(
u32
offset
,
u32
*
data
)
static
inline
void
mid_hdmi_audio_write
(
struct
snd_intelhad
*
ctx
,
u32
reg
,
u32
val
)
{
i
nt
retval
;
struct
snd_intelhad
*
intelhaddata
=
had_data
;
i
owrite32
(
val
,
ctx
->
mmio_start
+
ctx
->
had_config_offset
+
reg
)
;
}
retval
=
had_get_hwstate
(
intelhaddata
);
if
(
!
retval
)
retval
=
intelhaddata
->
reg_ops
.
hdmi_audio_read_register
(
offset
+
intelhaddata
->
audio_cfg_offset
,
data
);
static
int
had_read_register
(
struct
snd_intelhad
*
intelhaddata
,
u32
offset
,
u32
*
data
)
{
if
(
!
intelhaddata
->
connected
)
return
-
ENODEV
;
return
retval
;
mid_hdmi_audio_read
(
intelhaddata
,
offset
,
data
);
return
0
;
}
int
had_write_register
(
u32
offset
,
u32
data
)
static
void
fixup_dp_config
(
struct
snd_intelhad
*
intelhaddata
,
u32
offset
,
u32
*
data
)
{
int
retval
;
struct
snd_intelhad
*
intelhaddata
=
had_data
;
if
(
intelhaddata
->
dp_output
)
{
if
(
offset
==
AUD_CONFIG
&&
(
*
data
&
AUD_CONFIG_VALID_BIT
))
*
data
|=
AUD_CONFIG_DP_MODE
|
AUD_CONFIG_BLOCK_BIT
;
}
}
retval
=
had_get_hwstate
(
intelhaddata
);
if
(
!
retval
)
retval
=
intelhaddata
->
reg_ops
.
hdmi_audio_write_register
(
offset
+
intelhaddata
->
audio_cfg_offset
,
data
);
static
int
had_write_register
(
struct
snd_intelhad
*
intelhaddata
,
u32
offset
,
u32
data
)
{
if
(
!
intelhaddata
->
connected
)
return
-
ENODEV
;
return
retval
;
fixup_dp_config
(
intelhaddata
,
offset
,
&
data
);
mid_hdmi_audio_write
(
intelhaddata
,
offset
,
data
);
return
0
;
}
int
had_read_modify
(
u32
offset
,
u32
data
,
u32
mask
)
static
int
had_read_modify
(
struct
snd_intelhad
*
intelhaddata
,
u32
offset
,
u32
data
,
u32
mask
)
{
int
retval
;
struct
snd_intelhad
*
intelhaddata
=
had_data
;
u32
val_tmp
;
if
(
!
intelhaddata
->
connected
)
return
-
ENODEV
;
retval
=
had_get_hwstate
(
intelhaddata
);
if
(
!
retval
)
retval
=
intelhaddata
->
reg_ops
.
hdmi_audio_read_modify
(
offset
+
intelhaddata
->
audio_cfg_offset
,
data
,
mask
);
mid_hdmi_audio_read
(
intelhaddata
,
offset
,
&
val_tmp
);
val_tmp
&=
~
mask
;
val_tmp
|=
(
data
&
mask
);
return
retval
;
fixup_dp_config
(
intelhaddata
,
offset
,
&
val_tmp
);
mid_hdmi_audio_write
(
intelhaddata
,
offset
,
val_tmp
);
return
0
;
}
/**
* function to read-modify
* AUD_CONFIG register on VLV2.The had_read_modify() function should not
* directly be used on VLV2 for updating AUD_CONFIG register.
/*
* enable / disable audio configuration
*
* The had_read_modify() function should not directly be used on VLV2 for
* updating AUD_CONFIG register.
* This is because:
* Bit6 of AUD_CONFIG register is writeonly due to a silicon bug on VLV2
* HDMI IP. As a result a read-modify of AUD_CONFIG regiter will always
...
...
@@ -250,206 +260,147 @@ int had_read_modify(u32 offset, u32 data, u32 mask)
* causes the "channels" field to be updated as 0xy binary resulting in
* bad audio. The fix is to always write the AUD_CONFIG[6:4] with
* appropriate value when doing read-modify of AUD_CONFIG register.
*
* @substream: the current substream or NULL if no active substream
* @data : data to be written
* @mask : mask
*
*/
static
int
had_read_modify_aud_config_v2
(
struct
snd_pcm_substream
*
substream
,
u32
data
,
u32
mask
)
static
void
snd_intelhad_enable_audio
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
,
bool
enable
)
{
union
aud_cfg
cfg_val
=
{.
cfg_
regval
=
0
};
u8
channels
;
union
aud_cfg
cfg_val
=
{.
regval
=
0
};
u8
channels
,
data
,
mask
;
/*
* If substream is NULL, there is no active stream.
* In this case just set channels to 2
*/
if
(
substream
)
channels
=
substream
->
runtime
->
channels
;
else
channels
=
2
;
cfg_val
.
cfg_regx_v2
.
num_ch
=
channels
-
2
;
data
=
data
|
cfg_val
.
cfg_regval
;
mask
=
mask
|
AUD_CONFIG_CH_MASK_V2
;
channels
=
substream
?
substream
->
runtime
->
channels
:
2
;
cfg_val
.
regx
.
num_ch
=
channels
-
2
;
pr_debug
(
"%s : data = %x, mask =%x
\n
"
,
__func__
,
data
,
mask
);
data
=
cfg_val
.
regval
;
if
(
enable
)
data
|=
1
;
mask
=
AUD_CONFIG_CH_MASK
|
1
;
return
had_read_modify
(
AUD_CONFIG
,
data
,
mask
);
}
dev_dbg
(
intelhaddata
->
dev
,
"%s : data = %x, mask =%x
\n
"
,
__func__
,
data
,
mask
);
static
void
snd_intelhad_enable_audio_v1
(
struct
snd_pcm_substream
*
substream
,
u8
enable
)
{
had_read_modify
(
AUD_CONFIG
,
enable
,
BIT
(
0
));
had_read_modify
(
intelhaddata
,
AUD_CONFIG
,
data
,
mask
);
}
static
void
snd_intelhad_enable_audio_v2
(
struct
snd_pcm_substream
*
substream
,
u8
enable
)
/* enable / disable the audio interface */
static
void
snd_intelhad_enable_audio_int
(
struct
snd_intelhad
*
ctx
,
bool
enable
)
{
had_read_modify_aud_config_v2
(
substream
,
enable
,
BIT
(
0
));
}
u32
status_reg
;
static
void
snd_intelhad_reset_audio_v1
(
u8
reset
)
{
had_write_register
(
AUD_HDMI_STATUS
,
reset
);
if
(
enable
)
{
mid_hdmi_audio_read
(
ctx
,
AUD_HDMI_STATUS
,
&
status_reg
);
status_reg
|=
HDMI_AUDIO_BUFFER_DONE
|
HDMI_AUDIO_UNDERRUN
;
mid_hdmi_audio_write
(
ctx
,
AUD_HDMI_STATUS
,
status_reg
);
mid_hdmi_audio_read
(
ctx
,
AUD_HDMI_STATUS
,
&
status_reg
);
}
}
static
void
snd_intelhad_reset_audio_v2
(
u8
reset
)
static
void
snd_intelhad_reset_audio
(
struct
snd_intelhad
*
intelhaddata
,
u8
reset
)
{
had_write_register
(
AUD_HDMI_STATUS_v2
,
reset
);
had_write_register
(
intelhaddata
,
AUD_HDMI_STATUS
,
reset
);
}
/*
*
/*
* initialize audio channel status registers
* This function is called in the prepare callback
*/
static
int
had_prog_status_reg
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
{
union
aud_cfg
cfg_val
=
{.
cfg_
regval
=
0
};
union
aud_ch_status_0
ch_stat0
=
{.
status_0_
regval
=
0
};
union
aud_ch_status_1
ch_stat1
=
{.
status_1_
regval
=
0
};
union
aud_cfg
cfg_val
=
{.
regval
=
0
};
union
aud_ch_status_0
ch_stat0
=
{.
regval
=
0
};
union
aud_ch_status_1
ch_stat1
=
{.
regval
=
0
};
int
format
;
pr_debug
(
"Entry %s
\n
"
,
__func__
);
ch_stat0
.
status_0_regx
.
lpcm_id
=
(
intelhaddata
->
aes_bits
&
IEC958_AES0_NONAUDIO
)
>>
1
;
ch_stat0
.
status_0_regx
.
clk_acc
=
(
intelhaddata
->
aes_bits
&
IEC958_AES3_CON_CLOCK
)
>>
4
;
cfg_val
.
cfg_regx
.
val_bit
=
ch_stat0
.
status_0_regx
.
lpcm_id
;
ch_stat0
.
regx
.
lpcm_id
=
(
intelhaddata
->
aes_bits
&
IEC958_AES0_NONAUDIO
)
>>
1
;
ch_stat0
.
regx
.
clk_acc
=
(
intelhaddata
->
aes_bits
&
IEC958_AES3_CON_CLOCK
)
>>
4
;
cfg_val
.
regx
.
val_bit
=
ch_stat0
.
regx
.
lpcm_id
;
switch
(
substream
->
runtime
->
rate
)
{
case
AUD_SAMPLE_RATE_32
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_32KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_32KHZ
;
break
;
case
AUD_SAMPLE_RATE_44_1
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_44KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_44KHZ
;
break
;
case
AUD_SAMPLE_RATE_48
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_48KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_48KHZ
;
break
;
case
AUD_SAMPLE_RATE_88_2
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_88KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_88KHZ
;
break
;
case
AUD_SAMPLE_RATE_96
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_96KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_96KHZ
;
break
;
case
AUD_SAMPLE_RATE_176_4
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_176KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_176KHZ
;
break
;
case
AUD_SAMPLE_RATE_192
:
ch_stat0
.
status_0_
regx
.
samp_freq
=
CH_STATUS_MAP_192KHZ
;
ch_stat0
.
regx
.
samp_freq
=
CH_STATUS_MAP_192KHZ
;
break
;
default:
/* control should never come here */
return
-
EINVAL
;
break
;
}
had_write_register
(
AUD_CH_STATUS_0
,
ch_stat0
.
status_0_regval
);
had_write_register
(
intelhaddata
,
AUD_CH_STATUS_0
,
ch_stat0
.
regval
);
format
=
substream
->
runtime
->
format
;
if
(
format
==
SNDRV_PCM_FORMAT_S16_LE
)
{
ch_stat1
.
status_1_
regx
.
max_wrd_len
=
MAX_SMPL_WIDTH_20
;
ch_stat1
.
status_1_
regx
.
wrd_len
=
SMPL_WIDTH_16BITS
;
ch_stat1
.
regx
.
max_wrd_len
=
MAX_SMPL_WIDTH_20
;
ch_stat1
.
regx
.
wrd_len
=
SMPL_WIDTH_16BITS
;
}
else
if
(
format
==
SNDRV_PCM_FORMAT_S24_LE
)
{
ch_stat1
.
status_1_
regx
.
max_wrd_len
=
MAX_SMPL_WIDTH_24
;
ch_stat1
.
status_1_
regx
.
wrd_len
=
SMPL_WIDTH_24BITS
;
ch_stat1
.
regx
.
max_wrd_len
=
MAX_SMPL_WIDTH_24
;
ch_stat1
.
regx
.
wrd_len
=
SMPL_WIDTH_24BITS
;
}
else
{
ch_stat1
.
status_1_
regx
.
max_wrd_len
=
0
;
ch_stat1
.
status_1_
regx
.
wrd_len
=
0
;
ch_stat1
.
regx
.
max_wrd_len
=
0
;
ch_stat1
.
regx
.
wrd_len
=
0
;
}
had_write_register
(
AUD_CH_STATUS_1
,
ch_stat1
.
status_1_regval
);
had_write_register
(
intelhaddata
,
AUD_CH_STATUS_1
,
ch_stat1
.
regval
);
return
0
;
}
/*
*
/*
* function to initialize audio
* registers and buffer confgiuration registers
* This function is called in the prepare callback
*/
static
int
snd_intelhad_
prog_audio_ctrl_v2
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
static
int
snd_intelhad_
audio_ctrl
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
{
union
aud_cfg
cfg_val
=
{.
cfg_
regval
=
0
};
union
aud_buf_config
buf_cfg
=
{.
buf_cf
gval
=
0
};
union
aud_cfg
cfg_val
=
{.
regval
=
0
};
union
aud_buf_config
buf_cfg
=
{.
re
gval
=
0
};
u8
channels
;
had_prog_status_reg
(
substream
,
intelhaddata
);
buf_cfg
.
buf_cfg_regx_v2
.
audio_fifo_watermark
=
FIFO_THRESHOLD
;
buf_cfg
.
buf_cfg_regx_v2
.
dma_fifo_watermark
=
DMA_FIFO_THRESHOLD
;
buf_cfg
.
buf_cfg_regx_v2
.
aud_delay
=
0
;
had_write_register
(
AUD_BUF_CONFIG
,
buf_cfg
.
buf_cf
gval
);
buf_cfg
.
regx
.
audio_fifo_watermark
=
FIFO_THRESHOLD
;
buf_cfg
.
regx
.
dma_fifo_watermark
=
DMA_FIFO_THRESHOLD
;
buf_cfg
.
regx
.
aud_delay
=
0
;
had_write_register
(
intelhaddata
,
AUD_BUF_CONFIG
,
buf_cfg
.
re
gval
);
channels
=
substream
->
runtime
->
channels
;
cfg_val
.
cfg_regx_v2
.
num_ch
=
channels
-
2
;
cfg_val
.
regx
.
num_ch
=
channels
-
2
;
if
(
channels
<=
2
)
cfg_val
.
cfg_regx_v2
.
layout
=
LAYOUT0
;
cfg_val
.
regx
.
layout
=
LAYOUT0
;
else
cfg_val
.
cfg_regx_v2
.
layout
=
LAYOUT1
;
cfg_val
.
regx
.
layout
=
LAYOUT1
;
cfg_val
.
cfg_regx_v2
.
val_bit
=
1
;
had_write_register
(
AUD_CONFIG
,
cfg_val
.
cfg_regval
);
return
0
;
}
/**
* function to initialize audio
* registers and buffer confgiuration registers
* This function is called in the prepare callback
*/
static
int
snd_intelhad_prog_audio_ctrl_v1
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
{
union
aud_cfg
cfg_val
=
{.
cfg_regval
=
0
};
union
aud_buf_config
buf_cfg
=
{.
buf_cfgval
=
0
};
u8
channels
;
had_prog_status_reg
(
substream
,
intelhaddata
);
buf_cfg
.
buf_cfg_regx
.
fifo_width
=
FIFO_THRESHOLD
;
buf_cfg
.
buf_cfg_regx
.
aud_delay
=
0
;
had_write_register
(
AUD_BUF_CONFIG
,
buf_cfg
.
buf_cfgval
);
channels
=
substream
->
runtime
->
channels
;
switch
(
channels
)
{
case
1
:
case
2
:
cfg_val
.
cfg_regx
.
num_ch
=
CH_STEREO
;
cfg_val
.
cfg_regx
.
layout
=
LAYOUT0
;
break
;
case
3
:
case
4
:
cfg_val
.
cfg_regx
.
num_ch
=
CH_THREE_FOUR
;
cfg_val
.
cfg_regx
.
layout
=
LAYOUT1
;
break
;
case
5
:
case
6
:
cfg_val
.
cfg_regx
.
num_ch
=
CH_FIVE_SIX
;
cfg_val
.
cfg_regx
.
layout
=
LAYOUT1
;
break
;
case
7
:
case
8
:
cfg_val
.
cfg_regx
.
num_ch
=
CH_SEVEN_EIGHT
;
cfg_val
.
cfg_regx
.
layout
=
LAYOUT1
;
break
;
}
cfg_val
.
cfg_regx
.
val_bit
=
1
;
had_write_register
(
AUD_CONFIG
,
cfg_val
.
cfg_regval
);
cfg_val
.
regx
.
val_bit
=
1
;
had_write_register
(
intelhaddata
,
AUD_CONFIG
,
cfg_val
.
regval
);
return
0
;
}
...
...
@@ -461,8 +412,6 @@ static void init_channel_allocations(void)
int
i
,
j
;
struct
cea_channel_speaker_allocation
*
p
;
pr_debug
(
"%s: Enter
\n
"
,
__func__
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
channel_allocations
);
i
++
)
{
p
=
channel_allocations
+
i
;
p
->
channels
=
0
;
...
...
@@ -504,7 +453,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata,
*/
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
eld_speaker_allocation_bits
);
i
++
)
{
if
(
intelhaddata
->
e
eld
.
speaker_allocation_block
&
(
1
<<
i
))
if
(
intelhaddata
->
e
ld
[
DRM_ELD_SPEAKER
]
&
(
1
<<
i
))
spk_mask
|=
eld_speaker_allocation_bits
[
i
];
}
...
...
@@ -518,7 +467,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata,
}
}
pr_debug
(
"HDMI:
select CA 0x%x for %d
\n
"
,
ca
,
channels
);
dev_dbg
(
intelhaddata
->
dev
,
"
select CA 0x%x for %d
\n
"
,
ca
,
channels
);
return
ca
;
}
...
...
@@ -526,7 +475,7 @@ static int snd_intelhad_channel_allocation(struct snd_intelhad *intelhaddata,
/* from speaker bit mask to ALSA API channel position */
static
int
spk_to_chmap
(
int
spk
)
{
struct
channel_map_table
*
t
=
map_tables
;
const
struct
channel_map_table
*
t
=
map_tables
;
for
(;
t
->
map
;
t
++
)
{
if
(
t
->
spk_mask
==
spk
)
...
...
@@ -535,25 +484,22 @@ static int spk_to_chmap(int spk)
return
0
;
}
void
had_build_channel_allocation_map
(
struct
snd_intelhad
*
intelhaddata
)
static
void
had_build_channel_allocation_map
(
struct
snd_intelhad
*
intelhaddata
)
{
int
i
=
0
,
c
=
0
;
int
i
,
c
;
int
spk_mask
=
0
;
struct
snd_pcm_chmap_elem
*
chmap
;
u8
eld_high
,
eld_high_mask
=
0xF0
;
u8
high_msb
;
chmap
=
kzalloc
(
sizeof
(
*
chmap
),
GFP_KERNEL
);
if
(
chmap
==
NULL
)
{
if
(
!
chmap
)
{
intelhaddata
->
chmap
->
chmap
=
NULL
;
return
;
}
had_get_caps
(
HAD_GET_ELD
,
&
intelhaddata
->
eeld
);
had_get_caps
(
HAD_GET_DP_OUTPUT
,
&
intelhaddata
->
dp_output
);
pr_debug
(
"eeld.speaker_allocation_block = %x
\n
"
,
intelhaddata
->
eeld
.
speaker_allocation_block
);
dev_dbg
(
intelhaddata
->
dev
,
"eld speaker = %x
\n
"
,
intelhaddata
->
eld
[
DRM_ELD_SPEAKER
]);
/* WA: Fix the max channel supported to 8 */
...
...
@@ -564,14 +510,14 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata)
*/
/* if 0x2F < eld < 0x4F fall back to 0x2f, else fall back to 0x4F */
eld_high
=
intelhaddata
->
e
eld
.
speaker_allocation_block
&
eld_high_mask
;
eld_high
=
intelhaddata
->
e
ld
[
DRM_ELD_SPEAKER
]
&
eld_high_mask
;
if
((
eld_high
&
(
eld_high
-
1
))
&&
(
eld_high
>
0x1F
))
{
/* eld_high & (eld_high-1): if more than 1 bit set */
/* 0x1F: 7 channels */
for
(
i
=
1
;
i
<
4
;
i
++
)
{
high_msb
=
eld_high
&
(
0x80
>>
i
);
if
(
high_msb
)
{
intelhaddata
->
e
eld
.
speaker_allocation_block
&=
intelhaddata
->
e
ld
[
DRM_ELD_SPEAKER
]
&=
high_msb
|
0xF
;
break
;
}
...
...
@@ -579,7 +525,7 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata)
}
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
eld_speaker_allocation_bits
);
i
++
)
{
if
(
intelhaddata
->
e
eld
.
speaker_allocation_block
&
(
1
<<
i
))
if
(
intelhaddata
->
e
ld
[
DRM_ELD_SPEAKER
]
&
(
1
<<
i
))
spk_mask
|=
eld_speaker_allocation_bits
[
i
];
}
...
...
@@ -588,7 +534,7 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata)
for
(
c
=
0
;
c
<
channel_allocations
[
i
].
channels
;
c
++
)
{
chmap
->
map
[
c
]
=
spk_to_chmap
(
channel_allocations
[
i
].
speakers
[
(
MAX_SPEAKERS
-
1
)
-
c
]);
(
MAX_SPEAKERS
-
1
)
-
c
]);
}
chmap
->
channels
=
channel_allocations
[
i
].
channels
;
intelhaddata
->
chmap
->
chmap
=
chmap
;
...
...
@@ -610,7 +556,7 @@ static int had_chmap_ctl_info(struct snd_kcontrol *kcontrol,
struct
snd_pcm_chmap
*
info
=
snd_kcontrol_chip
(
kcontrol
);
struct
snd_intelhad
*
intelhaddata
=
info
->
private_data
;
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
if
(
!
intelhaddata
->
connected
)
return
-
ENODEV
;
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_INTEGER
;
uinfo
->
count
=
HAD_MAX_CHANNEL
;
...
...
@@ -624,18 +570,22 @@ static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol,
{
struct
snd_pcm_chmap
*
info
=
snd_kcontrol_chip
(
kcontrol
);
struct
snd_intelhad
*
intelhaddata
=
info
->
private_data
;
int
i
=
0
;
int
i
;
const
struct
snd_pcm_chmap_elem
*
chmap
;
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
if
(
!
intelhaddata
->
connected
)
return
-
ENODEV
;
if
(
intelhaddata
->
chmap
->
chmap
==
NULL
)
mutex_lock
(
&
intelhaddata
->
mutex
);
if
(
!
intelhaddata
->
chmap
->
chmap
)
{
mutex_unlock
(
&
intelhaddata
->
mutex
);
return
-
ENODATA
;
}
chmap
=
intelhaddata
->
chmap
->
chmap
;
for
(
i
=
0
;
i
<
chmap
->
channels
;
i
++
)
{
for
(
i
=
0
;
i
<
chmap
->
channels
;
i
++
)
ucontrol
->
value
.
integer
.
value
[
i
]
=
chmap
->
map
[
i
];
pr_debug
(
"chmap->map[%d] = %d
\n
"
,
i
,
chmap
->
map
[
i
]);
}
mutex_unlock
(
&
intelhaddata
->
mutex
);
return
0
;
}
...
...
@@ -643,7 +593,7 @@ static int had_chmap_ctl_get(struct snd_kcontrol *kcontrol,
static
int
had_register_chmap_ctls
(
struct
snd_intelhad
*
intelhaddata
,
struct
snd_pcm
*
pcm
)
{
int
err
=
0
;
int
err
;
err
=
snd_pcm_add_chmap_ctls
(
pcm
,
SNDRV_PCM_STREAM_PLAYBACK
,
NULL
,
0
,
(
unsigned
long
)
intelhaddata
,
...
...
@@ -652,142 +602,75 @@ static int had_register_chmap_ctls(struct snd_intelhad *intelhaddata,
return
err
;
intelhaddata
->
chmap
->
private_data
=
intelhaddata
;
intelhaddata
->
kctl
=
intelhaddata
->
chmap
->
kctl
;
intelhaddata
->
kctl
->
info
=
had_chmap_ctl_info
;
intelhaddata
->
kctl
->
get
=
had_chmap_ctl_get
;
intelhaddata
->
chmap
->
kctl
->
info
=
had_chmap_ctl_info
;
intelhaddata
->
chmap
->
kctl
->
get
=
had_chmap_ctl_get
;
intelhaddata
->
chmap
->
chmap
=
NULL
;
return
0
;
}
/**
* snd_intelhad_prog_dip_v1 - to initialize Data Island Packets registers
*
* @substream:substream for which the prepare function is called
* @intelhaddata:substream private data
*
* This function is called in the prepare callback
*/
static
void
snd_intelhad_prog_dip_v1
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
{
int
i
;
union
aud_ctrl_st
ctrl_state
=
{.
ctrl_val
=
0
};
union
aud_info_frame2
frame2
=
{.
fr2_val
=
0
};
union
aud_info_frame3
frame3
=
{.
fr3_val
=
0
};
u8
checksum
=
0
;
int
channels
;
channels
=
substream
->
runtime
->
channels
;
had_write_register
(
AUD_CNTL_ST
,
ctrl_state
.
ctrl_val
);
frame2
.
fr2_regx
.
chnl_cnt
=
substream
->
runtime
->
channels
-
1
;
frame3
.
fr3_regx
.
chnl_alloc
=
snd_intelhad_channel_allocation
(
intelhaddata
,
channels
);
/*Calculte the byte wide checksum for all valid DIP words*/
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
HDMI_INFO_FRAME_WORD1
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
frame2
.
fr2_val
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
frame3
.
fr3_val
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
frame2
.
fr2_regx
.
chksum
=
-
(
checksum
);
had_write_register
(
AUD_HDMIW_INFOFR
,
HDMI_INFO_FRAME_WORD1
);
had_write_register
(
AUD_HDMIW_INFOFR
,
frame2
.
fr2_val
);
had_write_register
(
AUD_HDMIW_INFOFR
,
frame3
.
fr3_val
);
/* program remaining DIP words with zero */
for
(
i
=
0
;
i
<
HAD_MAX_DIP_WORDS
-
VALID_DIP_WORDS
;
i
++
)
had_write_register
(
AUD_HDMIW_INFOFR
,
0x0
);
ctrl_state
.
ctrl_regx
.
dip_freq
=
1
;
ctrl_state
.
ctrl_regx
.
dip_en_sta
=
1
;
had_write_register
(
AUD_CNTL_ST
,
ctrl_state
.
ctrl_val
);
}
/**
* snd_intelhad_prog_dip_v2 - to initialize Data Island Packets registers
*
* @substream:substream for which the prepare function is called
* @intelhaddata:substream private data
*
/*
* Initialize Data Island Packets registers
* This function is called in the prepare callback
*/
static
void
snd_intelhad_prog_dip
_v2
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
static
void
snd_intelhad_prog_dip
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
)
{
int
i
;
union
aud_ctrl_st
ctrl_state
=
{.
ctrl_
val
=
0
};
union
aud_info_frame2
frame2
=
{.
fr2_
val
=
0
};
union
aud_info_frame3
frame3
=
{.
fr3_
val
=
0
};
union
aud_ctrl_st
ctrl_state
=
{.
reg
val
=
0
};
union
aud_info_frame2
frame2
=
{.
reg
val
=
0
};
union
aud_info_frame3
frame3
=
{.
reg
val
=
0
};
u8
checksum
=
0
;
u32
info_frame
;
int
channels
;
int
ca
;
channels
=
substream
->
runtime
->
channels
;
had_write_register
(
AUD_CNTL_ST
,
ctrl_state
.
ctrl_
val
);
had_write_register
(
intelhaddata
,
AUD_CNTL_ST
,
ctrl_state
.
reg
val
);
ca
=
snd_intelhad_channel_allocation
(
intelhaddata
,
channels
);
if
(
intelhaddata
->
dp_output
)
{
info_frame
=
DP_INFO_FRAME_WORD1
;
frame2
.
fr2_val
=
1
;
frame2
.
regval
=
(
substream
->
runtime
->
channels
-
1
)
|
(
ca
<<
24
)
;
}
else
{
info_frame
=
HDMI_INFO_FRAME_WORD1
;
frame2
.
fr2_regx
.
chnl_cnt
=
substream
->
runtime
->
channels
-
1
;
frame3
.
fr3_regx
.
chnl_alloc
=
snd_intelhad_channel_allocation
(
intelhaddata
,
channels
);
frame2
.
regx
.
chnl_cnt
=
substream
->
runtime
->
channels
-
1
;
frame3
.
regx
.
chnl_alloc
=
ca
;
/*
Calculte the byte wide checksum for all valid DIP words
*/
/*
Calculte the byte wide checksum for all valid DIP words
*/
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
info_frame
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
checksum
+=
(
info_frame
>>
(
i
*
8
))
&
0xff
;
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
frame2
.
fr2_val
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
checksum
+=
(
frame2
.
regval
>>
(
i
*
8
))
&
0xff
;
for
(
i
=
0
;
i
<
BYTES_PER_WORD
;
i
++
)
checksum
+=
(
frame3
.
fr3_val
>>
i
*
BITS_PER_BYTE
)
&
MASK_BYTE0
;
checksum
+=
(
frame3
.
regval
>>
(
i
*
8
))
&
0xff
;
frame2
.
fr2_
regx
.
chksum
=
-
(
checksum
);
frame2
.
regx
.
chksum
=
-
(
checksum
);
}
had_write_register
(
AUD_HDMIW_INFOFR_v2
,
info_frame
);
had_write_register
(
AUD_HDMIW_INFOFR_v2
,
frame2
.
fr2_
val
);
had_write_register
(
AUD_HDMIW_INFOFR_v2
,
frame3
.
fr3_
val
);
had_write_register
(
intelhaddata
,
AUD_HDMIW_INFOFR
,
info_frame
);
had_write_register
(
intelhaddata
,
AUD_HDMIW_INFOFR
,
frame2
.
reg
val
);
had_write_register
(
intelhaddata
,
AUD_HDMIW_INFOFR
,
frame3
.
reg
val
);
/* program remaining DIP words with zero */
for
(
i
=
0
;
i
<
HAD_MAX_DIP_WORDS
-
VALID_DIP_WORDS
;
i
++
)
had_write_register
(
AUD_HDMIW_INFOFR_v2
,
0x0
);
had_write_register
(
intelhaddata
,
AUD_HDMIW_INFOFR
,
0x0
);
ctrl_state
.
ctrl_
regx
.
dip_freq
=
1
;
ctrl_state
.
ctrl_
regx
.
dip_en_sta
=
1
;
had_write_register
(
AUD_CNTL_ST
,
ctrl_state
.
ctrl_
val
);
ctrl_state
.
regx
.
dip_freq
=
1
;
ctrl_state
.
regx
.
dip_en_sta
=
1
;
had_write_register
(
intelhaddata
,
AUD_CNTL_ST
,
ctrl_state
.
reg
val
);
}
/**
* snd_intelhad_prog_buffer - programs buffer
* address and length registers
*
* @substream:substream for which the prepare function is called
* @intelhaddata:substream private data
*
/*
* Programs buffer address and length registers
* This function programs ring buffer address and length into registers.
*/
int
snd_intelhad_prog_buffer
(
struct
snd_intelhad
*
intelhaddata
,
int
start
,
int
end
)
static
int
snd_intelhad_prog_buffer
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
,
int
start
,
int
end
)
{
u32
ring_buf_addr
,
ring_buf_size
,
period_bytes
;
u8
i
,
num_periods
;
struct
snd_pcm_substream
*
substream
;
substream
=
intelhaddata
->
stream_info
.
had_substream
;
if
(
!
substream
)
{
pr_err
(
"substream is NULL
\n
"
);
dump_stack
();
return
0
;
}
ring_buf_addr
=
substream
->
runtime
->
dma_addr
;
ring_buf_size
=
snd_pcm_lib_buffer_bytes
(
substream
);
...
...
@@ -814,36 +697,41 @@ int snd_intelhad_prog_buffer(struct snd_intelhad *intelhaddata,
intelhaddata
->
buf_info
[
i
].
buf_size
=
period_bytes
;
else
intelhaddata
->
buf_info
[
i
].
buf_size
=
ring_buf_size
-
(
period_bytes
*
i
);
(
i
*
period_bytes
);
had_write_register
(
AUD_BUF_A_ADDR
+
(
i
*
HAD_REG_WIDTH
),
had_write_register
(
intelhaddata
,
AUD_BUF_A_ADDR
+
(
i
*
HAD_REG_WIDTH
),
intelhaddata
->
buf_info
[
i
].
buf_addr
|
BIT
(
0
)
|
BIT
(
1
));
had_write_register
(
AUD_BUF_A_LENGTH
+
(
i
*
HAD_REG_WIDTH
),
had_write_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
i
*
HAD_REG_WIDTH
),
period_bytes
);
intelhaddata
->
buf_info
[
i
].
is_valid
=
true
;
}
pr_debug
(
"%s:buf[%d-%d] addr=%#x and size=%d
\n
"
,
__func__
,
start
,
end
,
intelhaddata
->
buf_info
[
start
].
buf_addr
,
intelhaddata
->
buf_info
[
start
].
buf_size
);
dev_dbg
(
intelhaddata
->
dev
,
"%s:buf[%d-%d] addr=%#x and size=%d
\n
"
,
__func__
,
start
,
end
,
intelhaddata
->
buf_info
[
start
].
buf_addr
,
intelhaddata
->
buf_info
[
start
].
buf_size
);
intelhaddata
->
valid_buf_cnt
=
num_periods
;
return
0
;
}
int
snd_intelhad_read_len
(
struct
snd_intelhad
*
intelhaddata
)
static
int
snd_intelhad_read_len
(
struct
snd_intelhad
*
intelhaddata
)
{
int
i
,
retval
=
0
;
u32
len
[
4
];
for
(
i
=
0
;
i
<
4
;
i
++
)
{
had_read_register
(
AUD_BUF_A_LENGTH
+
(
i
*
HAD_REG_WIDTH
),
&
len
[
i
]);
had_read_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
i
*
HAD_REG_WIDTH
),
&
len
[
i
]);
if
(
!
len
[
i
])
retval
++
;
}
if
(
retval
!=
1
)
{
for
(
i
=
0
;
i
<
4
;
i
++
)
pr_debug
(
"buf[%d] size=%d
\n
"
,
i
,
len
[
i
]);
dev_dbg
(
intelhaddata
->
dev
,
"buf[%d] size=%d
\n
"
,
i
,
len
[
i
]);
}
return
retval
;
...
...
@@ -853,7 +741,7 @@ static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate)
{
u32
maud_val
;
/* Select maud according to DP 1.2 spec*/
/* Select maud according to DP 1.2 spec
*/
if
(
link_rate
==
DP_2_7_GHZ
)
{
switch
(
aud_samp_freq
)
{
case
AUD_SAMPLE_RATE_32
:
...
...
@@ -928,34 +816,8 @@ static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate)
return
maud_val
;
}
/**
* snd_intelhad_prog_cts_v1 - Program HDMI audio CTS value
*
* @aud_samp_freq: sampling frequency of audio data
* @tmds: sampling frequency of the display data
* @n_param: N value, depends on aud_samp_freq
* @intelhaddata:substream private data
*
* Program CTS register based on the audio and display sampling frequency
*/
static
void
snd_intelhad_prog_cts_v1
(
u32
aud_samp_freq
,
u32
tmds
,
u32
link_rate
,
u32
n_param
,
struct
snd_intelhad
*
intelhaddata
)
{
u32
cts_val
;
u64
dividend
,
divisor
;
/* Calculate CTS according to HDMI 1.3a spec*/
dividend
=
(
u64
)
tmds
*
n_param
*
1000
;
divisor
=
128
*
aud_samp_freq
;
cts_val
=
div64_u64
(
dividend
,
divisor
);
pr_debug
(
"TMDS value=%d, N value=%d, CTS Value=%d
\n
"
,
tmds
,
n_param
,
cts_val
);
had_write_register
(
AUD_HDMI_CTS
,
(
BIT
(
20
)
|
cts_val
));
}
/**
* snd_intelhad_prog_cts_v2 - Program HDMI audio CTS value
/*
* Program HDMI audio CTS value
*
* @aud_samp_freq: sampling frequency of audio data
* @tmds: sampling frequency of the display data
...
...
@@ -964,9 +826,9 @@ static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds,
*
* Program CTS register based on the audio and display sampling frequency
*/
static
void
snd_intelhad_prog_cts
_v2
(
u32
aud_samp_freq
,
u32
tmds
,
u32
link_rate
,
u32
n_param
,
struct
snd_intelhad
*
intelhaddata
)
static
void
snd_intelhad_prog_cts
(
u32
aud_samp_freq
,
u32
tmds
,
u32
link_rate
,
u32
n_param
,
struct
snd_intelhad
*
intelhaddata
)
{
u32
cts_val
;
u64
dividend
,
divisor
;
...
...
@@ -980,79 +842,54 @@ static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds,
divisor
=
128
*
aud_samp_freq
;
cts_val
=
div64_u64
(
dividend
,
divisor
);
}
pr_debug
(
"TMDS value=%d, N value=%d, CTS Value=%d
\n
"
,
dev_dbg
(
intelhaddata
->
dev
,
"TMDS value=%d, N value=%d, CTS Value=%d
\n
"
,
tmds
,
n_param
,
cts_val
);
had_write_register
(
AUD_HDMI_CTS
,
(
BIT
(
24
)
|
cts_val
));
had_write_register
(
intelhaddata
,
AUD_HDMI_CTS
,
(
BIT
(
24
)
|
cts_val
));
}
static
int
had_calculate_n_value
(
u32
aud_samp_freq
)
{
s32
n_val
;
int
n_val
;
/* Select N according to HDMI 1.3a spec*/
switch
(
aud_samp_freq
)
{
case
AUD_SAMPLE_RATE_32
:
n_val
=
4096
;
break
;
break
;
case
AUD_SAMPLE_RATE_44_1
:
n_val
=
6272
;
break
;
break
;
case
AUD_SAMPLE_RATE_48
:
n_val
=
6144
;
break
;
break
;
case
AUD_SAMPLE_RATE_88_2
:
n_val
=
12544
;
break
;
break
;
case
AUD_SAMPLE_RATE_96
:
n_val
=
12288
;
break
;
break
;
case
AUD_SAMPLE_RATE_176_4
:
n_val
=
25088
;
break
;
break
;
case
HAD_MAX_RATE
:
n_val
=
24576
;
break
;
break
;
default:
n_val
=
-
EINVAL
;
break
;
break
;
}
return
n_val
;
}
/**
* snd_intelhad_prog_n_v1 - Program HDMI audio N value
*
* @aud_samp_freq: sampling frequency of audio data
* @n_param: N value, depends on aud_samp_freq
* @intelhaddata:substream private data
*
* This function is called in the prepare callback.
* It programs based on the audio and display sampling frequency
*/
static
int
snd_intelhad_prog_n_v1
(
u32
aud_samp_freq
,
u32
*
n_param
,
struct
snd_intelhad
*
intelhaddata
)
{
s32
n_val
;
n_val
=
had_calculate_n_value
(
aud_samp_freq
);
if
(
n_val
<
0
)
return
n_val
;
had_write_register
(
AUD_N_ENABLE
,
(
BIT
(
20
)
|
n_val
));
*
n_param
=
n_val
;
return
0
;
}
/**
* snd_intelhad_prog_n_v2 - Program HDMI audio N value
/*
* Program HDMI audio N value
*
* @aud_samp_freq: sampling frequency of audio data
* @n_param: N value, depends on aud_samp_freq
...
...
@@ -1061,10 +898,10 @@ static int snd_intelhad_prog_n_v1(u32 aud_samp_freq, u32 *n_param,
* This function is called in the prepare callback.
* It programs based on the audio and display sampling frequency
*/
static
int
snd_intelhad_prog_n
_v2
(
u32
aud_samp_freq
,
u32
*
n_param
,
struct
snd_intelhad
*
intelhaddata
)
static
int
snd_intelhad_prog_n
(
u32
aud_samp_freq
,
u32
*
n_param
,
struct
snd_intelhad
*
intelhaddata
)
{
s32
n_val
;
int
n_val
;
if
(
intelhaddata
->
dp_output
)
{
/*
...
...
@@ -1082,249 +919,143 @@ static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param,
if
(
n_val
<
0
)
return
n_val
;
had_write_register
(
AUD_N_ENABLE
,
(
BIT
(
24
)
|
n_val
));
had_write_register
(
intelhaddata
,
AUD_N_ENABLE
,
(
BIT
(
24
)
|
n_val
));
*
n_param
=
n_val
;
return
0
;
}
static
void
had_clear_underrun_intr_v1
(
struct
snd_intelhad
*
intelhaddata
)
{
u32
hdmi_status
,
i
=
0
;
#define MAX_CNT 0xFF
/* Handle Underrun interrupt within Audio Unit */
had_write_register
(
AUD_CONFIG
,
0
);
/* Reset buffer pointers */
had_write_register
(
AUD_HDMI_STATUS
,
1
);
had_write_register
(
AUD_HDMI_STATUS
,
0
);
/**
* The interrupt status 'sticky' bits might not be cleared by
* setting '1' to that bit once...
*/
do
{
/* clear bit30, 31 AUD_HDMI_STATUS */
had_read_register
(
AUD_HDMI_STATUS
,
&
hdmi_status
);
pr_debug
(
"HDMI status =0x%x
\n
"
,
hdmi_status
);
if
(
hdmi_status
&
AUD_CONFIG_MASK_UNDERRUN
)
{
i
++
;
hdmi_status
&=
(
AUD_CONFIG_MASK_SRDBG
|
AUD_CONFIG_MASK_FUNCRST
);
hdmi_status
|=
~
AUD_CONFIG_MASK_UNDERRUN
;
had_write_register
(
AUD_HDMI_STATUS
,
hdmi_status
);
}
else
break
;
}
while
(
i
<
MAX_CNT
);
if
(
i
>=
MAX_CNT
)
pr_err
(
"Unable to clear UNDERRUN bits
\n
"
);
}
static
void
had_clear_underrun_intr_v2
(
struct
snd_intelhad
*
intelhaddata
)
static
void
snd_intelhad_handle_underrun
(
struct
snd_intelhad
*
intelhaddata
)
{
u32
hdmi_status
,
i
=
0
;
u32
hdmi_status
=
0
,
i
=
0
;
/* Handle Underrun interrupt within Audio Unit */
had_write_register
(
AUD_CONFIG
,
0
);
had_write_register
(
intelhaddata
,
AUD_CONFIG
,
0
);
/* Reset buffer pointers */
had_write_register
(
AUD_HDMI_STATUS_v2
,
1
);
had_write_register
(
AUD_HDMI_STATUS_v2
,
0
);
/*
*
had_write_register
(
intelhaddata
,
AUD_HDMI_STATUS
,
1
);
had_write_register
(
intelhaddata
,
AUD_HDMI_STATUS
,
0
);
/*
* The interrupt status 'sticky' bits might not be cleared by
* setting '1' to that bit once...
*/
do
{
/* clear bit30, 31 AUD_HDMI_STATUS */
had_read_register
(
AUD_HDMI_STATUS_v2
,
&
hdmi_status
);
pr_debug
(
"HDMI status =0x%x
\n
"
,
hdmi_status
);
had_read_register
(
intelhaddata
,
AUD_HDMI_STATUS
,
&
hdmi_status
);
dev_dbg
(
intelhaddata
->
dev
,
"HDMI status =0x%x
\n
"
,
hdmi_status
);
if
(
hdmi_status
&
AUD_CONFIG_MASK_UNDERRUN
)
{
i
++
;
had_write_register
(
AUD_HDMI_STATUS_v2
,
hdmi_status
);
had_write_register
(
intelhaddata
,
AUD_HDMI_STATUS
,
hdmi_status
);
}
else
break
;
}
while
(
i
<
MAX_CNT
);
if
(
i
>=
MAX_CNT
)
pr_err
(
"Unable to clear UNDERRUN bits
\n
"
);
dev_err
(
intelhaddata
->
dev
,
"Unable to clear UNDERRUN bits
\n
"
);
}
/**
* snd_intelhad_open - stream initializations are done here
* @substream:substream for which the stream function is called
*
* This function is called whenever a PCM stream is opened
/*
* ALSA PCM open callback
*/
static
int
snd_intelhad_open
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_intelhad
*
intelhaddata
;
struct
snd_pcm_runtime
*
runtime
;
struct
had_stream_pvt
*
stream
;
struct
had_pvt_data
*
had_stream
;
int
retval
;
pr_debug
(
"snd_intelhad_open called
\n
"
);
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
had_stream
=
intelhaddata
->
private_data
;
runtime
=
substream
->
runtime
;
underrun_count
=
0
;
pm_runtime_get
(
intelhaddata
->
dev
);
pm_runtime_get
_sync
(
intelhaddata
->
dev
);
if
(
had_get_hwstate
(
intelhaddata
))
{
pr_err
(
"%s: HDMI cable plugged-out
\n
"
,
__func__
);
if
(
!
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"%s: HDMI cable plugged-out
\n
"
,
__func__
);
retval
=
-
ENODEV
;
goto
exit_put_handle
;
}
/* Check, if device already in use */
if
(
runtime
->
private_data
)
{
pr_err
(
"Device already in use
\n
"
);
retval
=
-
EBUSY
;
goto
exit_put_handle
;
goto
error
;
}
/* set the runtime hw parameter with local snd_pcm_hardware struct */
runtime
->
hw
=
snd_intel_hadstream
;
stream
=
kzalloc
(
sizeof
(
*
stream
),
GFP_KERNEL
);
if
(
!
stream
)
{
retval
=
-
ENOMEM
;
goto
exit_put_handle
;
}
stream
->
stream_status
=
STREAM_INIT
;
runtime
->
private_data
=
stream
;
retval
=
snd_pcm_hw_constraint_integer
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIODS
);
if
(
retval
<
0
)
goto
e
xit_er
r
;
goto
e
rro
r
;
/* Make sure, that the period size is always aligned
* 64byte boundary
*/
retval
=
snd_pcm_hw_constraint_step
(
substream
->
runtime
,
0
,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES
,
64
);
if
(
retval
<
0
)
{
pr_err
(
"%s:step_size=64 failed,err=%d
\n
"
,
__func__
,
retval
);
goto
exit_err
;
}
if
(
retval
<
0
)
goto
error
;
/* expose PCM substream */
spin_lock_irq
(
&
intelhaddata
->
had_spinlock
);
intelhaddata
->
stream_info
.
substream
=
substream
;
intelhaddata
->
stream_info
.
substream_refcount
++
;
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
/* these are cleared in prepare callback, but just to be sure */
intelhaddata
->
curr_buf
=
0
;
intelhaddata
->
underrun_count
=
0
;
intelhaddata
->
stream_info
.
buffer_rendered
=
0
;
return
retval
;
exit_err:
kfree
(
stream
);
exit_put_handle:
error:
pm_runtime_put
(
intelhaddata
->
dev
);
runtime
->
private_data
=
NULL
;
return
retval
;
}
/**
* had_period_elapsed - updates the hardware pointer status
* @had_substream:substream for which the stream function is called
*
*/
static
void
had_period_elapsed
(
void
*
had_substream
)
{
struct
snd_pcm_substream
*
substream
=
had_substream
;
struct
had_stream_pvt
*
stream
;
/* pr_debug("had_period_elapsed called\n"); */
if
(
!
substream
||
!
substream
->
runtime
)
return
;
stream
=
substream
->
runtime
->
private_data
;
if
(
!
stream
)
return
;
if
(
stream
->
stream_status
!=
STREAM_RUNNING
)
return
;
snd_pcm_period_elapsed
(
substream
);
}
/**
* snd_intelhad_init_stream - internal function to initialize stream info
* @substream:substream for which the stream function is called
*
*/
static
int
snd_intelhad_init_stream
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_intelhad
*
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
pr_debug
(
"snd_intelhad_init_stream called
\n
"
);
pr_debug
(
"setting buffer ptr param
\n
"
);
intelhaddata
->
stream_info
.
period_elapsed
=
had_period_elapsed
;
intelhaddata
->
stream_info
.
had_substream
=
substream
;
intelhaddata
->
stream_info
.
buffer_ptr
=
0
;
intelhaddata
->
stream_info
.
buffer_rendered
=
0
;
intelhaddata
->
stream_info
.
sfreq
=
substream
->
runtime
->
rate
;
return
0
;
}
/**
* snd_intelhad_close- to free parameteres when stream is stopped
*
* @substream: substream for which the function is called
*
* This function is called by ALSA framework when stream is stopped
/*
* ALSA PCM close callback
*/
static
int
snd_intelhad_close
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_intelhad
*
intelhaddata
;
struct
snd_pcm_runtime
*
runtime
;
pr_debug
(
"snd_intelhad_close called
\n
"
);
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
runtime
=
substream
->
runtime
;
if
(
!
runtime
->
private_data
)
{
pr_debug
(
"close() might have called after failed open"
);
return
0
;
/* unreference and sync with the pending PCM accesses */
spin_lock_irq
(
&
intelhaddata
->
had_spinlock
);
intelhaddata
->
stream_info
.
substream
=
NULL
;
intelhaddata
->
stream_info
.
substream_refcount
--
;
while
(
intelhaddata
->
stream_info
.
substream_refcount
>
0
)
{
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
cpu_relax
();
spin_lock_irq
(
&
intelhaddata
->
had_spinlock
);
}
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
intelhaddata
->
stream_info
.
buffer_rendered
=
0
;
intelhaddata
->
stream_info
.
buffer_ptr
=
0
;
intelhaddata
->
stream_info
.
str_id
=
0
;
intelhaddata
->
stream_info
.
had_substream
=
NULL
;
/* Check if following drv_status modification is required - VA */
if
(
intelhaddata
->
drv_status
!=
HAD_DRV_DISCONNECTED
)
{
intelhaddata
->
drv_status
=
HAD_DRV_CONNECTED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED
\n
"
,
__func__
,
__LINE__
);
}
kfree
(
runtime
->
private_data
);
runtime
->
private_data
=
NULL
;
pm_runtime_put
(
intelhaddata
->
dev
);
return
0
;
}
/**
* snd_intelhad_hw_params- to setup the hardware parameters
* like allocating the buffers
*
* @substream: substream for which the function is called
* @hw_params: hardware parameters
*
* This function is called by ALSA framework when hardware params are set
/*
* ALSA PCM hw_params callback
*/
static
int
snd_intelhad_hw_params
(
struct
snd_pcm_substream
*
substream
,
struct
snd_pcm_hw_params
*
hw_params
)
{
struct
snd_intelhad
*
intelhaddata
;
unsigned
long
addr
;
int
pages
,
buf_size
,
retval
;
pr_debug
(
"snd_intelhad_hw_params called
\n
"
);
if
(
!
hw_params
)
return
-
EINVAL
;
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
buf_size
=
params_buffer_bytes
(
hw_params
);
retval
=
snd_pcm_lib_malloc_pages
(
substream
,
buf_size
);
if
(
retval
<
0
)
return
retval
;
pr_debug
(
"%s:allocated memory = %d
\n
"
,
__func__
,
buf_size
);
dev_dbg
(
intelhaddata
->
dev
,
"%s:allocated memory = %d
\n
"
,
__func__
,
buf_size
);
/* mark the pages as uncached region */
addr
=
(
unsigned
long
)
substream
->
runtime
->
dma_area
;
pages
=
(
substream
->
runtime
->
dma_bytes
+
PAGE_SIZE
-
1
)
/
PAGE_SIZE
;
retval
=
set_memory_uc
(
addr
,
pages
);
if
(
retval
)
{
pr_err
(
"set_memory_uc failed.Error:%d
\n
"
,
retval
);
dev_err
(
intelhaddata
->
dev
,
"set_memory_uc failed.Error:%d
\n
"
,
retval
);
return
retval
;
}
memset
(
substream
->
runtime
->
dma_area
,
0
,
buf_size
);
...
...
@@ -1332,22 +1063,14 @@ static int snd_intelhad_hw_params(struct snd_pcm_substream *substream,
return
retval
;
}
/**
* snd_intelhad_hw_free- to release the resources allocated during
* hardware params setup
*
* @substream: substream for which the function is called
*
* This function is called by ALSA framework before close callback.
*
/*
* ALSA PCM hw_free callback
*/
static
int
snd_intelhad_hw_free
(
struct
snd_pcm_substream
*
substream
)
{
unsigned
long
addr
;
u32
pages
;
pr_debug
(
"snd_intelhad_hw_free called
\n
"
);
/* mark back the pages as cached/writeback region before the free */
if
(
substream
->
runtime
->
dma_area
!=
NULL
)
{
addr
=
(
unsigned
long
)
substream
->
runtime
->
dma_area
;
...
...
@@ -1359,78 +1082,52 @@ static int snd_intelhad_hw_free(struct snd_pcm_substream *substream)
return
0
;
}
/**
* snd_intelhad_pcm_trigger - stream activities are handled here
* @substream:substream for which the stream function is called
* @cmd:the stream commamd thats requested from upper layer
* This function is called whenever an a stream activity is invoked
/*
* ALSA PCM trigger callback
*/
static
int
snd_intelhad_pcm_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
int
caps
,
retval
=
0
;
unsigned
long
flag_irq
;
int
retval
=
0
;
struct
snd_intelhad
*
intelhaddata
;
struct
had_stream_pvt
*
stream
;
struct
had_pvt_data
*
had_stream
;
pr_debug
(
"snd_intelhad_pcm_trigger called
\n
"
);
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
stream
=
substream
->
runtime
->
private_data
;
had_stream
=
intelhaddata
->
private_data
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
pr_debug
(
"Trigger Start
\n
"
);
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
case
SNDRV_PCM_TRIGGER_RESUME
:
/* Disable local INTRs till register prgmng is done */
if
(
had_get_hwstate
(
intelhaddata
))
{
pr_err
(
"_START: HDMI cable plugged-out
\n
"
);
if
(
!
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"_START: HDMI cable plugged-out
\n
"
);
retval
=
-
ENODEV
;
break
;
}
stream
->
stream_status
=
STREAM_RUNNING
;
had_stream
->
stream_type
=
HAD_RUNNING_STREAM
;
intelhaddata
->
stream_info
.
running
=
true
;
/* Enable Audio */
/*
* ToDo: Need to enable UNDERRUN interrupts as well
* caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
*/
caps
=
HDMI_AUDIO_BUFFER_DONE
;
retval
=
had_set_caps
(
HAD_SET_ENABLE_AUDIO_INT
,
&
caps
);
retval
=
had_set_caps
(
HAD_SET_ENABLE_AUDIO
,
NULL
);
intelhaddata
->
ops
->
enable_audio
(
substream
,
1
);
pr_debug
(
"Processed _Start
\n
"
);
snd_intelhad_enable_audio_int
(
intelhaddata
,
true
);
snd_intelhad_enable_audio
(
substream
,
intelhaddata
,
true
);
break
;
case
SNDRV_PCM_TRIGGER_STOP
:
pr_debug
(
"Trigger Stop
\n
"
);
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irq
);
intelhaddata
->
stream_info
.
str_id
=
0
;
intelhaddata
->
curr_buf
=
0
;
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
case
SNDRV_PCM_TRIGGER_SUSPEND
:
spin_lock
(
&
intelhaddata
->
had_spinlock
);
/* Stop reporting BUFFER_DONE/UNDERRUN to above layers*/
/* Stop reporting BUFFER_DONE/UNDERRUN to above layers
*/
had_stream
->
stream_type
=
HAD_INIT
;
spin_unlock
_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irq
);
intelhaddata
->
stream_info
.
running
=
false
;
spin_unlock
(
&
intelhaddata
->
had_spinlock
);
/* Disable Audio */
/*
* ToDo: Need to disable UNDERRUN interrupts as well
* caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
*/
caps
=
HDMI_AUDIO_BUFFER_DONE
;
had_set_caps
(
HAD_SET_DISABLE_AUDIO_INT
,
&
caps
);
intelhaddata
->
ops
->
enable_audio
(
substream
,
0
);
snd_intelhad_enable_audio_int
(
intelhaddata
,
false
);
snd_intelhad_enable_audio
(
substream
,
intelhaddata
,
false
);
/* Reset buffer pointers */
intelhaddata
->
ops
->
reset_audio
(
1
);
intelhaddata
->
ops
->
reset_audio
(
0
);
stream
->
stream_status
=
STREAM_DROPPED
;
had_set_caps
(
HAD_SET_DISABLE_AUDIO
,
NULL
);
snd_intelhad_reset_audio
(
intelhaddata
,
1
);
snd_intelhad_reset_audio
(
intelhaddata
,
0
);
snd_intelhad_enable_audio_int
(
intelhaddata
,
false
);
break
;
default:
...
...
@@ -1439,12 +1136,8 @@ static int snd_intelhad_pcm_trigger(struct snd_pcm_substream *substream,
return
retval
;
}
/**
* snd_intelhad_pcm_prepare- internal preparation before starting a stream
*
* @substream: substream for which the function is called
*
* This function is called when a stream is started for internal preparation.
/*
* ALSA PCM prepare callback
*/
static
int
snd_intelhad_pcm_prepare
(
struct
snd_pcm_substream
*
substream
)
{
...
...
@@ -1453,71 +1146,53 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
u32
link_rate
=
0
;
struct
snd_intelhad
*
intelhaddata
;
struct
snd_pcm_runtime
*
runtime
;
struct
had_pvt_data
*
had_stream
;
pr_debug
(
"snd_intelhad_pcm_prepare called
\n
"
);
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
runtime
=
substream
->
runtime
;
had_stream
=
intelhaddata
->
private_data
;
if
(
had_get_hwstate
(
intelhaddata
))
{
pr_err
(
"%s: HDMI cable plugged-out
\n
"
,
__func__
);
if
(
!
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"%s: HDMI cable plugged-out
\n
"
,
__func__
);
retval
=
-
ENODEV
;
goto
prep_end
;
}
pr_debug
(
"period_size=%d
\n
"
,
dev_dbg
(
intelhaddata
->
dev
,
"period_size=%d
\n
"
,
(
int
)
frames_to_bytes
(
runtime
,
runtime
->
period_size
));
pr_debug
(
"periods=%d
\n
"
,
runtime
->
periods
);
pr_debug
(
"buffer_size=%d
\n
"
,
(
int
)
snd_pcm_lib_buffer_bytes
(
substream
));
pr_debug
(
"rate=%d
\n
"
,
runtime
->
rate
);
pr_debug
(
"channels=%d
\n
"
,
runtime
->
channels
);
if
(
intelhaddata
->
stream_info
.
str_id
)
{
pr_debug
(
"_prepare is called for existing str_id#%d
\n
"
,
intelhaddata
->
stream_info
.
str_id
);
retval
=
snd_intelhad_pcm_trigger
(
substream
,
SNDRV_PCM_TRIGGER_STOP
);
return
retval
;
}
retval
=
snd_intelhad_init_stream
(
substream
);
if
(
retval
)
goto
prep_end
;
dev_dbg
(
intelhaddata
->
dev
,
"periods=%d
\n
"
,
runtime
->
periods
);
dev_dbg
(
intelhaddata
->
dev
,
"buffer_size=%d
\n
"
,
(
int
)
snd_pcm_lib_buffer_bytes
(
substream
));
dev_dbg
(
intelhaddata
->
dev
,
"rate=%d
\n
"
,
runtime
->
rate
);
dev_dbg
(
intelhaddata
->
dev
,
"channels=%d
\n
"
,
runtime
->
channels
);
intelhaddata
->
curr_buf
=
0
;
intelhaddata
->
underrun_count
=
0
;
intelhaddata
->
stream_info
.
buffer_rendered
=
0
;
/* Get N value in KHz */
retval
=
had_get_caps
(
HAD_GET_DISPLAY_RATE
,
&
disp_samp_freq
);
if
(
retval
)
{
pr_err
(
"querying display sampling freq failed %#x
\n
"
,
retval
);
goto
prep_end
;
}
disp_samp_freq
=
intelhaddata
->
tmds_clock_speed
;
had_get_caps
(
HAD_GET_ELD
,
&
intelhaddata
->
eeld
);
had_get_caps
(
HAD_GET_DP_OUTPUT
,
&
intelhaddata
->
dp_output
);
retval
=
intelhaddata
->
ops
->
prog_n
(
substream
->
runtime
->
rate
,
&
n_param
,
intelhaddata
);
retval
=
snd_intelhad_prog_n
(
substream
->
runtime
->
rate
,
&
n_param
,
intelhaddata
);
if
(
retval
)
{
pr_err
(
"programming N value failed %#x
\n
"
,
retval
);
dev_err
(
intelhaddata
->
dev
,
"programming N value failed %#x
\n
"
,
retval
);
goto
prep_end
;
}
if
(
intelhaddata
->
dp_output
)
had_get_caps
(
HAD_GET_LINK_RATE
,
&
link_rate
);
link_rate
=
intelhaddata
->
link_rate
;
intelhaddata
->
ops
->
prog_cts
(
substream
->
runtime
->
rate
,
disp_samp_freq
,
link_rate
,
n_param
,
intelhaddata
);
snd_intelhad_
prog_cts
(
substream
->
runtime
->
rate
,
disp_samp_freq
,
link_rate
,
n_param
,
intelhaddata
);
intelhaddata
->
ops
->
prog_dip
(
substream
,
intelhaddata
);
snd_intelhad_
prog_dip
(
substream
,
intelhaddata
);
retval
=
intelhaddata
->
ops
->
audio_ctrl
(
substream
,
intelhaddata
);
retval
=
snd_intelhad_
audio_ctrl
(
substream
,
intelhaddata
);
/* Prog buffer address */
retval
=
snd_intelhad_prog_buffer
(
intelhaddata
,
retval
=
snd_intelhad_prog_buffer
(
substream
,
intelhaddata
,
HAD_BUF_TYPE_A
,
HAD_BUF_TYPE_D
);
/*
...
...
@@ -1525,58 +1200,51 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
* FL, FR, C, LFE, RL, RR
*/
had_write_register
(
AUD_BUF_CH_SWAP
,
SWAP_LFE_CENTER
);
had_write_register
(
intelhaddata
,
AUD_BUF_CH_SWAP
,
SWAP_LFE_CENTER
);
prep_end:
return
retval
;
}
/**
* snd_intelhad_pcm_pointer- to send the current buffer pointerprocessed by hw
*
* @substream: substream for which the function is called
*
* This function is called by ALSA framework to get the current hw buffer ptr
* when a period is elapsed
/*
* ALSA PCM pointer callback
*/
static
snd_pcm_uframes_t
snd_intelhad_pcm_pointer
(
struct
snd_pcm_substream
*
substream
)
static
snd_pcm_uframes_t
snd_intelhad_pcm_pointer
(
struct
snd_pcm_substream
*
substream
)
{
struct
snd_intelhad
*
intelhaddata
;
u32
bytes_rendered
=
0
;
u32
t
;
int
buf_id
;
/* pr_debug("snd_intelhad_pcm_pointer called\n"); */
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
if
(
intelhaddata
->
flag_underrun
)
{
intelhaddata
->
flag_underrun
=
0
;
if
(
!
intelhaddata
->
connected
)
return
SNDRV_PCM_POS_XRUN
;
}
/* Use a hw register to calculate sub-period position reports.
* This makes PulseAudio happier.
*/
buf_id
=
intelhaddata
->
curr_buf
%
4
;
had_read_register
(
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
&
t
);
had_read_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
&
t
);
if
((
t
==
0
)
||
(
t
==
((
u32
)
-
1L
)))
{
underrun_count
++
;
pr_debug
(
"discovered buffer done for buf %d, count = %d
\n
"
,
buf_id
,
underrun_count
);
if
(
underrun_count
>
(
HAD_MIN_PERIODS
/
2
))
{
pr_debug
(
"assume audio_codec_reset, underrun = %d - do xrun
\n
"
,
underrun_count
);
underrun_count
=
0
;
intelhaddata
->
underrun_count
++
;
dev_dbg
(
intelhaddata
->
dev
,
"discovered buffer done for buf %d, count = %d
\n
"
,
buf_id
,
intelhaddata
->
underrun_count
);
if
(
intelhaddata
->
underrun_count
>
(
HAD_MIN_PERIODS
/
2
))
{
dev_dbg
(
intelhaddata
->
dev
,
"assume audio_codec_reset, underrun = %d - do xrun
\n
"
,
intelhaddata
->
underrun_count
);
return
SNDRV_PCM_POS_XRUN
;
}
}
else
{
/* Reset Counter */
underrun_count
=
0
;
intelhaddata
->
underrun_count
=
0
;
}
t
=
intelhaddata
->
buf_info
[
buf_id
].
buf_size
-
t
;
...
...
@@ -1586,124 +1254,327 @@ static snd_pcm_uframes_t snd_intelhad_pcm_pointer(
intelhaddata
->
stream_info
.
ring_buf_size
,
&
(
bytes_rendered
));
intelhaddata
->
stream_info
.
buffer_ptr
=
bytes_to_frames
(
substream
->
runtime
,
bytes_rendered
+
t
);
return
intelhaddata
->
stream_info
.
buffer_ptr
;
return
bytes_to_frames
(
substream
->
runtime
,
bytes_rendered
+
t
);
}
/**
* snd_intelhad_pcm_mmap- mmaps a kernel buffer to user space for copying data
*
* @substream: substream for which the function is called
* @vma: struct instance of memory VMM memory area
*
* This function is called by OS when a user space component
* tries to get mmap memory from driver
/*
* ALSA PCM mmap callback
*/
static
int
snd_intelhad_pcm_mmap
(
struct
snd_pcm_substream
*
substream
,
struct
vm_area_struct
*
vma
)
{
pr_debug
(
"snd_intelhad_pcm_mmap called
\n
"
);
pr_debug
(
"entry with prot:%s
\n
"
,
__func__
);
vma
->
vm_page_prot
=
pgprot_noncached
(
vma
->
vm_page_prot
);
return
remap_pfn_range
(
vma
,
vma
->
vm_start
,
substream
->
dma_buffer
.
addr
>>
PAGE_SHIFT
,
vma
->
vm_end
-
vma
->
vm_start
,
vma
->
vm_page_prot
);
}
int
hdmi_audio_mode_change
(
struct
snd_pcm_substream
*
substream
)
/*
* ALSA PCM ops
*/
static
const
struct
snd_pcm_ops
snd_intelhad_playback_ops
=
{
.
open
=
snd_intelhad_open
,
.
close
=
snd_intelhad_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
snd_intelhad_hw_params
,
.
hw_free
=
snd_intelhad_hw_free
,
.
prepare
=
snd_intelhad_pcm_prepare
,
.
trigger
=
snd_intelhad_pcm_trigger
,
.
pointer
=
snd_intelhad_pcm_pointer
,
.
mmap
=
snd_intelhad_pcm_mmap
,
};
/* process mode change of the running stream; called in mutex */
static
int
hdmi_audio_mode_change
(
struct
snd_intelhad
*
intelhaddata
)
{
struct
snd_pcm_substream
*
substream
;
int
retval
=
0
;
u32
disp_samp_freq
,
n_param
;
u32
link_rate
=
0
;
struct
snd_intelhad
*
intelhaddata
;
intelhaddata
=
snd_pcm_substream_chip
(
substream
);
substream
=
had_substream_get
(
intelhaddata
);
if
(
!
substream
)
return
0
;
/* Disable Audio */
intelhaddata
->
ops
->
enable_audio
(
substream
,
0
);
snd_intelhad_enable_audio
(
substream
,
intelhaddata
,
false
);
/* Update CTS value */
retval
=
had_get_caps
(
HAD_GET_DISPLAY_RATE
,
&
disp_samp_freq
);
if
(
retval
)
{
pr_err
(
"querying display sampling freq failed %#x
\n
"
,
retval
);
goto
out
;
}
disp_samp_freq
=
intelhaddata
->
tmds_clock_speed
;
retval
=
intelhaddata
->
ops
->
prog_n
(
substream
->
runtime
->
rate
,
&
n_param
,
intelhaddata
);
retval
=
snd_intelhad_
prog_n
(
substream
->
runtime
->
rate
,
&
n_param
,
intelhaddata
);
if
(
retval
)
{
pr_err
(
"programming N value failed %#x
\n
"
,
retval
);
dev_err
(
intelhaddata
->
dev
,
"programming N value failed %#x
\n
"
,
retval
);
goto
out
;
}
if
(
intelhaddata
->
dp_output
)
had_get_caps
(
HAD_GET_LINK_RATE
,
&
link_rate
)
;
link_rate
=
intelhaddata
->
link_rate
;
intelhaddata
->
ops
->
prog_cts
(
substream
->
runtime
->
rate
,
disp_samp_freq
,
link_rate
,
n_param
,
intelhaddata
);
snd_intelhad_
prog_cts
(
substream
->
runtime
->
rate
,
disp_samp_freq
,
link_rate
,
n_param
,
intelhaddata
);
/* Enable Audio */
intelhaddata
->
ops
->
enable_audio
(
substream
,
1
);
snd_intelhad_enable_audio
(
substream
,
intelhaddata
,
true
);
out:
had_substream_put
(
intelhaddata
);
return
retval
;
}
/*PCM operations structure and the calls back for the same */
struct
snd_pcm_ops
snd_intelhad_playback_ops
=
{
.
open
=
snd_intelhad_open
,
.
close
=
snd_intelhad_close
,
.
ioctl
=
snd_pcm_lib_ioctl
,
.
hw_params
=
snd_intelhad_hw_params
,
.
hw_free
=
snd_intelhad_hw_free
,
.
prepare
=
snd_intelhad_pcm_prepare
,
.
trigger
=
snd_intelhad_pcm_trigger
,
.
pointer
=
snd_intelhad_pcm_pointer
,
.
mmap
=
snd_intelhad_pcm_mmap
,
};
static
inline
int
had_chk_intrmiss
(
struct
snd_intelhad
*
intelhaddata
,
enum
intel_had_aud_buf_type
buf_id
)
{
int
i
,
intr_count
=
0
;
enum
intel_had_aud_buf_type
buff_done
;
u32
buf_size
,
buf_addr
;
buff_done
=
buf_id
;
intr_count
=
snd_intelhad_read_len
(
intelhaddata
);
if
(
intr_count
>
1
)
{
/* In case of active playback */
dev_err
(
intelhaddata
->
dev
,
"Driver detected %d missed buffer done interrupt(s)
\n
"
,
(
intr_count
-
1
));
if
(
intr_count
>
3
)
return
intr_count
;
buf_id
+=
(
intr_count
-
1
);
/* Reprogram registers*/
for
(
i
=
buff_done
;
i
<
buf_id
;
i
++
)
{
int
j
=
i
%
4
;
buf_size
=
intelhaddata
->
buf_info
[
j
].
buf_size
;
buf_addr
=
intelhaddata
->
buf_info
[
j
].
buf_addr
;
had_write_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
j
*
HAD_REG_WIDTH
),
buf_size
);
had_write_register
(
intelhaddata
,
AUD_BUF_A_ADDR
+
(
j
*
HAD_REG_WIDTH
),
(
buf_addr
|
BIT
(
0
)
|
BIT
(
1
)));
}
buf_id
=
buf_id
%
4
;
intelhaddata
->
buff_done
=
buf_id
;
}
/**
* snd_intelhad_create - to crete alsa card instance
*
* @intelhaddata: pointer to internal context
* @card: pointer to card
*
* This function is called when the hdmi cable is plugged in
*/
static
int
snd_intelhad_create
(
struct
snd_intelhad
*
intelhaddata
,
struct
snd_card
*
card
)
return
intr_count
;
}
/* called from irq handler */
static
int
had_process_buffer_done
(
struct
snd_intelhad
*
intelhaddata
)
{
int
retval
;
static
struct
snd_device_ops
ops
=
{
};
u32
len
=
1
;
enum
intel_had_aud_buf_type
buf_id
;
enum
intel_had_aud_buf_type
buff_done
;
struct
pcm_stream_info
*
stream
;
struct
snd_pcm_substream
*
substream
;
u32
buf_size
;
int
intr_count
;
unsigned
long
flags
;
stream
=
&
intelhaddata
->
stream_info
;
intr_count
=
1
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flags
);
if
(
!
intelhaddata
->
connected
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
dev_dbg
(
intelhaddata
->
dev
,
"%s:Device already disconnected
\n
"
,
__func__
);
return
0
;
}
buf_id
=
intelhaddata
->
curr_buf
;
intelhaddata
->
buff_done
=
buf_id
;
buff_done
=
intelhaddata
->
buff_done
;
buf_size
=
intelhaddata
->
buf_info
[
buf_id
].
buf_size
;
/* Every debug statement has an implication
* of ~5msec. Thus, avoid having >3 debug statements
* for each buffer_done handling.
*/
pr_debug
(
"snd_intelhad_create called
\n
"
);
/* Check for any intr_miss in case of active playback */
if
(
stream
->
running
)
{
intr_count
=
had_chk_intrmiss
(
intelhaddata
,
buf_id
);
if
(
!
intr_count
||
(
intr_count
>
3
))
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
dev_err
(
intelhaddata
->
dev
,
"HAD SW state in non-recoverable mode
\n
"
);
return
0
;
}
buf_id
+=
(
intr_count
-
1
);
buf_id
=
buf_id
%
4
;
}
if
(
!
intelhaddata
)
return
-
EINVAL
;
intelhaddata
->
buf_info
[
buf_id
].
is_valid
=
true
;
if
(
intelhaddata
->
valid_buf_cnt
-
1
==
buf_id
)
{
if
(
stream
->
running
)
intelhaddata
->
curr_buf
=
HAD_BUF_TYPE_A
;
}
else
intelhaddata
->
curr_buf
=
buf_id
+
1
;
/* ALSA api to register the device */
retval
=
snd_device_new
(
card
,
SNDRV_DEV_LOWLEVEL
,
intelhaddata
,
&
ops
);
return
retval
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
if
(
!
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"HDMI cable plugged-out
\n
"
);
return
0
;
}
/* Reprogram the registers with addr and length */
had_write_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
buf_size
);
had_write_register
(
intelhaddata
,
AUD_BUF_A_ADDR
+
(
buf_id
*
HAD_REG_WIDTH
),
intelhaddata
->
buf_info
[
buf_id
].
buf_addr
|
BIT
(
0
)
|
BIT
(
1
));
had_read_register
(
intelhaddata
,
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
&
len
);
dev_dbg
(
intelhaddata
->
dev
,
"%s:Enabled buf[%d]
\n
"
,
__func__
,
buf_id
);
/* In case of actual data,
* report buffer_done to above ALSA layer
*/
substream
=
had_substream_get
(
intelhaddata
);
if
(
substream
)
{
buf_size
=
intelhaddata
->
buf_info
[
buf_id
].
buf_size
;
intelhaddata
->
stream_info
.
buffer_rendered
+=
(
intr_count
*
buf_size
);
snd_pcm_period_elapsed
(
substream
);
had_substream_put
(
intelhaddata
);
}
return
0
;
}
/**
* snd_intelhad_pcm_free - to free the memory allocated
*
* @pcm: pointer to pcm instance
* This function is called when the device is removed
*/
static
void
snd_intelhad_pcm_free
(
struct
snd_pcm
*
pcm
)
/* called from irq handler */
static
int
had_process_buffer_underrun
(
struct
snd_intelhad
*
intelhaddata
)
{
enum
intel_had_aud_buf_type
buf_id
;
struct
pcm_stream_info
*
stream
;
struct
snd_pcm_substream
*
substream
;
unsigned
long
flags
;
int
connected
;
stream
=
&
intelhaddata
->
stream_info
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flags
);
buf_id
=
intelhaddata
->
curr_buf
;
intelhaddata
->
buff_done
=
buf_id
;
connected
=
intelhaddata
->
connected
;
if
(
stream
->
running
)
intelhaddata
->
curr_buf
=
HAD_BUF_TYPE_A
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flags
);
dev_dbg
(
intelhaddata
->
dev
,
"Enter:%s buf_id=%d, stream_running=%d
\n
"
,
__func__
,
buf_id
,
stream
->
running
);
snd_intelhad_handle_underrun
(
intelhaddata
);
if
(
!
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"%s:Device already disconnected
\n
"
,
__func__
);
return
0
;
}
/* Report UNDERRUN error to above layers */
substream
=
had_substream_get
(
intelhaddata
);
if
(
substream
)
{
snd_pcm_stop_xrun
(
substream
);
had_substream_put
(
intelhaddata
);
}
return
0
;
}
/* process hot plug, called from wq with mutex locked */
static
void
had_process_hot_plug
(
struct
snd_intelhad
*
intelhaddata
)
{
pr_debug
(
"Freeing PCM preallocated pages
\n
"
);
snd_pcm_lib_preallocate_free_for_all
(
pcm
);
enum
intel_had_aud_buf_type
buf_id
;
struct
snd_pcm_substream
*
substream
;
spin_lock_irq
(
&
intelhaddata
->
had_spinlock
);
if
(
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"Device already connected
\n
"
);
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
return
;
}
buf_id
=
intelhaddata
->
curr_buf
;
intelhaddata
->
buff_done
=
buf_id
;
intelhaddata
->
connected
=
true
;
dev_dbg
(
intelhaddata
->
dev
,
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED
\n
"
,
__func__
,
__LINE__
);
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
dev_dbg
(
intelhaddata
->
dev
,
"Processing HOT_PLUG, buf_id = %d
\n
"
,
buf_id
);
/* Safety check */
substream
=
had_substream_get
(
intelhaddata
);
if
(
substream
)
{
dev_dbg
(
intelhaddata
->
dev
,
"Force to stop the active stream by disconnection
\n
"
);
/* Set runtime->state to hw_params done */
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_SETUP
);
had_substream_put
(
intelhaddata
);
}
had_build_channel_allocation_map
(
intelhaddata
);
}
/* process hot unplug, called from wq with mutex locked */
static
void
had_process_hot_unplug
(
struct
snd_intelhad
*
intelhaddata
)
{
enum
intel_had_aud_buf_type
buf_id
;
struct
snd_pcm_substream
*
substream
;
buf_id
=
intelhaddata
->
curr_buf
;
substream
=
had_substream_get
(
intelhaddata
);
spin_lock_irq
(
&
intelhaddata
->
had_spinlock
);
if
(
!
intelhaddata
->
connected
)
{
dev_dbg
(
intelhaddata
->
dev
,
"Device already disconnected
\n
"
);
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
goto
out
;
}
/* Disable Audio */
snd_intelhad_enable_audio_int
(
intelhaddata
,
false
);
snd_intelhad_enable_audio
(
substream
,
intelhaddata
,
false
);
intelhaddata
->
connected
=
false
;
dev_dbg
(
intelhaddata
->
dev
,
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED
\n
"
,
__func__
,
__LINE__
);
spin_unlock_irq
(
&
intelhaddata
->
had_spinlock
);
/* Report to above ALSA layer */
if
(
substream
)
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_SETUP
);
out:
if
(
substream
)
had_substream_put
(
intelhaddata
);
kfree
(
intelhaddata
->
chmap
->
chmap
);
intelhaddata
->
chmap
->
chmap
=
NULL
;
}
/*
* ALSA iec958 and ELD controls
*/
static
int
had_iec958_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
...
...
@@ -1717,14 +1588,17 @@ static int had_iec958_get(struct snd_kcontrol *kcontrol,
{
struct
snd_intelhad
*
intelhaddata
=
snd_kcontrol_chip
(
kcontrol
);
mutex_lock
(
&
intelhaddata
->
mutex
);
ucontrol
->
value
.
iec958
.
status
[
0
]
=
(
intelhaddata
->
aes_bits
>>
0
)
&
0xff
;
ucontrol
->
value
.
iec958
.
status
[
1
]
=
(
intelhaddata
->
aes_bits
>>
8
)
&
0xff
;
ucontrol
->
value
.
iec958
.
status
[
2
]
=
(
intelhaddata
->
aes_bits
>>
16
)
&
0xff
;
ucontrol
->
value
.
iec958
.
status
[
3
]
=
(
intelhaddata
->
aes_bits
>>
24
)
&
0xff
;
mutex_unlock
(
&
intelhaddata
->
mutex
);
return
0
;
}
static
int
had_iec958_mask_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
...
...
@@ -1734,254 +1608,381 @@ static int had_iec958_mask_get(struct snd_kcontrol *kcontrol,
ucontrol
->
value
.
iec958
.
status
[
3
]
=
0xff
;
return
0
;
}
static
int
had_iec958_put
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
unsigned
int
val
;
struct
snd_intelhad
*
intelhaddata
=
snd_kcontrol_chip
(
kcontrol
);
int
changed
=
0
;
pr_debug
(
"entered had_iec958_put
\n
"
);
val
=
(
ucontrol
->
value
.
iec958
.
status
[
0
]
<<
0
)
|
(
ucontrol
->
value
.
iec958
.
status
[
1
]
<<
8
)
|
(
ucontrol
->
value
.
iec958
.
status
[
2
]
<<
16
)
|
(
ucontrol
->
value
.
iec958
.
status
[
3
]
<<
24
);
mutex_lock
(
&
intelhaddata
->
mutex
);
if
(
intelhaddata
->
aes_bits
!=
val
)
{
intelhaddata
->
aes_bits
=
val
;
return
1
;
changed
=
1
;
}
return
1
;
mutex_unlock
(
&
intelhaddata
->
mutex
);
return
changed
;
}
static
struct
snd_kcontrol_new
had_control_iec958_mask
=
{
.
access
=
SNDRV_CTL_ELEM_ACCESS_READ
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_PCM
,
.
name
=
SNDRV_CTL_NAME_IEC958
(
""
,
PLAYBACK
,
MASK
),
.
info
=
had_iec958_info
,
/* shared */
.
get
=
had_iec958_mask_get
,
}
;
static
int
had_ctl_eld_info
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_info
*
uinfo
)
{
uinfo
->
type
=
SNDRV_CTL_ELEM_TYPE_BYTES
;
uinfo
->
count
=
HDMI_MAX_ELD_BYTES
;
return
0
;
}
static
struct
snd_kcontrol_new
had_control_iec958
=
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_PCM
,
.
name
=
SNDRV_CTL_NAME_IEC958
(
""
,
PLAYBACK
,
DEFAULT
),
.
info
=
had_iec958_info
,
.
get
=
had_iec958_get
,
.
put
=
had_iec958_put
};
static
int
had_ctl_eld_get
(
struct
snd_kcontrol
*
kcontrol
,
struct
snd_ctl_elem_value
*
ucontrol
)
{
struct
snd_intelhad
*
intelhaddata
=
snd_kcontrol_chip
(
kcontrol
);
static
struct
snd_intel_had_interface
had_interface
=
{
.
name
=
"hdmi-audio"
,
.
query
=
hdmi_audio_query
,
.
suspend
=
hdmi_audio_suspend
,
.
resume
=
hdmi_audio_resume
,
}
;
mutex_lock
(
&
intelhaddata
->
mutex
);
memcpy
(
ucontrol
->
value
.
bytes
.
data
,
intelhaddata
->
eld
,
HDMI_MAX_ELD_BYTES
);
mutex_unlock
(
&
intelhaddata
->
mutex
);
return
0
;
}
static
struct
had_ops
had_ops_v1
=
{
.
enable_audio
=
snd_intelhad_enable_audio_v1
,
.
reset_audio
=
snd_intelhad_reset_audio_v1
,
.
prog_n
=
snd_intelhad_prog_n_v1
,
.
prog_cts
=
snd_intelhad_prog_cts_v1
,
.
audio_ctrl
=
snd_intelhad_prog_audio_ctrl_v1
,
.
prog_dip
=
snd_intelhad_prog_dip_v1
,
.
handle_underrun
=
had_clear_underrun_intr_v1
,
static
const
struct
snd_kcontrol_new
had_controls
[]
=
{
{
.
access
=
SNDRV_CTL_ELEM_ACCESS_READ
,
.
iface
=
SNDRV_CTL_ELEM_IFACE_PCM
,
.
name
=
SNDRV_CTL_NAME_IEC958
(
""
,
PLAYBACK
,
MASK
),
.
info
=
had_iec958_info
,
/* shared */
.
get
=
had_iec958_mask_get
,
},
{
.
iface
=
SNDRV_CTL_ELEM_IFACE_PCM
,
.
name
=
SNDRV_CTL_NAME_IEC958
(
""
,
PLAYBACK
,
DEFAULT
),
.
info
=
had_iec958_info
,
.
get
=
had_iec958_get
,
.
put
=
had_iec958_put
,
},
{
.
access
=
(
SNDRV_CTL_ELEM_ACCESS_READ
|
SNDRV_CTL_ELEM_ACCESS_VOLATILE
),
.
iface
=
SNDRV_CTL_ELEM_IFACE_PCM
,
.
name
=
"ELD"
,
.
info
=
had_ctl_eld_info
,
.
get
=
had_ctl_eld_get
,
},
};
static
struct
had_ops
had_ops_v2
=
{
.
enable_audio
=
snd_intelhad_enable_audio_v2
,
.
reset_audio
=
snd_intelhad_reset_audio_v2
,
.
prog_n
=
snd_intelhad_prog_n_v2
,
.
prog_cts
=
snd_intelhad_prog_cts_v2
,
.
audio_ctrl
=
snd_intelhad_prog_audio_ctrl_v2
,
.
prog_dip
=
snd_intelhad_prog_dip_v2
,
.
handle_underrun
=
had_clear_underrun_intr_v2
,
};
/**
* hdmi_audio_probe - to create sound card instance for HDMI audio playabck
*
*@haddata: pointer to HAD private data
*@card_id: card for which probe is called
*
* This function is called when the hdmi cable is plugged in. This function
* creates and registers the sound card with ALSA
/*
* audio interrupt handler
*/
int
hdmi_audio_probe
(
void
*
deviceptr
)
static
irqreturn_t
display_pipe_interrupt_handler
(
int
irq
,
void
*
dev_id
)
{
int
retval
;
struct
snd_pcm
*
pcm
;
struct
snd_card
*
card
;
struct
had_callback_ops
ops_cb
;
struct
snd_intelhad
*
intelhaddata
;
struct
had_pvt_data
*
had_stream
;
struct
platform_device
*
devptr
=
deviceptr
;
struct
snd_intelhad
*
ctx
=
dev_id
;
u32
audio_stat
,
audio_reg
;
audio_reg
=
AUD_HDMI_STATUS
;
mid_hdmi_audio_read
(
ctx
,
audio_reg
,
&
audio_stat
);
if
(
audio_stat
&
HDMI_AUDIO_UNDERRUN
)
{
mid_hdmi_audio_write
(
ctx
,
audio_reg
,
HDMI_AUDIO_UNDERRUN
);
had_process_buffer_underrun
(
ctx
);
}
if
(
audio_stat
&
HDMI_AUDIO_BUFFER_DONE
)
{
mid_hdmi_audio_write
(
ctx
,
audio_reg
,
HDMI_AUDIO_BUFFER_DONE
);
had_process_buffer_done
(
ctx
);
}
return
IRQ_HANDLED
;
}
/*
* monitor plug/unplug notification from i915; just kick off the work
*/
static
void
notify_audio_lpe
(
struct
platform_device
*
pdev
)
{
struct
snd_intelhad
*
ctx
=
platform_get_drvdata
(
pdev
);
schedule_work
(
&
ctx
->
hdmi_audio_wq
);
}
/* the work to handle monitor hot plug/unplug */
static
void
had_audio_wq
(
struct
work_struct
*
work
)
{
struct
snd_intelhad
*
ctx
=
container_of
(
work
,
struct
snd_intelhad
,
hdmi_audio_wq
);
struct
intel_hdmi_lpe_audio_pdata
*
pdata
=
ctx
->
dev
->
platform_data
;
pm_runtime_get_sync
(
ctx
->
dev
);
mutex_lock
(
&
ctx
->
mutex
);
if
(
!
pdata
->
hdmi_connected
)
{
dev_dbg
(
ctx
->
dev
,
"%s: Event: HAD_NOTIFY_HOT_UNPLUG
\n
"
,
__func__
);
memset
(
ctx
->
eld
,
0
,
sizeof
(
ctx
->
eld
));
/* clear the old ELD */
had_process_hot_unplug
(
ctx
);
}
else
{
struct
intel_hdmi_lpe_audio_eld
*
eld
=
&
pdata
->
eld
;
dev_dbg
(
ctx
->
dev
,
"%s: HAD_NOTIFY_ELD : port = %d, tmds = %d
\n
"
,
__func__
,
eld
->
port_id
,
pdata
->
tmds_clock_speed
);
pr_debug
(
"Enter %s
\n
"
,
__func__
);
switch
(
eld
->
pipe_id
)
{
case
0
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_A
;
break
;
case
1
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_B
;
break
;
case
2
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_C
;
break
;
default:
dev_dbg
(
ctx
->
dev
,
"Invalid pipe %d
\n
"
,
eld
->
pipe_id
);
break
;
}
pr_debug
(
"hdmi_audio_probe dma_mask: %p
\n
"
,
devptr
->
dev
.
dma_mask
);
memcpy
(
ctx
->
eld
,
eld
->
eld_data
,
sizeof
(
ctx
->
eld
)
);
/* allocate memory for saving internal context and working */
intelhaddata
=
kzalloc
(
sizeof
(
*
intelhaddata
),
GFP_KERNEL
);
if
(
!
intelhaddata
)
return
-
ENOMEM
;
ctx
->
dp_output
=
pdata
->
dp_output
;
ctx
->
tmds_clock_speed
=
pdata
->
tmds_clock_speed
;
ctx
->
link_rate
=
pdata
->
link_rate
;
had_stream
=
kzalloc
(
sizeof
(
*
had_stream
),
GFP_KERNEL
);
if
(
!
had_stream
)
{
retval
=
-
ENOMEM
;
goto
free_haddata
;
had_process_hot_plug
(
ctx
);
/* Process mode change if stream is active */
hdmi_audio_mode_change
(
ctx
)
;
}
mutex_unlock
(
&
ctx
->
mutex
);
pm_runtime_put
(
ctx
->
dev
);
}
had_data
=
intelhaddata
;
ops_cb
.
intel_had_event_call_back
=
had_event_handler
;
/*
* PM callbacks
*/
/* registering with display driver to get access to display APIs */
static
int
hdmi_lpe_audio_runtime_suspend
(
struct
device
*
dev
)
{
struct
snd_intelhad
*
ctx
=
dev_get_drvdata
(
dev
);
struct
snd_pcm_substream
*
substream
;
retval
=
mid_hdmi_audio_setup
(
ops_cb
.
intel_had_event_call_back
,
&
(
intelhaddata
->
reg_ops
),
&
(
intelhaddata
->
query_ops
));
if
(
retval
)
{
pr_err
(
"querying display driver APIs failed %#x
\n
"
,
retval
);
goto
free_hadstream
;
substream
=
had_substream_get
(
ctx
);
if
(
substream
)
{
snd_pcm_suspend
(
substream
);
had_substream_put
(
ctx
);
}
return
0
;
}
static
int
hdmi_lpe_audio_suspend
(
struct
device
*
dev
)
{
struct
snd_intelhad
*
ctx
=
dev_get_drvdata
(
dev
);
int
err
;
err
=
hdmi_lpe_audio_runtime_suspend
(
dev
);
if
(
!
err
)
snd_power_change_state
(
ctx
->
card
,
SNDRV_CTL_POWER_D3hot
);
return
err
;
}
static
int
hdmi_lpe_audio_resume
(
struct
device
*
dev
)
{
struct
snd_intelhad
*
ctx
=
dev_get_drvdata
(
dev
);
snd_power_change_state
(
ctx
->
card
,
SNDRV_CTL_POWER_D0
);
return
0
;
}
/* release resources */
static
void
hdmi_lpe_audio_free
(
struct
snd_card
*
card
)
{
struct
snd_intelhad
*
ctx
=
card
->
private_data
;
cancel_work_sync
(
&
ctx
->
hdmi_audio_wq
);
if
(
ctx
->
mmio_start
)
iounmap
(
ctx
->
mmio_start
);
if
(
ctx
->
irq
>=
0
)
free_irq
(
ctx
->
irq
,
ctx
);
}
/*
* hdmi_lpe_audio_probe - start bridge with i915
*
* This function is called when the i915 driver creates the
* hdmi-lpe-audio platform device.
*/
static
int
hdmi_lpe_audio_probe
(
struct
platform_device
*
pdev
)
{
struct
snd_card
*
card
;
struct
snd_intelhad
*
ctx
;
struct
snd_pcm
*
pcm
;
struct
intel_hdmi_lpe_audio_pdata
*
pdata
;
int
irq
;
struct
resource
*
res_mmio
;
int
i
,
ret
;
dev_dbg
(
&
pdev
->
dev
,
"dma_mask: %p
\n
"
,
pdev
->
dev
.
dma_mask
);
pdata
=
pdev
->
dev
.
platform_data
;
if
(
!
pdata
)
{
dev_err
(
&
pdev
->
dev
,
"%s: quit: pdata not allocated by i915!!
\n
"
,
__func__
);
return
-
EINVAL
;
}
/* get resources */
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"Could not get irq resource
\n
"
);
return
-
ENODEV
;
}
res_mmio
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res_mmio
)
{
dev_err
(
&
pdev
->
dev
,
"Could not get IO_MEM resources
\n
"
);
return
-
ENXIO
;
}
mutex_lock
(
&
had_mutex
);
spin_lock_init
(
&
intelhaddata
->
had_spinlock
);
intelhaddata
->
drv_status
=
HAD_DRV_DISCONNECTED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED
\n
"
,
__func__
,
__LINE__
);
/* create a card instance with ALSA framework */
retval
=
snd_card_new
(
&
devptr
->
dev
,
hdmi_card_index
,
hdmi_card_id
,
THIS_MODULE
,
0
,
&
card
);
if
(
retval
)
goto
unlock_mutex
;
intelhaddata
->
card
=
card
;
intelhaddata
->
card_id
=
hdmi_card_id
;
intelhaddata
->
card_index
=
card
->
number
;
intelhaddata
->
private_data
=
had_stream
;
intelhaddata
->
flag_underrun
=
0
;
intelhaddata
->
aes_bits
=
SNDRV_PCM_DEFAULT_CON_SPDIF
;
strncpy
(
card
->
driver
,
INTEL_HAD
,
strlen
(
INTEL_HAD
));
strncpy
(
card
->
shortname
,
INTEL_HAD
,
strlen
(
INTEL_HAD
));
retval
=
snd_pcm_new
(
card
,
INTEL_HAD
,
PCM_INDEX
,
MAX_PB_STREAMS
,
MAX_CAP_STREAMS
,
&
pcm
);
if
(
retval
)
ret
=
snd_card_new
(
&
pdev
->
dev
,
hdmi_card_index
,
hdmi_card_id
,
THIS_MODULE
,
sizeof
(
*
ctx
),
&
card
);
if
(
ret
)
return
ret
;
ctx
=
card
->
private_data
;
spin_lock_init
(
&
ctx
->
had_spinlock
);
mutex_init
(
&
ctx
->
mutex
);
ctx
->
connected
=
false
;
ctx
->
dev
=
&
pdev
->
dev
;
ctx
->
card
=
card
;
ctx
->
aes_bits
=
SNDRV_PCM_DEFAULT_CON_SPDIF
;
strcpy
(
card
->
driver
,
INTEL_HAD
);
strcpy
(
card
->
shortname
,
INTEL_HAD
);
ctx
->
irq
=
-
1
;
ctx
->
tmds_clock_speed
=
DIS_SAMPLE_RATE_148_5
;
INIT_WORK
(
&
ctx
->
hdmi_audio_wq
,
had_audio_wq
);
card
->
private_free
=
hdmi_lpe_audio_free
;
/* assume pipe A as default */
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_A
;
platform_set_drvdata
(
pdev
,
ctx
);
dev_dbg
(
&
pdev
->
dev
,
"%s: mmio_start = 0x%x, mmio_end = 0x%x
\n
"
,
__func__
,
(
unsigned
int
)
res_mmio
->
start
,
(
unsigned
int
)
res_mmio
->
end
);
ctx
->
mmio_start
=
ioremap_nocache
(
res_mmio
->
start
,
(
size_t
)(
resource_size
(
res_mmio
)));
if
(
!
ctx
->
mmio_start
)
{
dev_err
(
&
pdev
->
dev
,
"Could not get ioremap
\n
"
);
ret
=
-
EACCES
;
goto
err
;
}
/* setup interrupt handler */
ret
=
request_irq
(
irq
,
display_pipe_interrupt_handler
,
0
,
pdev
->
name
,
ctx
);
if
(
ret
<
0
)
{
dev_err
(
&
pdev
->
dev
,
"request_irq failed
\n
"
);
goto
err
;
}
ctx
->
irq
=
irq
;
ret
=
snd_pcm_new
(
card
,
INTEL_HAD
,
PCM_INDEX
,
MAX_PB_STREAMS
,
MAX_CAP_STREAMS
,
&
pcm
);
if
(
ret
)
goto
err
;
/* setup private data which can be retrieved when required */
pcm
->
private_data
=
intelhaddata
;
pcm
->
private_free
=
snd_intelhad_pcm_free
;
pcm
->
private_data
=
ctx
;
pcm
->
info_flags
=
0
;
strncpy
(
pcm
->
name
,
card
->
shortname
,
strlen
(
card
->
shortname
));
/* setup the ops for p
al
yabck */
/* setup the ops for p
la
yabck */
snd_pcm_set_ops
(
pcm
,
SNDRV_PCM_STREAM_PLAYBACK
,
&
snd_intelhad_playback_ops
);
/* allocate dma pages for ALSA stream operations
* memory allocated is based on size, not max value
* thus using same argument for max & size
*/
retval
=
snd_pcm_lib_preallocate_pages_for_all
(
pcm
,
snd_pcm_lib_preallocate_pages_for_all
(
pcm
,
SNDRV_DMA_TYPE_DEV
,
NULL
,
HAD_MAX_BUFFER
,
HAD_MAX_BUFFER
);
if
(
card
->
dev
==
NULL
)
pr_debug
(
"card->dev is NULL!!!!! Should not be this case
\n
"
);
else
if
(
card
->
dev
->
dma_mask
==
NULL
)
pr_debug
(
"hdmi_audio_probe dma_mask is NULL!!!!!
\n
"
);
else
pr_debug
(
"hdmi_audio_probe dma_mask is : %p
\n
"
,
card
->
dev
->
dma_mask
);
if
(
retval
)
goto
err
;
/* create controls */
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
had_controls
);
i
++
)
{
ret
=
snd_ctl_add
(
card
,
snd_ctl_new1
(
&
had_controls
[
i
],
ctx
));
if
(
ret
<
0
)
goto
err
;
}
/* internal function call to register device with ALSA */
retval
=
snd_intelhad_create
(
intelhaddata
,
card
);
if
(
retval
)
goto
err
;
init_channel_allocations
();
card
->
private_data
=
&
intelhaddata
;
ret
val
=
snd_card_register
(
card
);
if
(
ret
val
)
/* Register channel map controls */
ret
=
had_register_chmap_ctls
(
ctx
,
pcm
);
if
(
ret
<
0
)
goto
err
;
/* IEC958 controls */
retval
=
snd_ctl_add
(
card
,
snd_ctl_new1
(
&
had_control_iec958_mask
,
intelhaddata
));
if
(
retval
<
0
)
goto
err
;
retval
=
snd_ctl_add
(
card
,
snd_ctl_new1
(
&
had_control_iec958
,
intelhaddata
));
if
(
retval
<
0
)
ret
=
snd_card_register
(
card
);
if
(
ret
)
goto
err
;
init_channel_allocations
();
spin_lock_irq
(
&
pdata
->
lpe_audio_slock
);
pdata
->
notify_audio_lpe
=
notify_audio_lpe
;
pdata
->
notify_pending
=
false
;
spin_unlock_irq
(
&
pdata
->
lpe_audio_slock
);
/* Register channel map controls */
retval
=
had_register_chmap_ctls
(
intelhaddata
,
pcm
);
if
(
retval
<
0
)
goto
err
;
pm_runtime_set_active
(
&
pdev
->
dev
);
pm_runtime_enable
(
&
pdev
->
dev
);
intelhaddata
->
dev
=
&
devptr
->
dev
;
pm_runtime_set_active
(
intelhaddata
->
dev
);
pm_runtime_enable
(
intelhaddata
->
dev
);
dev_dbg
(
&
pdev
->
dev
,
"%s: handle pending notification
\n
"
,
__func__
);
schedule_work
(
&
ctx
->
hdmi_audio_wq
);
mutex_unlock
(
&
had_mutex
);
retval
=
mid_hdmi_audio_register
(
&
had_interface
,
intelhaddata
);
if
(
retval
)
{
pr_err
(
"registering with display driver failed %#x
\n
"
,
retval
);
snd_card_free
(
card
);
goto
free_hadstream
;
}
intelhaddata
->
hw_silence
=
1
;
had_ops_v1
=
had_ops_v1
;
/* unused */
intelhaddata
->
ops
=
&
had_ops_v2
;
return
0
;
return
retval
;
err:
snd_card_free
(
card
);
unlock_mutex:
mutex_unlock
(
&
had_mutex
);
free_hadstream:
kfree
(
had_stream
);
pm_runtime_disable
(
intelhaddata
->
dev
);
intelhaddata
->
dev
=
NULL
;
free_haddata:
kfree
(
intelhaddata
);
intelhaddata
=
NULL
;
pr_err
(
"Error returned from %s api %#x
\n
"
,
__func__
,
retval
);
return
retval
;
return
ret
;
}
/**
* hdmi_audio_remove - removes the alsa card
*
*@haddata: pointer to HAD private data
/*
* hdmi_lpe_audio_remove - stop bridge with i915
*
* This function is called when the hdmi cable is un-plugged. This function
* free the sound card.
* This function is called when the platform device is destroyed.
*/
int
hdmi_audio_remove
(
void
*
pdevptr
)
static
int
hdmi_lpe_audio_remove
(
struct
platform_device
*
pdev
)
{
struct
snd_intelhad
*
intelhaddata
=
had_data
;
int
caps
;
struct
snd_intelhad
*
ctx
=
platform_get_drvdata
(
pdev
);
pr_debug
(
"Enter %s
\n
"
,
__func__
);
if
(
!
intelhaddata
)
return
0
;
if
(
intelhaddata
->
drv_status
!=
HAD_DRV_DISCONNECTED
)
{
caps
=
HDMI_AUDIO_UNDERRUN
|
HDMI_AUDIO_BUFFER_DONE
;
had_set_caps
(
HAD_SET_DISABLE_AUDIO_INT
,
&
caps
);
had_set_caps
(
HAD_SET_DISABLE_AUDIO
,
NULL
);
}
snd_card_free
(
intelhaddata
->
card
);
kfree
(
intelhaddata
->
private_data
);
kfree
(
intelhaddata
);
if
(
ctx
->
connected
)
snd_intelhad_enable_audio_int
(
ctx
,
false
);
snd_card_free
(
ctx
->
card
);
return
0
;
}
static
const
struct
dev_pm_ops
hdmi_lpe_audio_pm
=
{
SET_SYSTEM_SLEEP_PM_OPS
(
hdmi_lpe_audio_suspend
,
hdmi_lpe_audio_resume
)
SET_RUNTIME_PM_OPS
(
hdmi_lpe_audio_runtime_suspend
,
NULL
,
NULL
)
};
static
struct
platform_driver
hdmi_lpe_audio_driver
=
{
.
driver
=
{
.
name
=
"hdmi-lpe-audio"
,
.
pm
=
&
hdmi_lpe_audio_pm
,
},
.
probe
=
hdmi_lpe_audio_probe
,
.
remove
=
hdmi_lpe_audio_remove
,
};
module_platform_driver
(
hdmi_lpe_audio_driver
);
MODULE_ALIAS
(
"platform:hdmi_lpe_audio"
);
MODULE_AUTHOR
(
"Sailaja Bandarupalli <sailaja.bandarupalli@intel.com>"
);
MODULE_AUTHOR
(
"Ramesh Babu K V <ramesh.babu@intel.com>"
);
MODULE_AUTHOR
(
"Vaibhav Agarwal <vaibhav.agarwal@intel.com>"
);
...
...
sound/x86/intel_hdmi_audio.h
View file @
c1a7c40c
...
...
@@ -30,19 +30,11 @@
#ifndef _INTEL_HDMI_AUDIO_H_
#define _INTEL_HDMI_AUDIO_H_
#include <linux/types.h>
#include <sound/initval.h>
#include <linux/version.h>
#include <linux/pm_runtime.h>
#include <sound/asoundef.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include "intel_hdmi_lpe_audio.h"
#define PCM_INDEX 0
#define MAX_PB_STREAMS 1
#define MAX_CAP_STREAMS 0
#define HDMI_AUDIO_DRIVER "hdmi-audio"
#define HDMI_INFO_FRAME_WORD1 0x000a0184
#define DP_INFO_FRAME_WORD1 0x00441b84
...
...
@@ -64,21 +56,18 @@
#define SMPL_WIDTH_16BITS 0x1
#define SMPL_WIDTH_24BITS 0x5
#define CHANNEL_ALLOCATION 0x1F
#define MASK_BYTE0 0x000000FF
#define VALID_DIP_WORDS 3
#define LAYOUT0 0
#define LAYOUT1 1
#define SWAP_LFE_CENTER 0x00fac4c8
#define AUD_CONFIG_CH_MASK
_V2
0x70
#define AUD_CONFIG_CH_MASK 0x70
struct
pcm_stream_info
{
int
str_id
;
void
*
had_substream
;
void
(
*
period_elapsed
)(
void
*
had_substream
);
u32
buffer_ptr
;
struct
snd_pcm_substream
*
substream
;
u64
buffer_rendered
;
u32
ring_buf_size
;
int
sfreq
;
int
substream_refcount
;
bool
running
;
};
struct
ring_buf_info
{
...
...
@@ -87,113 +76,47 @@ struct ring_buf_info {
u8
is_valid
;
};
struct
had_stream_pvt
{
enum
had_stream_status
stream_status
;
int
stream_ops
;
ssize_t
dbg_cum_bytes
;
};
struct
had_pvt_data
{
enum
had_status_stream
stream_type
;
};
struct
had_callback_ops
{
had_event_call_back
intel_had_event_call_back
;
};
/**
/*
* struct snd_intelhad - intelhad driver structure
*
* @card: ptr to hold card details
* @card_index: sound card index
* @card_id: detected sound card id
* @reg_ops: register operations to program registers
* @query_ops: caps call backs for get/set operations
* @drv_status: driver status
* @connected: the monitor connection status
* @buf_info: ring buffer info
* @stream_info: stream information
* @e
eld: holds E
ELD info
* @e
ld: holds
ELD info
* @curr_buf: pointer to hold current active ring buf
* @valid_buf_cnt: ring buffer count for stream
* @had_spinlock: driver lock
* @aes_bits: IEC958 status bits
* @buff_done: id of current buffer done intr
* @dev: platoform device handle
* @kctl: holds kctl ptrs used for channel map
* @chmap: holds channel map info
* @audio_reg_base: hdmi audio register base offset
* @hw_silence: flag indicates SoC support for HW silence/Keep alive
* @ops: holds ops functions based on platform
* @underrun_count: PCM stream underrun counter
*/
struct
snd_intelhad
{
struct
snd_card
*
card
;
int
card_index
;
char
*
card_id
;
struct
hdmi_audio_registers_ops
reg_ops
;
struct
hdmi_audio_query_set_ops
query_ops
;
enum
had_drv_status
drv_status
;
bool
connected
;
struct
ring_buf_info
buf_info
[
HAD_NUM_OF_RING_BUFS
];
struct
pcm_stream_info
stream_info
;
un
ion
otm_hdmi_eld_t
eeld
;
un
signed
char
eld
[
HDMI_MAX_ELD_BYTES
]
;
bool
dp_output
;
enum
intel_had_aud_buf_type
curr_buf
;
int
valid_buf_cnt
;
unsigned
int
aes_bits
;
int
flag_underrun
;
struct
had_pvt_data
*
private_data
;
spinlock_t
had_spinlock
;
enum
intel_had_aud_buf_type
buff_done
;
struct
device
*
dev
;
struct
snd_kcontrol
*
kctl
;
struct
snd_pcm_chmap
*
chmap
;
unsigned
int
*
audio_reg_base
;
unsigned
int
audio_cfg_offset
;
bool
hw_silence
;
struct
had_ops
*
ops
;
int
underrun_count
;
int
tmds_clock_speed
;
int
link_rate
;
/* internal stuff */
int
irq
;
void
__iomem
*
mmio_start
;
unsigned
int
had_config_offset
;
struct
work_struct
hdmi_audio_wq
;
struct
mutex
mutex
;
/* for protecting chmap and eld */
};
struct
had_ops
{
void
(
*
enable_audio
)(
struct
snd_pcm_substream
*
substream
,
u8
enable
);
void
(
*
reset_audio
)(
u8
reset
);
int
(
*
prog_n
)(
u32
aud_samp_freq
,
u32
*
n_param
,
struct
snd_intelhad
*
intelhaddata
);
void
(
*
prog_cts
)(
u32
aud_samp_freq
,
u32
tmds
,
u32
link_rate
,
u32
n_param
,
struct
snd_intelhad
*
intelhaddata
);
int
(
*
audio_ctrl
)(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
);
void
(
*
prog_dip
)(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
);
void
(
*
handle_underrun
)(
struct
snd_intelhad
*
intelhaddata
);
};
int
had_event_handler
(
enum
had_event_type
event_type
,
void
*
data
);
int
hdmi_audio_query
(
void
*
drv_data
,
struct
hdmi_audio_event
event
);
int
hdmi_audio_suspend
(
void
*
drv_data
,
struct
hdmi_audio_event
event
);
int
hdmi_audio_resume
(
void
*
drv_data
);
int
hdmi_audio_mode_change
(
struct
snd_pcm_substream
*
substream
);
extern
struct
snd_pcm_ops
snd_intelhad_playback_ops
;
int
snd_intelhad_init_audio_ctrl
(
struct
snd_pcm_substream
*
substream
,
struct
snd_intelhad
*
intelhaddata
,
int
flag_silence
);
int
snd_intelhad_prog_buffer
(
struct
snd_intelhad
*
intelhaddata
,
int
start
,
int
end
);
int
snd_intelhad_invd_buffer
(
int
start
,
int
end
);
int
snd_intelhad_read_len
(
struct
snd_intelhad
*
intelhaddata
);
void
had_build_channel_allocation_map
(
struct
snd_intelhad
*
intelhaddata
);
/* Register access functions */
int
had_get_hwstate
(
struct
snd_intelhad
*
intelhaddata
);
int
had_get_caps
(
enum
had_caps_list
query_element
,
void
*
capabilties
);
int
had_set_caps
(
enum
had_caps_list
set_element
,
void
*
capabilties
);
int
had_read_register
(
u32
reg_addr
,
u32
*
data
);
int
had_write_register
(
u32
reg_addr
,
u32
data
);
int
had_read_modify
(
u32
reg_addr
,
u32
data
,
u32
mask
);
int
hdmi_audio_probe
(
void
*
devptr
);
int
hdmi_audio_remove
(
void
*
pdev
);
#endif
/* _INTEL_HDMI_AUDIO_ */
sound/x86/intel_hdmi_audio_if.c
deleted
100644 → 0
View file @
374a5040
/*
* intel_hdmi_audio_if.c - Intel HDMI audio driver for MID
*
* Copyright (C) 2016 Intel Corp
* Authors: Sailaja Bandarupalli <sailaja.bandarupalli@intel.com>
* Ramesh Babu K V <ramesh.babu@intel.com>
* Vaibhav Agarwal <vaibhav.agarwal@intel.com>
* Jerome Anand <jerome.anand@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ALSA driver for Intel MID HDMI audio controller. This file contains
* interface functions exposed to HDMI Display driver and code to register
* with ALSA framework..
*/
#define pr_fmt(fmt) "had: " fmt
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include <sound/pcm.h>
#include <sound/core.h>
#include "intel_hdmi_audio.h"
#include "intel_hdmi_lpe_audio.h"
/**
* hdmi_audio_query - hdmi audio query function
*
*@haddata: pointer to HAD private data
*@event: audio event for which this method is invoked
*
* This function is called by client driver to query the
* hdmi audio.
*/
int
hdmi_audio_query
(
void
*
haddata
,
struct
hdmi_audio_event
event
)
{
struct
snd_pcm_substream
*
substream
=
NULL
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
struct
snd_intelhad
*
intelhaddata
=
(
struct
snd_intelhad
*
)
haddata
;
if
(
intelhaddata
->
stream_info
.
had_substream
)
substream
=
intelhaddata
->
stream_info
.
had_substream
;
had_stream
=
intelhaddata
->
private_data
;
switch
(
event
.
type
)
{
case
HAD_EVENT_QUERY_IS_AUDIO_BUSY
:
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
((
had_stream
->
stream_type
==
HAD_RUNNING_STREAM
)
||
substream
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"Audio stream active
\n
"
);
return
-
EBUSY
;
}
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
break
;
case
HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED
:
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_SUSPENDED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"Audio is suspended
\n
"
);
return
1
;
}
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
break
;
default:
pr_debug
(
"error un-handled event !!
\n
"
);
return
-
EINVAL
;
break
;
}
return
0
;
}
/**
* hdmi_audio_suspend - power management suspend function
*
*@haddata: pointer to HAD private data
*@event: pm event for which this method is invoked
*
* This function is called by client driver to suspend the
* hdmi audio.
*/
int
hdmi_audio_suspend
(
void
*
haddata
,
struct
hdmi_audio_event
event
)
{
int
caps
,
retval
=
0
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
struct
snd_pcm_substream
*
substream
;
struct
snd_intelhad
*
intelhaddata
=
(
struct
snd_intelhad
*
)
haddata
;
pr_debug
(
"Enter:%s
\n
"
,
__func__
);
had_stream
=
intelhaddata
->
private_data
;
substream
=
intelhaddata
->
stream_info
.
had_substream
;
if
(
intelhaddata
->
dev
->
power
.
runtime_status
!=
RPM_SUSPENDED
)
{
pr_err
(
"audio stream is active
\n
"
);
return
-
EAGAIN
;
}
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"had not connected
\n
"
);
return
retval
;
}
if
(
intelhaddata
->
drv_status
==
HAD_DRV_SUSPENDED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"had already suspended
\n
"
);
return
retval
;
}
intelhaddata
->
drv_status
=
HAD_DRV_SUSPENDED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_SUSPENDED
\n
"
,
__func__
,
__LINE__
);
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
/*
* ToDo: Need to disable UNDERRUN interrupts as well
* caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
*/
caps
=
HDMI_AUDIO_BUFFER_DONE
;
had_set_caps
(
HAD_SET_DISABLE_AUDIO_INT
,
&
caps
);
had_set_caps
(
HAD_SET_DISABLE_AUDIO
,
NULL
);
pr_debug
(
"Exit:%s"
,
__func__
);
return
retval
;
}
/**
* hdmi_audio_resume - power management resume function
*
*@haddata: pointer to HAD private data
*
* This function is called by client driver to resume the
* hdmi audio.
*/
int
hdmi_audio_resume
(
void
*
haddata
)
{
int
caps
,
retval
=
0
;
struct
snd_intelhad
*
intelhaddata
=
(
struct
snd_intelhad
*
)
haddata
;
unsigned
long
flag_irqs
;
pr_debug
(
"Enter:%s
\n
"
,
__func__
);
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"had not connected
\n
"
);
return
0
;
}
if
(
intelhaddata
->
drv_status
!=
HAD_DRV_SUSPENDED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_err
(
"had is not in suspended state
\n
"
);
return
0
;
}
if
(
had_get_hwstate
(
intelhaddata
))
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_err
(
"Failed to resume. Device not accessible
\n
"
);
return
-
ENODEV
;
}
intelhaddata
->
drv_status
=
HAD_DRV_CONNECTED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED
\n
"
,
__func__
,
__LINE__
);
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
/*
* ToDo: Need to enable UNDERRUN interrupts as well
* caps = HDMI_AUDIO_UNDERRUN | HDMI_AUDIO_BUFFER_DONE;
*/
caps
=
HDMI_AUDIO_BUFFER_DONE
;
retval
=
had_set_caps
(
HAD_SET_ENABLE_AUDIO_INT
,
&
caps
);
retval
=
had_set_caps
(
HAD_SET_ENABLE_AUDIO
,
NULL
);
pr_debug
(
"Exit:%s"
,
__func__
);
return
retval
;
}
static
inline
int
had_chk_intrmiss
(
struct
snd_intelhad
*
intelhaddata
,
enum
intel_had_aud_buf_type
buf_id
)
{
int
i
,
intr_count
=
0
;
enum
intel_had_aud_buf_type
buff_done
;
u32
buf_size
,
buf_addr
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
had_stream
=
intelhaddata
->
private_data
;
buff_done
=
buf_id
;
intr_count
=
snd_intelhad_read_len
(
intelhaddata
);
if
(
intr_count
>
1
)
{
/* In case of active playback */
pr_err
(
"Driver detected %d missed buffer done interrupt(s)!!!!
\n
"
,
(
intr_count
-
1
));
if
(
intr_count
>
3
)
return
intr_count
;
buf_id
+=
(
intr_count
-
1
);
/* Reprogram registers*/
for
(
i
=
buff_done
;
i
<
buf_id
;
i
++
)
{
int
j
=
i
%
4
;
buf_size
=
intelhaddata
->
buf_info
[
j
].
buf_size
;
buf_addr
=
intelhaddata
->
buf_info
[
j
].
buf_addr
;
had_write_register
(
AUD_BUF_A_LENGTH
+
(
j
*
HAD_REG_WIDTH
),
buf_size
);
had_write_register
(
AUD_BUF_A_ADDR
+
(
j
*
HAD_REG_WIDTH
),
(
buf_addr
|
BIT
(
0
)
|
BIT
(
1
)));
}
buf_id
=
buf_id
%
4
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
intelhaddata
->
buff_done
=
buf_id
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
}
return
intr_count
;
}
int
had_process_buffer_done
(
struct
snd_intelhad
*
intelhaddata
)
{
u32
len
=
1
;
enum
intel_had_aud_buf_type
buf_id
;
enum
intel_had_aud_buf_type
buff_done
;
struct
pcm_stream_info
*
stream
;
u32
buf_size
;
struct
had_pvt_data
*
had_stream
;
int
intr_count
;
enum
had_status_stream
stream_type
;
unsigned
long
flag_irqs
;
had_stream
=
intelhaddata
->
private_data
;
stream
=
&
intelhaddata
->
stream_info
;
intr_count
=
1
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_err
(
"%s:Device already disconnected
\n
"
,
__func__
);
return
0
;
}
buf_id
=
intelhaddata
->
curr_buf
;
intelhaddata
->
buff_done
=
buf_id
;
buff_done
=
intelhaddata
->
buff_done
;
buf_size
=
intelhaddata
->
buf_info
[
buf_id
].
buf_size
;
stream_type
=
had_stream
->
stream_type
;
pr_debug
(
"Enter:%s buf_id=%d
\n
"
,
__func__
,
buf_id
);
/* Every debug statement has an implication
* of ~5msec. Thus, avoid having >3 debug statements
* for each buffer_done handling.
*/
/* Check for any intr_miss in case of active playback */
if
(
had_stream
->
stream_type
==
HAD_RUNNING_STREAM
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
intr_count
=
had_chk_intrmiss
(
intelhaddata
,
buf_id
);
if
(
!
intr_count
||
(
intr_count
>
3
))
{
pr_err
(
"HAD SW state in non-recoverable!!! mode
\n
"
);
pr_err
(
"Already played stale data
\n
"
);
return
0
;
}
buf_id
+=
(
intr_count
-
1
);
buf_id
=
buf_id
%
4
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
}
intelhaddata
->
buf_info
[
buf_id
].
is_valid
=
true
;
if
(
intelhaddata
->
valid_buf_cnt
-
1
==
buf_id
)
{
if
(
had_stream
->
stream_type
>=
HAD_RUNNING_STREAM
)
intelhaddata
->
curr_buf
=
HAD_BUF_TYPE_A
;
}
else
intelhaddata
->
curr_buf
=
buf_id
+
1
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
had_get_hwstate
(
intelhaddata
))
{
pr_err
(
"HDMI cable plugged-out
\n
"
);
return
0
;
}
/*Reprogram the registers with addr and length*/
had_write_register
(
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
buf_size
);
had_write_register
(
AUD_BUF_A_ADDR
+
(
buf_id
*
HAD_REG_WIDTH
),
intelhaddata
->
buf_info
[
buf_id
].
buf_addr
|
BIT
(
0
)
|
BIT
(
1
));
had_read_register
(
AUD_BUF_A_LENGTH
+
(
buf_id
*
HAD_REG_WIDTH
),
&
len
);
pr_debug
(
"%s:Enabled buf[%d]
\n
"
,
__func__
,
buf_id
);
/* In case of actual data,
* report buffer_done to above ALSA layer
*/
buf_size
=
intelhaddata
->
buf_info
[
buf_id
].
buf_size
;
if
(
stream_type
>=
HAD_RUNNING_STREAM
)
{
intelhaddata
->
stream_info
.
buffer_rendered
+=
(
intr_count
*
buf_size
);
stream
->
period_elapsed
(
stream
->
had_substream
);
}
return
0
;
}
int
had_process_buffer_underrun
(
struct
snd_intelhad
*
intelhaddata
)
{
enum
intel_had_aud_buf_type
buf_id
;
struct
pcm_stream_info
*
stream
;
struct
had_pvt_data
*
had_stream
;
enum
had_status_stream
stream_type
;
unsigned
long
flag_irqs
;
int
drv_status
;
had_stream
=
intelhaddata
->
private_data
;
stream
=
&
intelhaddata
->
stream_info
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
buf_id
=
intelhaddata
->
curr_buf
;
stream_type
=
had_stream
->
stream_type
;
intelhaddata
->
buff_done
=
buf_id
;
drv_status
=
intelhaddata
->
drv_status
;
if
(
stream_type
==
HAD_RUNNING_STREAM
)
intelhaddata
->
curr_buf
=
HAD_BUF_TYPE_A
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"Enter:%s buf_id=%d, stream_type=%d
\n
"
,
__func__
,
buf_id
,
stream_type
);
intelhaddata
->
ops
->
handle_underrun
(
intelhaddata
);
if
(
drv_status
==
HAD_DRV_DISCONNECTED
)
{
pr_err
(
"%s:Device already disconnected
\n
"
,
__func__
);
return
0
;
}
if
(
stream_type
==
HAD_RUNNING_STREAM
)
{
/* Report UNDERRUN error to above layers */
intelhaddata
->
flag_underrun
=
1
;
stream
->
period_elapsed
(
stream
->
had_substream
);
}
return
0
;
}
int
had_process_hot_plug
(
struct
snd_intelhad
*
intelhaddata
)
{
enum
intel_had_aud_buf_type
buf_id
;
struct
snd_pcm_substream
*
substream
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
pr_debug
(
"Enter:%s
\n
"
,
__func__
);
substream
=
intelhaddata
->
stream_info
.
had_substream
;
had_stream
=
intelhaddata
->
private_data
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_CONNECTED
)
{
pr_debug
(
"Device already connected
\n
"
);
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
return
0
;
}
buf_id
=
intelhaddata
->
curr_buf
;
intelhaddata
->
buff_done
=
buf_id
;
intelhaddata
->
drv_status
=
HAD_DRV_CONNECTED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_CONNECTED
\n
"
,
__func__
,
__LINE__
);
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"Processing HOT_PLUG, buf_id = %d
\n
"
,
buf_id
);
/* Query display driver for audio register base */
if
(
intelhaddata
->
reg_ops
.
hdmi_audio_get_register_base
(
&
intelhaddata
->
audio_reg_base
,
&
intelhaddata
->
audio_cfg_offset
))
{
pr_err
(
"Unable to get audio reg base from Display driver
\n
"
);
goto
err
;
}
if
(
intelhaddata
->
audio_reg_base
==
NULL
)
{
pr_err
(
"audio reg base value is NULL
\n
"
);
goto
err
;
}
pr_debug
(
"%s audio_reg_base = 0x%p
\n
"
,
__func__
,
intelhaddata
->
audio_reg_base
);
/* Safety check */
if
(
substream
)
{
pr_debug
(
"There should not be active PB from ALSA
\n
"
);
pr_debug
(
"Signifies, cable is plugged-in even before
\n
"
);
pr_debug
(
"processing snd_pcm_disconnect
\n
"
);
/* Set runtime->state to hw_params done */
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_SETUP
);
}
had_build_channel_allocation_map
(
intelhaddata
);
return
0
;
err:
pm_runtime_disable
(
intelhaddata
->
dev
);
intelhaddata
->
dev
=
NULL
;
return
0
;
}
int
had_process_hot_unplug
(
struct
snd_intelhad
*
intelhaddata
)
{
int
caps
,
retval
=
0
;
enum
intel_had_aud_buf_type
buf_id
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
pr_debug
(
"Enter:%s
\n
"
,
__func__
);
had_stream
=
intelhaddata
->
private_data
;
buf_id
=
intelhaddata
->
curr_buf
;
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
==
HAD_DRV_DISCONNECTED
)
{
pr_debug
(
"Device already disconnected
\n
"
);
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
return
retval
;
}
else
{
/* Disable Audio */
caps
=
HDMI_AUDIO_BUFFER_DONE
;
retval
=
had_set_caps
(
HAD_SET_DISABLE_AUDIO_INT
,
&
caps
);
retval
=
had_set_caps
(
HAD_SET_DISABLE_AUDIO
,
NULL
);
intelhaddata
->
ops
->
enable_audio
(
intelhaddata
->
stream_info
.
had_substream
,
0
);
}
intelhaddata
->
drv_status
=
HAD_DRV_DISCONNECTED
;
pr_debug
(
"%s @ %d:DEBUG PLUG/UNPLUG : HAD_DRV_DISCONNECTED
\n
"
,
__func__
,
__LINE__
);
/* Report to above ALSA layer */
if
(
intelhaddata
->
stream_info
.
had_substream
!=
NULL
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
pr_debug
(
"%s: unlock -> sending pcm_stop -> lock
\n
"
,
__func__
);
snd_pcm_stop
(
intelhaddata
->
stream_info
.
had_substream
,
SNDRV_PCM_STATE_DISCONNECTED
);
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
}
had_stream
->
stream_type
=
HAD_INIT
;
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
kfree
(
intelhaddata
->
chmap
->
chmap
);
intelhaddata
->
chmap
->
chmap
=
NULL
;
intelhaddata
->
audio_reg_base
=
NULL
;
pr_debug
(
"%s: unlocked -> returned
\n
"
,
__func__
);
return
retval
;
}
/**
* had_event_handler - Call back function to handle events
*
* @event_type: Event type to handle
* @data: data related to the event_type
*
* This function is invoked to handle HDMI events from client driver.
*/
int
had_event_handler
(
enum
had_event_type
event_type
,
void
*
data
)
{
int
retval
=
0
;
struct
snd_intelhad
*
intelhaddata
=
data
;
enum
intel_had_aud_buf_type
buf_id
;
struct
snd_pcm_substream
*
substream
;
struct
had_pvt_data
*
had_stream
;
unsigned
long
flag_irqs
;
buf_id
=
intelhaddata
->
curr_buf
;
had_stream
=
intelhaddata
->
private_data
;
/* Switching to a function can drop atomicity even in INTR context.
* Thus, a big lock is acquired to maintain atomicity.
* This can be optimized later.
* Currently, only buffer_done/_underrun executes in INTR context.
* Also, locking is implemented separately to avoid real contention
* of data(struct intelhaddata) between IRQ/SOFT_IRQ/PROCESS context.
*/
substream
=
intelhaddata
->
stream_info
.
had_substream
;
switch
(
event_type
)
{
case
HAD_EVENT_AUDIO_BUFFER_DONE
:
retval
=
had_process_buffer_done
(
intelhaddata
);
break
;
case
HAD_EVENT_AUDIO_BUFFER_UNDERRUN
:
retval
=
had_process_buffer_underrun
(
intelhaddata
);
break
;
case
HAD_EVENT_HOT_PLUG
:
retval
=
had_process_hot_plug
(
intelhaddata
);
break
;
case
HAD_EVENT_HOT_UNPLUG
:
retval
=
had_process_hot_unplug
(
intelhaddata
);
break
;
case
HAD_EVENT_MODE_CHANGING
:
pr_debug
(
" called _event_handler with _MODE_CHANGE event
\n
"
);
/* Process only if stream is active & cable Plugged-in */
spin_lock_irqsave
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
(
intelhaddata
->
drv_status
>=
HAD_DRV_DISCONNECTED
)
{
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
break
;
}
spin_unlock_irqrestore
(
&
intelhaddata
->
had_spinlock
,
flag_irqs
);
if
((
had_stream
->
stream_type
==
HAD_RUNNING_STREAM
)
&&
substream
)
retval
=
hdmi_audio_mode_change
(
substream
);
break
;
default:
pr_debug
(
"error un-handled event !!
\n
"
);
retval
=
-
EINVAL
;
break
;
}
return
retval
;
}
sound/x86/intel_hdmi_lpe_audio.c
deleted
100644 → 0
View file @
374a5040
/*
* intel_hdmi_lpe_audio.c - Intel HDMI LPE audio driver for Atom platforms
*
* Copyright (C) 2016 Intel Corp
* Authors:
* Jerome Anand <jerome.anand@intel.com>
* Aravind Siddappaji <aravindx.siddappaji@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <linux/platform_device.h>
#include <linux/irqreturn.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <drm/intel_lpe_audio.h>
#include "intel_hdmi_lpe_audio.h"
#include "intel_hdmi_audio.h"
/* globals*/
static
struct
platform_device
*
hlpe_pdev
;
static
int
hlpe_state
;
static
union
otm_hdmi_eld_t
hlpe_eld
;
struct
hdmi_lpe_audio_ctx
{
int
irq
;
void
__iomem
*
mmio_start
;
had_event_call_back
had_event_callbacks
;
struct
snd_intel_had_interface
*
had_interface
;
void
*
had_pvt_data
;
int
tmds_clock_speed
;
bool
dp_output
;
int
link_rate
;
unsigned
int
had_config_offset
;
int
hdmi_audio_interrupt_mask
;
struct
work_struct
hdmi_audio_wq
;
};
static
void
hdmi_set_eld
(
void
*
eld
)
{
int
size
;
BUILD_BUG_ON
(
sizeof
(
hlpe_eld
)
>
HDMI_MAX_ELD_BYTES
);
size
=
sizeof
(
hlpe_eld
);
memcpy
((
void
*
)
&
hlpe_eld
,
eld
,
size
);
}
static
int
hdmi_get_eld
(
void
*
eld
)
{
u8
*
eld_data
=
(
u8
*
)
&
hlpe_eld
;
memcpy
(
eld
,
(
void
*
)
&
hlpe_eld
,
sizeof
(
hlpe_eld
));
print_hex_dump_bytes
(
"eld: "
,
DUMP_PREFIX_NONE
,
eld_data
,
sizeof
(
hlpe_eld
));
return
0
;
}
static
struct
hdmi_lpe_audio_ctx
*
get_hdmi_context
(
void
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
return
ctx
;
}
/*
* return whether HDMI audio device is busy.
*/
bool
mid_hdmi_audio_is_busy
(
void
*
ddev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
int
hdmi_audio_busy
=
0
;
struct
hdmi_audio_event
hdmi_audio_event
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Enter"
,
__func__
);
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
if
(
hlpe_state
==
hdmi_connector_status_disconnected
)
{
/* HDMI is not connected, assuming audio device is idle. */
return
false
;
}
if
(
ctx
->
had_interface
)
{
hdmi_audio_event
.
type
=
HAD_EVENT_QUERY_IS_AUDIO_BUSY
;
hdmi_audio_busy
=
ctx
->
had_interface
->
query
(
ctx
->
had_pvt_data
,
hdmi_audio_event
);
return
hdmi_audio_busy
!=
0
;
}
return
false
;
}
/*
* return true if HDMI audio device is suspended/ disconnected
*/
bool
mid_hdmi_audio_suspend
(
void
*
ddev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
struct
hdmi_audio_event
hdmi_audio_event
;
int
ret
=
0
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
if
(
hlpe_state
==
hdmi_connector_status_disconnected
)
{
/* HDMI is not connected, assuming audio device
* is suspended already.
*/
return
true
;
}
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: hlpe_state %d"
,
__func__
,
hlpe_state
);
if
(
ctx
->
had_interface
)
{
hdmi_audio_event
.
type
=
0
;
ret
=
ctx
->
had_interface
->
suspend
(
ctx
->
had_pvt_data
,
hdmi_audio_event
);
return
(
ret
==
0
)
?
true
:
false
;
}
return
true
;
}
void
mid_hdmi_audio_resume
(
void
*
ddev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
if
(
hlpe_state
==
hdmi_connector_status_disconnected
)
{
/* HDMI is not connected, there is no need
* to resume audio device.
*/
return
;
}
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: hlpe_state %d"
,
__func__
,
hlpe_state
);
if
(
ctx
->
had_interface
)
ctx
->
had_interface
->
resume
(
ctx
->
had_pvt_data
);
}
void
mid_hdmi_audio_signal_event
(
enum
had_event_type
event
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Enter
\n
"
,
__func__
);
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
/* The handler is protected in the respective
* event handlers to avoid races
*/
if
(
ctx
->
had_event_callbacks
)
(
*
ctx
->
had_event_callbacks
)(
event
,
ctx
->
had_pvt_data
);
}
/**
* used to write into display controller HDMI audio registers.
*/
static
int
hdmi_audio_write
(
u32
reg
,
u32
val
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: reg[0x%x] = 0x%x
\n
"
,
__func__
,
reg
,
val
);
if
(
ctx
->
dp_output
)
{
if
((
reg
==
AUDIO_HDMI_CONFIG_A
)
||
(
reg
==
AUDIO_HDMI_CONFIG_B
)
||
(
reg
==
AUDIO_HDMI_CONFIG_C
))
{
if
(
val
&
AUD_CONFIG_VALID_BIT
)
val
=
val
|
AUD_CONFIG_DP_MODE
|
AUD_CONFIG_BLOCK_BIT
;
}
}
iowrite32
(
val
,
(
ctx
->
mmio_start
+
reg
));
return
0
;
}
/**
* used to get the register value read from
* display controller HDMI audio registers.
*/
static
int
hdmi_audio_read
(
u32
reg
,
u32
*
val
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
*
val
=
ioread32
(
ctx
->
mmio_start
+
reg
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: reg[0x%x] = 0x%x
\n
"
,
__func__
,
reg
,
*
val
);
return
0
;
}
/**
* used to update the masked bits in display controller HDMI
* audio registers.
*/
static
int
hdmi_audio_rmw
(
u32
reg
,
u32
val
,
u32
mask
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
u32
val_tmp
=
0
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
val_tmp
=
(
val
&
mask
)
|
((
ioread32
(
ctx
->
mmio_start
+
reg
))
&
~
mask
);
if
(
ctx
->
dp_output
)
{
if
((
reg
==
AUDIO_HDMI_CONFIG_A
)
||
(
reg
==
AUDIO_HDMI_CONFIG_B
)
||
(
reg
==
AUDIO_HDMI_CONFIG_C
))
{
if
(
val_tmp
&
AUD_CONFIG_VALID_BIT
)
val_tmp
=
val_tmp
|
AUD_CONFIG_DP_MODE
|
AUD_CONFIG_BLOCK_BIT
;
}
}
iowrite32
(
val_tmp
,
(
ctx
->
mmio_start
+
reg
));
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: reg[0x%x] = 0x%x
\n
"
,
__func__
,
reg
,
val_tmp
);
return
0
;
}
/**
* used to return the HDMI audio capabilities.
* e.g. resolution, frame rate.
*/
static
int
hdmi_audio_get_caps
(
enum
had_caps_list
get_element
,
void
*
capabilities
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
int
ret
=
0
;
ctx
=
get_hdmi_context
();
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Enter
\n
"
,
__func__
);
switch
(
get_element
)
{
case
HAD_GET_ELD
:
ret
=
hdmi_get_eld
(
capabilities
);
break
;
case
HAD_GET_DISPLAY_RATE
:
/* ToDo: Verify if sampling freq logic is correct */
*
(
u32
*
)
capabilities
=
ctx
->
tmds_clock_speed
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: tmds_clock_speed = 0x%x
\n
"
,
__func__
,
ctx
->
tmds_clock_speed
);
break
;
case
HAD_GET_LINK_RATE
:
/* ToDo: Verify if sampling freq logic is correct */
*
(
u32
*
)
capabilities
=
ctx
->
link_rate
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: link rate = 0x%x
\n
"
,
__func__
,
ctx
->
link_rate
);
break
;
case
HAD_GET_DP_OUTPUT
:
*
(
u32
*
)
capabilities
=
ctx
->
dp_output
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: dp_output = %d
\n
"
,
__func__
,
ctx
->
dp_output
);
break
;
default:
break
;
}
return
ret
;
}
/**
* used to get the current hdmi base address
*/
int
hdmi_audio_get_register_base
(
u32
**
reg_base
,
u32
*
config_offset
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
*
reg_base
=
(
u32
*
)(
ctx
->
mmio_start
);
*
config_offset
=
ctx
->
had_config_offset
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: reg_base = 0x%p, cfg_off = 0x%x
\n
"
,
__func__
,
*
reg_base
,
*
config_offset
);
return
0
;
}
/**
* used to set the HDMI audio capabilities.
* e.g. Audio INT.
*/
int
hdmi_audio_set_caps
(
enum
had_caps_list
set_element
,
void
*
capabilties
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: cap_id = 0x%x
\n
"
,
__func__
,
set_element
);
switch
(
set_element
)
{
case
HAD_SET_ENABLE_AUDIO_INT
:
{
u32
status_reg
;
hdmi_audio_read
(
AUD_HDMI_STATUS_v2
+
ctx
->
had_config_offset
,
&
status_reg
);
status_reg
|=
HDMI_AUDIO_BUFFER_DONE
|
HDMI_AUDIO_UNDERRUN
;
hdmi_audio_write
(
AUD_HDMI_STATUS_v2
+
ctx
->
had_config_offset
,
status_reg
);
hdmi_audio_read
(
AUD_HDMI_STATUS_v2
+
ctx
->
had_config_offset
,
&
status_reg
);
}
break
;
default:
break
;
}
return
0
;
}
static
struct
hdmi_audio_registers_ops
hdmi_audio_reg_ops
=
{
.
hdmi_audio_get_register_base
=
hdmi_audio_get_register_base
,
.
hdmi_audio_read_register
=
hdmi_audio_read
,
.
hdmi_audio_write_register
=
hdmi_audio_write
,
.
hdmi_audio_read_modify
=
hdmi_audio_rmw
,
};
static
struct
hdmi_audio_query_set_ops
hdmi_audio_get_set_ops
=
{
.
hdmi_audio_get_caps
=
hdmi_audio_get_caps
,
.
hdmi_audio_set_caps
=
hdmi_audio_set_caps
,
};
int
mid_hdmi_audio_setup
(
had_event_call_back
audio_callbacks
,
struct
hdmi_audio_registers_ops
*
reg_ops
,
struct
hdmi_audio_query_set_ops
*
query_ops
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: called
\n
"
,
__func__
);
reg_ops
->
hdmi_audio_get_register_base
=
(
hdmi_audio_reg_ops
.
hdmi_audio_get_register_base
);
reg_ops
->
hdmi_audio_read_register
=
(
hdmi_audio_reg_ops
.
hdmi_audio_read_register
);
reg_ops
->
hdmi_audio_write_register
=
(
hdmi_audio_reg_ops
.
hdmi_audio_write_register
);
reg_ops
->
hdmi_audio_read_modify
=
(
hdmi_audio_reg_ops
.
hdmi_audio_read_modify
);
query_ops
->
hdmi_audio_get_caps
=
hdmi_audio_get_set_ops
.
hdmi_audio_get_caps
;
query_ops
->
hdmi_audio_set_caps
=
hdmi_audio_get_set_ops
.
hdmi_audio_set_caps
;
ctx
->
had_event_callbacks
=
audio_callbacks
;
return
0
;
}
static
void
_had_wq
(
struct
work_struct
*
work
)
{
mid_hdmi_audio_signal_event
(
HAD_EVENT_HOT_PLUG
);
}
int
mid_hdmi_audio_register
(
struct
snd_intel_had_interface
*
driver
,
void
*
had_data
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: called
\n
"
,
__func__
);
ctx
->
had_pvt_data
=
had_data
;
ctx
->
had_interface
=
driver
;
/* The Audio driver is loading now and we need to notify
* it if there is an HDMI device attached
*/
INIT_WORK
(
&
ctx
->
hdmi_audio_wq
,
_had_wq
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Scheduling HDMI audio work queue
\n
"
,
__func__
);
schedule_work
(
&
ctx
->
hdmi_audio_wq
);
return
0
;
}
static
irqreturn_t
display_pipe_interrupt_handler
(
int
irq
,
void
*
dev_id
)
{
u32
audio_stat
,
audio_reg
;
struct
hdmi_lpe_audio_ctx
*
ctx
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Enter
\n
"
,
__func__
);
ctx
=
platform_get_drvdata
(
hlpe_pdev
);
audio_reg
=
ctx
->
had_config_offset
+
AUD_HDMI_STATUS_v2
;
hdmi_audio_read
(
audio_reg
,
&
audio_stat
);
if
(
audio_stat
&
HDMI_AUDIO_UNDERRUN
)
{
hdmi_audio_write
(
audio_reg
,
HDMI_AUDIO_UNDERRUN
);
mid_hdmi_audio_signal_event
(
HAD_EVENT_AUDIO_BUFFER_UNDERRUN
);
}
if
(
audio_stat
&
HDMI_AUDIO_BUFFER_DONE
)
{
hdmi_audio_write
(
audio_reg
,
HDMI_AUDIO_BUFFER_DONE
);
mid_hdmi_audio_signal_event
(
HAD_EVENT_AUDIO_BUFFER_DONE
);
}
return
IRQ_HANDLED
;
}
static
void
notify_audio_lpe
(
struct
platform_device
*
pdev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
=
platform_get_drvdata
(
pdev
);
struct
intel_hdmi_lpe_audio_pdata
*
pdata
=
pdev
->
dev
.
platform_data
;
if
(
pdata
->
hdmi_connected
!=
true
)
{
dev_dbg
(
&
pdev
->
dev
,
"%s: Event: HAD_NOTIFY_HOT_UNPLUG
\n
"
,
__func__
);
if
(
hlpe_state
==
hdmi_connector_status_connected
)
{
hlpe_state
=
hdmi_connector_status_disconnected
;
mid_hdmi_audio_signal_event
(
HAD_EVENT_HOT_UNPLUG
);
}
else
dev_dbg
(
&
pdev
->
dev
,
"%s: Already Unplugged!
\n
"
,
__func__
);
}
else
{
struct
intel_hdmi_lpe_audio_eld
*
eld
=
&
pdata
->
eld
;
switch
(
eld
->
pipe_id
)
{
case
0
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_A
;
break
;
case
1
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_B
;
break
;
case
2
:
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_C
;
break
;
default:
dev_dbg
(
&
pdev
->
dev
,
"Invalid pipe %d
\n
"
,
eld
->
pipe_id
);
break
;
}
hdmi_set_eld
(
eld
->
eld_data
);
mid_hdmi_audio_signal_event
(
HAD_EVENT_HOT_PLUG
);
hlpe_state
=
hdmi_connector_status_connected
;
dev_dbg
(
&
pdev
->
dev
,
"%s: HAD_NOTIFY_ELD : port = %d, tmds = %d
\n
"
,
__func__
,
eld
->
port_id
,
pdata
->
tmds_clock_speed
);
if
(
pdata
->
tmds_clock_speed
)
{
ctx
->
tmds_clock_speed
=
pdata
->
tmds_clock_speed
;
ctx
->
dp_output
=
pdata
->
dp_output
;
ctx
->
link_rate
=
pdata
->
link_rate
;
mid_hdmi_audio_signal_event
(
HAD_EVENT_MODE_CHANGING
);
}
}
}
/**
* hdmi_lpe_audio_probe - start bridge with i915
*
* This function is called when the i915 driver creates the
* hdmi-lpe-audio platform device. Card creation is deferred until a
* hot plug event is received
*/
static
int
hdmi_lpe_audio_probe
(
struct
platform_device
*
pdev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
struct
intel_hdmi_lpe_audio_pdata
*
pdata
;
int
irq
;
struct
resource
*
res_mmio
;
void
__iomem
*
mmio_start
;
int
ret
=
0
;
unsigned
long
flag_irq
;
static
const
struct
pci_device_id
cherryview_ids
[]
=
{
{
PCI_DEVICE
(
0x8086
,
0x22b0
)},
{
PCI_DEVICE
(
0x8086
,
0x22b1
)},
{
PCI_DEVICE
(
0x8086
,
0x22b2
)},
{
PCI_DEVICE
(
0x8086
,
0x22b3
)},
{}
};
dev_dbg
(
&
hlpe_pdev
->
dev
,
"Enter %s
\n
"
,
__func__
);
/*TBD:remove globals*/
hlpe_pdev
=
pdev
;
hlpe_state
=
hdmi_connector_status_disconnected
;
/* get resources */
irq
=
platform_get_irq
(
pdev
,
0
);
if
(
irq
<
0
)
{
dev_err
(
&
hlpe_pdev
->
dev
,
"Could not get irq resource
\n
"
);
return
-
ENODEV
;
}
res_mmio
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
if
(
!
res_mmio
)
{
dev_err
(
&
hlpe_pdev
->
dev
,
"Could not get IO_MEM resources
\n
"
);
return
-
ENXIO
;
}
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: mmio_start = 0x%x, mmio_end = 0x%x
\n
"
,
__func__
,
(
unsigned
int
)
res_mmio
->
start
,
(
unsigned
int
)
res_mmio
->
end
);
mmio_start
=
ioremap_nocache
(
res_mmio
->
start
,
(
size_t
)(
resource_size
(
res_mmio
)));
if
(
!
mmio_start
)
{
dev_err
(
&
hlpe_pdev
->
dev
,
"Could not get ioremap
\n
"
);
return
-
EACCES
;
}
/* setup interrupt handler */
ret
=
request_irq
(
irq
,
display_pipe_interrupt_handler
,
0
,
pdev
->
name
,
NULL
);
if
(
ret
<
0
)
{
dev_err
(
&
hlpe_pdev
->
dev
,
"request_irq failed
\n
"
);
iounmap
(
mmio_start
);
return
-
ENODEV
;
}
/* alloc and save context */
ctx
=
kzalloc
(
sizeof
(
*
ctx
),
GFP_KERNEL
);
if
(
ctx
==
NULL
)
{
free_irq
(
irq
,
NULL
);
iounmap
(
mmio_start
);
return
-
ENOMEM
;
}
ctx
->
irq
=
irq
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"hdmi lpe audio: irq num = %d
\n
"
,
irq
);
ctx
->
mmio_start
=
mmio_start
;
ctx
->
tmds_clock_speed
=
DIS_SAMPLE_RATE_148_5
;
if
(
pci_dev_present
(
cherryview_ids
))
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Cherrytrail LPE - Detected
\n
"
,
__func__
);
else
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: Baytrail LPE - Assume
\n
"
,
__func__
);
/* assume pipe A as default */
ctx
->
had_config_offset
=
AUDIO_HDMI_CONFIG_A
;
pdata
=
pdev
->
dev
.
platform_data
;
if
(
pdata
==
NULL
)
{
dev_err
(
&
hlpe_pdev
->
dev
,
"%s: quit: pdata not allocated by i915!!
\n
"
,
__func__
);
kfree
(
ctx
);
free_irq
(
irq
,
NULL
);
iounmap
(
mmio_start
);
return
-
ENOMEM
;
}
platform_set_drvdata
(
pdev
,
ctx
);
ret
=
hdmi_audio_probe
((
void
*
)
pdev
);
dev_dbg
(
&
hlpe_pdev
->
dev
,
"hdmi lpe audio: setting pin eld notify callback
\n
"
);
spin_lock_irqsave
(
&
pdata
->
lpe_audio_slock
,
flag_irq
);
pdata
->
notify_audio_lpe
=
notify_audio_lpe
;
if
(
pdata
->
notify_pending
)
{
dev_dbg
(
&
hlpe_pdev
->
dev
,
"%s: handle pending notification
\n
"
,
__func__
);
notify_audio_lpe
(
pdev
);
pdata
->
notify_pending
=
false
;
}
spin_unlock_irqrestore
(
&
pdata
->
lpe_audio_slock
,
flag_irq
);
return
ret
;
}
/**
* hdmi_lpe_audio_remove - stop bridge with i915
*
* This function is called when the platform device is destroyed. The sound
* card should have been removed on hot plug event.
*/
static
int
hdmi_lpe_audio_remove
(
struct
platform_device
*
pdev
)
{
struct
hdmi_lpe_audio_ctx
*
ctx
;
dev_dbg
(
&
hlpe_pdev
->
dev
,
"Enter %s
\n
"
,
__func__
);
hdmi_audio_remove
(
pdev
);
/* get context, release resources */
ctx
=
platform_get_drvdata
(
pdev
);
iounmap
(
ctx
->
mmio_start
);
free_irq
(
ctx
->
irq
,
NULL
);
kfree
(
ctx
);
return
0
;
}
static
int
hdmi_lpe_audio_suspend
(
struct
platform_device
*
pt_dev
,
pm_message_t
state
)
{
dev_dbg
(
&
hlpe_pdev
->
dev
,
"Enter %s
\n
"
,
__func__
);
mid_hdmi_audio_suspend
(
NULL
);
return
0
;
}
static
int
hdmi_lpe_audio_resume
(
struct
platform_device
*
pt_dev
)
{
dev_dbg
(
&
hlpe_pdev
->
dev
,
"Enter %s
\n
"
,
__func__
);
mid_hdmi_audio_resume
(
NULL
);
return
0
;
}
static
struct
platform_driver
hdmi_lpe_audio_driver
=
{
.
driver
=
{
.
name
=
"hdmi-lpe-audio"
,
},
.
probe
=
hdmi_lpe_audio_probe
,
.
remove
=
hdmi_lpe_audio_remove
,
.
suspend
=
hdmi_lpe_audio_suspend
,
.
resume
=
hdmi_lpe_audio_resume
};
module_platform_driver
(
hdmi_lpe_audio_driver
);
MODULE_LICENSE
(
"GPL v2"
);
MODULE_ALIAS
(
"platform:hdmi_lpe_audio"
);
sound/x86/intel_hdmi_lpe_audio.h
View file @
c1a7c40c
...
...
@@ -23,19 +23,6 @@
#ifndef __INTEL_HDMI_LPE_AUDIO_H
#define __INTEL_HDMI_LPE_AUDIO_H
#include <linux/types.h>
#include <sound/initval.h>
#include <linux/version.h>
#include <linux/pm_runtime.h>
#include <sound/asoundef.h>
#include <sound/control.h>
#include <sound/pcm.h>
#define AUD_CONFIG_VALID_BIT (1<<9)
#define AUD_CONFIG_DP_MODE (1<<15)
#define AUD_CONFIG_BLOCK_BIT (1<<7)
#define HMDI_LPE_AUDIO_DRIVER_NAME "intel-hdmi-lpe-audio"
#define HAD_MAX_DEVICES 1
#define HAD_MIN_CHANNEL 2
#define HAD_MAX_CHANNEL 8
...
...
@@ -95,164 +82,6 @@
/* Naud Value */
#define DP_NAUD_VAL 32768
/* _AUD_CONFIG register MASK */
#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000
#define AUD_CONFIG_MASK_SRDBG 0x00000002
#define AUD_CONFIG_MASK_FUNCRST 0x00000001
#define MAX_CNT 0xFF
#define HAD_SUSPEND_DELAY 1000
#define OTM_HDMI_ELD_SIZE 128
union
otm_hdmi_eld_t
{
unsigned
char
eld_data
[
OTM_HDMI_ELD_SIZE
];
struct
{
/* Byte[0] = ELD Version Number */
union
{
unsigned
char
byte0
;
struct
{
unsigned
char
reserved
:
3
;
/* Reserf */
unsigned
char
eld_ver
:
5
;
/* ELD Version Number */
/* 00000b - reserved
* 00001b - first rev, obsoleted
* 00010b - version 2, supporting CEA version
* 861D or below
* 00011b:11111b - reserved
* for future
*/
};
};
/* Byte[1] = Vendor Version Field */
union
{
unsigned
char
vendor_version
;
struct
{
unsigned
char
reserved1
:
3
;
unsigned
char
veld_ver
:
5
;
/* Version number of the ELD
* extension. This value is
* provisioned and unique to
* each vendor.
*/
};
};
/* Byte[2] = Baseline Length field */
unsigned
char
baseline_eld_length
;
/* Length of the Baseline structure
* divided by Four.
*/
/* Byte [3] = Reserved for future use */
unsigned
char
byte3
;
/* Starting of the BaseLine EELD structure
* Byte[4] = Monitor Name Length
*/
union
{
unsigned
char
byte4
;
struct
{
unsigned
char
mnl
:
5
;
unsigned
char
cea_edid_rev_id
:
3
;
};
};
/* Byte[5] = Capabilities */
union
{
unsigned
char
capabilities
;
struct
{
unsigned
char
hdcp
:
1
;
/* HDCP support */
unsigned
char
ai_support
:
1
;
/* AI support */
unsigned
char
connection_type
:
2
;
/* Connection type
* 00 - HDMI
* 01 - DP
* 10 -11 Reserved
* for future
* connection types
*/
unsigned
char
sadc
:
4
;
/* Indicates number of 3 bytes
* Short Audio Descriptors.
*/
};
};
/* Byte[6] = Audio Synch Delay */
unsigned
char
audio_synch_delay
;
/* Amount of time reported by the
* sink that the video trails audio
* in milliseconds.
*/
/* Byte[7] = Speaker Allocation Block */
union
{
unsigned
char
speaker_allocation_block
;
struct
{
unsigned
char
flr
:
1
;
/*Front Left and Right channels*/
unsigned
char
lfe
:
1
;
/*Low Frequency Effect channel*/
unsigned
char
fc
:
1
;
/*Center transmission channel*/
unsigned
char
rlr
:
1
;
/*Rear Left and Right channels*/
unsigned
char
rc
:
1
;
/*Rear Center channel*/
unsigned
char
flrc
:
1
;
/*Front left and Right of Center
*transmission channels
*/
unsigned
char
rlrc
:
1
;
/*Rear left and Right of Center
*transmission channels
*/
unsigned
char
reserved3
:
1
;
/* Reserved */
};
};
/* Byte[8 - 15] - 8 Byte port identification value */
unsigned
char
port_id_value
[
8
];
/* Byte[16 - 17] - 2 Byte Manufacturer ID */
unsigned
char
manufacturer_id
[
2
];
/* Byte[18 - 19] - 2 Byte Product ID */
unsigned
char
product_id
[
2
];
/* Byte [20-83] - 64 Bytes of BaseLine Data */
unsigned
char
mn_sand_sads
[
64
];
/* This will include
* - ASCII string of Monitor name
* - List of 3 byte SADs
* - Zero padding
*/
/* Vendor ELD Block should continue here!
* No Vendor ELD block defined as of now.
*/
}
__packed
;
};
/**
* enum had_status - Audio stream states
*
* @STREAM_INIT: Stream initialized
* @STREAM_RUNNING: Stream running
* @STREAM_PAUSED: Stream paused
* @STREAM_DROPPED: Stream dropped
*/
enum
had_stream_status
{
STREAM_INIT
=
0
,
STREAM_RUNNING
=
1
,
STREAM_PAUSED
=
2
,
STREAM_DROPPED
=
3
};
/**
* enum had_status_stream - HAD stream states
*/
enum
had_status_stream
{
HAD_INIT
=
0
,
HAD_RUNNING_STREAM
,
};
enum
had_drv_status
{
HAD_DRV_CONNECTED
,
HAD_DRV_RUNNING
,
HAD_DRV_DISCONNECTED
,
HAD_DRV_SUSPENDED
,
HAD_DRV_ERR
,
};
/* enum intel_had_aud_buf_type - HDMI controller ring buffer types */
enum
intel_had_aud_buf_type
{
HAD_BUF_TYPE_A
=
0
,
...
...
@@ -261,22 +90,15 @@ enum intel_had_aud_buf_type {
HAD_BUF_TYPE_D
=
3
,
};
enum
num_aud_ch
{
CH_STEREO
=
0
,
CH_THREE_FOUR
=
1
,
CH_FIVE_SIX
=
2
,
CH_SEVEN_EIGHT
=
3
};
/* HDMI Controller register offsets - audio domain common */
/* Base address for below regs = 0x65000 */
enum
hdmi_ctrl_reg_offset_common
{
AUDIO_HDMI_CONFIG_A
=
0x000
,
AUDIO_HDMI_CONFIG_A
=
0x000
,
AUDIO_HDMI_CONFIG_B
=
0x800
,
AUDIO_HDMI_CONFIG_C
=
0x900
,
};
/* HDMI controller register offsets */
enum
hdmi_ctrl_reg_offset
_v1
{
enum
hdmi_ctrl_reg_offset
{
AUD_CONFIG
=
0x0
,
AUD_CH_STATUS_0
=
0x08
,
AUD_CH_STATUS_1
=
0x0C
,
...
...
@@ -294,18 +116,8 @@ enum hdmi_ctrl_reg_offset_v1 {
AUD_BUF_D_ADDR
=
0x58
,
AUD_BUF_D_LENGTH
=
0x5c
,
AUD_CNTL_ST
=
0x60
,
AUD_HDMI_STATUS
=
0x68
,
AUD_HDMIW_INFOFR
=
0x114
,
};
/*
* Delta changes in HDMI controller register offsets
* compare to v1 version
*/
enum
hdmi_ctrl_reg_offset_v2
{
AUD_HDMI_STATUS_v2
=
0x64
,
AUD_HDMIW_INFOFR_v2
=
0x68
,
AUD_HDMI_STATUS
=
0x64
,
/* v2 */
AUD_HDMIW_INFOFR
=
0x68
,
/* v2 */
};
/*
...
...
@@ -350,27 +162,8 @@ struct channel_map_table {
int
spk_mask
;
/* speaker position bit mask */
};
/**
* union aud_cfg - Audio configuration
*
* @cfg_regx: individual register bits
* @cfg_regval: full register value
*
*/
/* Audio configuration */
union
aud_cfg
{
struct
{
u32
aud_en
:
1
;
u32
layout
:
1
;
u32
fmt
:
2
;
u32
num_ch
:
2
;
u32
rsvd0
:
1
;
u32
set
:
1
;
u32
flat
:
1
;
u32
val_bit
:
1
;
u32
user_bit
:
1
;
u32
underrun
:
1
;
u32
rsvd1
:
20
;
}
cfg_regx
;
struct
{
u32
aud_en
:
1
;
u32
layout
:
1
;
...
...
@@ -386,17 +179,15 @@ union aud_cfg {
u32
bogus_sample
:
1
;
u32
dp_modei
:
1
;
u32
rsvd
:
16
;
}
cfg_regx_v2
;
u32
cfg_
regval
;
}
regx
;
u32
regval
;
};
/**
* union aud_ch_status_0 - Audio Channel Status 0 Attributes
*
* @status_0_regx:individual register bits
* @status_0_regval:full register value
*
*/
#define AUD_CONFIG_BLOCK_BIT (1 << 7)
#define AUD_CONFIG_VALID_BIT (1 << 9)
#define AUD_CONFIG_DP_MODE (1 << 15)
/* Audio Channel Status 0 Attributes */
union
aud_ch_status_0
{
struct
{
u32
ch_status
:
1
;
...
...
@@ -410,99 +201,53 @@ union aud_ch_status_0 {
u32
samp_freq
:
4
;
u32
clk_acc
:
2
;
u32
rsvd
:
2
;
}
status_0_
regx
;
u32
status_0_
regval
;
}
regx
;
u32
regval
;
};
/**
* union aud_ch_status_1 - Audio Channel Status 1 Attributes
*
* @status_1_regx: individual register bits
* @status_1_regval: full register value
*
*/
/* Audio Channel Status 1 Attributes */
union
aud_ch_status_1
{
struct
{
u32
max_wrd_len
:
1
;
u32
wrd_len
:
3
;
u32
rsvd
:
28
;
}
status_1_
regx
;
u32
status_1_
regval
;
}
regx
;
u32
regval
;
};
/**
* union aud_hdmi_cts - CTS register
*
* @cts_regx: individual register bits
* @cts_regval: full register value
*
*/
/* CTS register */
union
aud_hdmi_cts
{
struct
{
u32
cts_val
:
20
;
u32
en_cts_prog
:
1
;
u32
rsvd
:
11
;
}
cts_regx
;
struct
{
u32
cts_val
:
24
;
u32
en_cts_prog
:
1
;
u32
rsvd
:
7
;
}
cts_regx_v2
;
u32
cts_
regval
;
}
regx
;
u32
regval
;
};
/**
* union aud_hdmi_n_enable - N register
*
* @n_regx: individual register bits
* @n_regval: full register value
*
*/
/* N register */
union
aud_hdmi_n_enable
{
struct
{
u32
n_val
:
20
;
u32
en_n_prog
:
1
;
u32
rsvd
:
11
;
}
n_regx
;
struct
{
u32
n_val
:
24
;
u32
en_n_prog
:
1
;
u32
rsvd
:
7
;
}
n_regx_v2
;
u32
n_
regval
;
}
regx
;
u32
regval
;
};
/**
* union aud_buf_config - Audio Buffer configurations
*
* @buf_cfg_regx: individual register bits
* @buf_cfgval: full register value
*
*/
/* Audio Buffer configurations */
union
aud_buf_config
{
struct
{
u32
fifo_width
:
8
;
u32
rsvd0
:
8
;
u32
aud_delay
:
8
;
u32
rsvd1
:
8
;
}
buf_cfg_regx
;
struct
{
u32
audio_fifo_watermark
:
8
;
u32
dma_fifo_watermark
:
3
;
u32
rsvd0
:
5
;
u32
aud_delay
:
8
;
u32
rsvd1
:
8
;
}
buf_cfg_regx_v2
;
u32
buf_cf
gval
;
}
regx
;
u32
re
gval
;
};
/**
* union aud_buf_ch_swap - Audio Sample Swapping offset
*
* @buf_ch_swap_regx: individual register bits
* @buf_ch_swap_val: full register value
*
*/
/* Audio Sample Swapping offset */
union
aud_buf_ch_swap
{
struct
{
u32
first_0
:
3
;
...
...
@@ -514,49 +259,31 @@ union aud_buf_ch_swap {
u32
first_3
:
3
;
u32
second_3
:
3
;
u32
rsvd
:
8
;
}
buf_ch_swap_
regx
;
u32
buf_ch_swap_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_buf_addr - Address for Audio Buffer
*
* @buf_addr_regx: individual register bits
* @buf_addr_val: full register value
*
*/
/* Address for Audio Buffer */
union
aud_buf_addr
{
struct
{
u32
valid
:
1
;
u32
intr_en
:
1
;
u32
rsvd
:
4
;
u32
addr
:
26
;
}
buf_addr_
regx
;
u32
buf_addr_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_buf_len - Length of Audio Buffer
*
* @buf_len_regx: individual register bits
* @buf_len_val: full register value
*
*/
/* Length of Audio Buffer */
union
aud_buf_len
{
struct
{
u32
buf_len
:
20
;
u32
rsvd
:
12
;
}
buf_len_
regx
;
u32
buf_len_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_ctrl_st - Audio Control State Register offset
*
* @ctrl_regx: individual register bits
* @ctrl_val: full register value
*
*/
/* Audio Control State Register offset */
union
aud_ctrl_st
{
struct
{
u32
ram_addr
:
4
;
...
...
@@ -569,34 +296,22 @@ union aud_ctrl_st {
u32
dip_idx
:
3
;
u32
dip_en_sta
:
4
;
u32
rsvd
:
7
;
}
ctrl_
regx
;
u32
ctrl_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_info_frame1 - Audio HDMI Widget Data Island Packet offset
*
* @fr1_regx: individual register bits
* @fr1_val: full register value
*
*/
/* Audio HDMI Widget Data Island Packet offset */
union
aud_info_frame1
{
struct
{
u32
pkt_type
:
8
;
u32
ver_num
:
8
;
u32
len
:
5
;
u32
rsvd
:
11
;
}
fr1_
regx
;
u32
fr1_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_info_frame2 - DIP frame 2
*
* @fr2_regx: individual register bits
* @fr2_val: full register value
*
*/
/* DIP frame 2 */
union
aud_info_frame2
{
struct
{
u32
chksum
:
8
;
...
...
@@ -607,17 +322,11 @@ union aud_info_frame2 {
u32
smpl_freq
:
3
;
u32
rsvd1
:
3
;
u32
format
:
8
;
}
fr2_
regx
;
u32
fr2_
val
;
}
regx
;
u32
reg
val
;
};
/**
* union aud_info_frame3 - DIP frame 3
*
* @fr3_regx: individual register bits
* @fr3_val: full register value
*
*/
/* DIP frame 3 */
union
aud_info_frame3
{
struct
{
u32
chnl_alloc
:
8
;
...
...
@@ -625,88 +334,17 @@ union aud_info_frame3 {
u32
lsv
:
4
;
u32
dm_inh
:
1
;
u32
rsvd1
:
16
;
}
fr3_regx
;
u32
fr3_val
;
};
enum
hdmi_connector_status
{
hdmi_connector_status_connected
=
1
,
hdmi_connector_status_disconnected
=
2
,
hdmi_connector_status_unknown
=
3
,
};
#define HDMI_AUDIO_UNDERRUN (1UL<<31)
#define HDMI_AUDIO_BUFFER_DONE (1UL<<29)
#define PORT_ENABLE (1 << 31)
#define SDVO_AUDIO_ENABLE (1 << 6)
enum
had_caps_list
{
HAD_GET_ELD
=
1
,
HAD_GET_DISPLAY_RATE
,
HAD_GET_DP_OUTPUT
,
HAD_GET_LINK_RATE
,
HAD_SET_ENABLE_AUDIO
,
HAD_SET_DISABLE_AUDIO
,
HAD_SET_ENABLE_AUDIO_INT
,
HAD_SET_DISABLE_AUDIO_INT
,
};
enum
had_event_type
{
HAD_EVENT_HOT_PLUG
=
1
,
HAD_EVENT_HOT_UNPLUG
,
HAD_EVENT_MODE_CHANGING
,
HAD_EVENT_AUDIO_BUFFER_DONE
,
HAD_EVENT_AUDIO_BUFFER_UNDERRUN
,
HAD_EVENT_QUERY_IS_AUDIO_BUSY
,
HAD_EVENT_QUERY_IS_AUDIO_SUSPENDED
,
};
/*
* HDMI Display Controller Audio Interface
*
*/
typedef
int
(
*
had_event_call_back
)
(
enum
had_event_type
event_type
,
void
*
ctxt_info
);
struct
hdmi_audio_registers_ops
{
int
(
*
hdmi_audio_get_register_base
)(
u32
**
reg_base
,
u32
*
config_offset
);
int
(
*
hdmi_audio_read_register
)(
u32
reg_addr
,
u32
*
data
);
int
(
*
hdmi_audio_write_register
)(
u32
reg_addr
,
u32
data
);
int
(
*
hdmi_audio_read_modify
)(
u32
reg_addr
,
u32
data
,
u32
mask
);
}
regx
;
u32
regval
;
};
struct
hdmi_audio_query_set_ops
{
int
(
*
hdmi_audio_get_caps
)(
enum
had_caps_list
query_element
,
void
*
capabilties
);
int
(
*
hdmi_audio_set_caps
)(
enum
had_caps_list
set_element
,
void
*
capabilties
);
};
/* AUD_HDMI_STATUS bits */
#define HDMI_AUDIO_UNDERRUN (1U << 31)
#define HDMI_AUDIO_BUFFER_DONE (1U << 29)
struct
hdmi_audio_event
{
int
type
;
};
struct
snd_intel_had_interface
{
const
char
*
name
;
int
(
*
query
)(
void
*
had_data
,
struct
hdmi_audio_event
event
);
int
(
*
suspend
)(
void
*
had_data
,
struct
hdmi_audio_event
event
);
int
(
*
resume
)(
void
*
had_data
);
};
bool
mid_hdmi_audio_is_busy
(
void
*
dev
);
bool
mid_hdmi_audio_suspend
(
void
*
dev
);
void
mid_hdmi_audio_resume
(
void
*
dev
);
void
mid_hdmi_audio_signal_event
(
enum
had_event_type
event
);
int
mid_hdmi_audio_setup
(
had_event_call_back
audio_callbacks
,
struct
hdmi_audio_registers_ops
*
reg_ops
,
struct
hdmi_audio_query_set_ops
*
query_ops
);
int
mid_hdmi_audio_register
(
struct
snd_intel_had_interface
*
driver
,
void
*
had_data
);
/* AUD_HDMI_STATUS register mask */
#define AUD_CONFIG_MASK_UNDERRUN 0xC0000000
#define AUD_CONFIG_MASK_SRDBG 0x00000002
#define AUD_CONFIG_MASK_FUNCRST 0x00000001
#endif
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