mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-01-30 14:52:17 +00:00
GPU optimizations
Clock, encoder and decoder got fixes. Now the GPU can handle voltage spikes better for Russcade.
This commit is contained in:
parent
54b7936165
commit
9fb43c2ca9
@ -359,7 +359,7 @@ hdmi0: hdmi@7ef00700 {
|
||||
interrupt-names = "cec-tx", "cec-rx", "cec-low",
|
||||
"wakeup", "hpd-connected", "hpd-removed";
|
||||
ddc = <&ddc0>;
|
||||
dmas = <&dma 10>;
|
||||
dmas = <&dma (10 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
|
||||
dma-names = "audio-rx";
|
||||
status = "disabled";
|
||||
};
|
||||
@ -406,7 +406,7 @@ hdmi1: hdmi@7ef05700 {
|
||||
<9>, <10>, <11>;
|
||||
interrupt-names = "cec-tx", "cec-rx", "cec-low",
|
||||
"wakeup", "hpd-connected", "hpd-removed";
|
||||
dmas = <&dma 17>;
|
||||
dmas = <&dma (17 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
|
||||
dma-names = "audio-rx";
|
||||
status = "disabled";
|
||||
};
|
||||
|
@ -126,7 +126,7 @@ boardid: gpio@3a {
|
||||
compatible = "nxp,pca8574";
|
||||
reg = <0x3a>;
|
||||
gpio-controller;
|
||||
#gpio-cells = <1>;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1318,6 +1318,7 @@ CONFIG_R8188EU=m
|
||||
CONFIG_VT6656=m
|
||||
CONFIG_STAGING_MEDIA=y
|
||||
CONFIG_VIDEO_RPIVID=m
|
||||
CONFIG_ASHMEM=y
|
||||
CONFIG_FB_TFT=m
|
||||
CONFIG_FB_TFT_AGM1264K_FL=m
|
||||
CONFIG_FB_TFT_BD663474=m
|
||||
@ -1384,6 +1385,9 @@ CONFIG_MAX31856=m
|
||||
CONFIG_PWM_BCM2835=m
|
||||
CONFIG_PWM_PCA9685=m
|
||||
CONFIG_RPI_AXIPERF=m
|
||||
CONFIG_ANDROID=y
|
||||
CONFIG_ANDROID_BINDER_IPC=y
|
||||
CONFIG_ANDROID_BINDERFS=y
|
||||
CONFIG_NVMEM_RMEM=m
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
|
@ -1175,6 +1175,7 @@ CONFIG_R8712U=m
|
||||
CONFIG_R8188EU=m
|
||||
CONFIG_VT6656=m
|
||||
CONFIG_STAGING_MEDIA=y
|
||||
CONFIG_ASHMEM=y
|
||||
CONFIG_FB_TFT=m
|
||||
CONFIG_FB_TFT_AGM1264K_FL=m
|
||||
CONFIG_FB_TFT_BD663474=m
|
||||
@ -1234,6 +1235,9 @@ CONFIG_MAXIM_THERMOCOUPLE=m
|
||||
CONFIG_MAX31856=m
|
||||
CONFIG_PWM_BCM2835=m
|
||||
CONFIG_PWM_PCA9685=m
|
||||
CONFIG_ANDROID=y
|
||||
CONFIG_ANDROID_BINDER_IPC=y
|
||||
CONFIG_ANDROID_BINDERFS=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||
CONFIG_EXT4_FS_SECURITY=y
|
||||
|
@ -279,27 +279,15 @@ static u32 vc4_crtc_get_fifo_full_level_bits(struct vc4_crtc *vc4_crtc,
|
||||
* allows drivers to push pixels to more than one encoder from the
|
||||
* same CRTC.
|
||||
*/
|
||||
static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state,
|
||||
struct drm_connector_state *(*get_state)(struct drm_atomic_state *state,
|
||||
struct drm_connector *connector))
|
||||
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
drm_connector_list_iter_begin(crtc->dev, &conn_iter);
|
||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
||||
struct drm_connector_state *conn_state = get_state(state, connector);
|
||||
WARN_ON(hweight32(state->encoder_mask) > 1);
|
||||
|
||||
if (!conn_state)
|
||||
continue;
|
||||
|
||||
if (conn_state->crtc == crtc) {
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
return connector->encoder;
|
||||
}
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask)
|
||||
return encoder;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -313,12 +301,11 @@ static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc)
|
||||
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
|
||||
}
|
||||
|
||||
static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_atomic_state *state)
|
||||
static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encoder,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
|
||||
drm_atomic_get_new_connector_state);
|
||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
|
||||
@ -535,10 +522,12 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
|
||||
crtc);
|
||||
struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
|
||||
drm_atomic_get_old_connector_state);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, old_state);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
|
||||
drm_dbg(dev, "Disabling CRTC %s (%u) connected to Encoder %s (%u)",
|
||||
crtc->name, crtc->base.id, encoder->name, encoder->base.id);
|
||||
|
||||
require_hvs_enabled(dev);
|
||||
|
||||
/* Disable vblank irq handling before crtc is disabled. */
|
||||
@ -563,12 +552,16 @@ static void vc4_crtc_atomic_disable(struct drm_crtc *crtc,
|
||||
static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_crtc_state *new_state = drm_atomic_get_new_crtc_state(state,
|
||||
crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
|
||||
drm_atomic_get_new_connector_state);
|
||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state);
|
||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||
|
||||
drm_dbg(dev, "Enabling CRTC %s (%u) connected to Encoder %s (%u)",
|
||||
crtc->name, crtc->base.id, encoder->name, encoder->base.id);
|
||||
|
||||
require_hvs_enabled(dev);
|
||||
|
||||
/* Enable vblank irq handling before crtc is started otherwise
|
||||
@ -581,7 +574,7 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||
if (vc4_encoder->pre_crtc_configure)
|
||||
vc4_encoder->pre_crtc_configure(encoder, state);
|
||||
|
||||
vc4_crtc_config_pv(crtc, state);
|
||||
vc4_crtc_config_pv(crtc, encoder, state);
|
||||
|
||||
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_EN);
|
||||
|
||||
@ -650,12 +643,27 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
|
||||
struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
|
||||
struct drm_connector *conn;
|
||||
struct drm_connector_state *conn_state;
|
||||
struct drm_encoder *encoder;
|
||||
int ret, i;
|
||||
|
||||
ret = vc4_hvs_atomic_check(crtc, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
encoder = vc4_get_crtc_encoder(crtc, crtc_state);
|
||||
if (encoder) {
|
||||
const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
|
||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||
|
||||
mode = &crtc_state->adjusted_mode;
|
||||
if (vc4_encoder->type == VC4_ENCODER_TYPE_HDMI0) {
|
||||
vc4_state->hvs_load = max(mode->clock * mode->hdisplay / mode->htotal + 1000,
|
||||
mode->clock * 9 / 10) * 1000;
|
||||
} else {
|
||||
vc4_state->hvs_load = mode->clock * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
for_each_new_connector_in_state(state, conn, conn_state,
|
||||
i) {
|
||||
if (conn_state->crtc != crtc)
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <linux/circ_buf.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "vc4_drv.h"
|
||||
#include "vc4_regs.h"
|
||||
@ -26,8 +27,10 @@ vc4_debugfs_init(struct drm_minor *minor)
|
||||
struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
|
||||
struct vc4_debugfs_info_entry *entry;
|
||||
|
||||
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
|
||||
minor->debugfs_root, &vc4->load_tracker_enabled);
|
||||
if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
|
||||
"brcm,bcm2711-vc5"))
|
||||
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
|
||||
minor->debugfs_root, &vc4->load_tracker_enabled);
|
||||
|
||||
list_for_each_entry(entry, &vc4->debugfs_list, link) {
|
||||
drm_debugfs_create_files(&entry->info, 1,
|
||||
|
@ -205,9 +205,6 @@ struct vc4_dev {
|
||||
|
||||
int power_refcount;
|
||||
|
||||
/* Set to true when the load tracker is supported. */
|
||||
bool load_tracker_available;
|
||||
|
||||
/* Set to true when the load tracker is active. */
|
||||
bool load_tracker_enabled;
|
||||
|
||||
@ -327,6 +324,7 @@ struct vc4_hvs {
|
||||
u32 __iomem *dlist;
|
||||
|
||||
struct clk *core_clk;
|
||||
struct clk_request *core_req;
|
||||
|
||||
/* Memory manager for CRTCs to allocate space in the display
|
||||
* list. Units are dwords.
|
||||
@ -520,6 +518,9 @@ vc4_crtc_to_vc4_pv_data(const struct vc4_crtc *crtc)
|
||||
return container_of(data, struct vc4_pv_data, base);
|
||||
}
|
||||
|
||||
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
||||
struct drm_crtc_state *state);
|
||||
|
||||
struct vc4_crtc_state {
|
||||
struct drm_crtc_state base;
|
||||
/* Dlist area for this CRTC configuration. */
|
||||
@ -535,6 +536,8 @@ struct vc4_crtc_state {
|
||||
unsigned int bottom;
|
||||
} margins;
|
||||
|
||||
unsigned long hvs_load;
|
||||
|
||||
/* Transitional state below, only valid during atomic commits */
|
||||
bool update_muxing;
|
||||
};
|
||||
|
@ -39,9 +39,11 @@ static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv)
|
||||
|
||||
struct vc4_hvs_state {
|
||||
struct drm_private_state base;
|
||||
unsigned long core_clock_rate;
|
||||
|
||||
struct {
|
||||
unsigned in_use: 1;
|
||||
unsigned long fifo_load;
|
||||
struct drm_crtc_commit *pending_commit;
|
||||
} fifo_state[HVS_NUM_CHANNELS];
|
||||
};
|
||||
@ -342,11 +344,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
struct vc4_hvs *hvs = vc4->hvs;
|
||||
struct drm_crtc_state *old_crtc_state;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct vc4_hvs_state *new_hvs_state;
|
||||
struct drm_crtc *crtc;
|
||||
struct vc4_hvs_state *old_hvs_state;
|
||||
struct clk_request *core_req;
|
||||
int i;
|
||||
|
||||
old_hvs_state = vc4_hvs_get_old_global_state(state);
|
||||
if (WARN_ON(!old_hvs_state))
|
||||
return;
|
||||
|
||||
new_hvs_state = vc4_hvs_get_new_global_state(state);
|
||||
if (WARN_ON(!new_hvs_state))
|
||||
return;
|
||||
|
||||
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||
struct vc4_crtc_state *vc4_crtc_state;
|
||||
|
||||
@ -357,12 +368,19 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel);
|
||||
}
|
||||
|
||||
if (vc4->hvs && vc4->hvs->hvs5)
|
||||
if (vc4->hvs && vc4->hvs->hvs5) {
|
||||
/*
|
||||
* Do a temporary request on the core clock during the
|
||||
* modeset.
|
||||
*/
|
||||
core_req = clk_request_start(hvs->core_clk, 500000000);
|
||||
|
||||
old_hvs_state = vc4_hvs_get_old_global_state(state);
|
||||
if (!old_hvs_state)
|
||||
return;
|
||||
/*
|
||||
* And remove the previous one based on the HVS
|
||||
* requirements if any.
|
||||
*/
|
||||
clk_request_done(hvs->core_req);
|
||||
}
|
||||
|
||||
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
|
||||
struct vc4_crtc_state *vc4_crtc_state =
|
||||
@ -413,8 +431,20 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
|
||||
|
||||
drm_atomic_helper_cleanup_planes(dev, state);
|
||||
|
||||
if (vc4->hvs && vc4->hvs->hvs5)
|
||||
if (vc4->hvs && vc4->hvs->hvs5) {
|
||||
drm_dbg(dev, "Running the core clock at %lu Hz\n",
|
||||
new_hvs_state->core_clock_rate);
|
||||
|
||||
/*
|
||||
* Request a clock rate based on the current HVS
|
||||
* requirements.
|
||||
*/
|
||||
hvs->core_req = clk_request_start(hvs->core_clk,
|
||||
new_hvs_state->core_clock_rate);
|
||||
|
||||
/* And drop the temporary request */
|
||||
clk_request_done(core_req);
|
||||
}
|
||||
}
|
||||
|
||||
static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
|
||||
@ -576,9 +606,6 @@ static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state)
|
||||
struct drm_plane *plane;
|
||||
int i;
|
||||
|
||||
if (!vc4->load_tracker_available)
|
||||
return 0;
|
||||
|
||||
priv_state = drm_atomic_get_private_obj_state(state,
|
||||
&vc4->load_tracker);
|
||||
if (IS_ERR(priv_state))
|
||||
@ -653,9 +680,6 @@ static void vc4_load_tracker_obj_fini(struct drm_device *dev, void *unused)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||
|
||||
if (!vc4->load_tracker_available)
|
||||
return;
|
||||
|
||||
drm_atomic_private_obj_fini(&vc4->load_tracker);
|
||||
}
|
||||
|
||||
@ -663,9 +687,6 @@ static int vc4_load_tracker_obj_init(struct vc4_dev *vc4)
|
||||
{
|
||||
struct vc4_load_tracker_state *load_state;
|
||||
|
||||
if (!vc4->load_tracker_available)
|
||||
return 0;
|
||||
|
||||
load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
|
||||
if (!load_state)
|
||||
return -ENOMEM;
|
||||
@ -690,9 +711,9 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
|
||||
|
||||
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
|
||||
|
||||
|
||||
for (i = 0; i < HVS_NUM_CHANNELS; i++) {
|
||||
state->fifo_state[i].in_use = old_state->fifo_state[i].in_use;
|
||||
state->fifo_state[i].fifo_load = old_state->fifo_state[i].fifo_load;
|
||||
|
||||
if (!old_state->fifo_state[i].pending_commit)
|
||||
continue;
|
||||
@ -701,6 +722,9 @@ vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj)
|
||||
drm_crtc_commit_get(old_state->fifo_state[i].pending_commit);
|
||||
}
|
||||
|
||||
state->core_clock_rate = old_state->core_clock_rate;
|
||||
|
||||
|
||||
return &state->base;
|
||||
}
|
||||
|
||||
@ -859,6 +883,76 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
vc4_core_clock_atomic_check(struct drm_atomic_state *state)
|
||||
{
|
||||
struct vc4_dev *vc4 = to_vc4_dev(state->dev);
|
||||
struct drm_private_state *priv_state;
|
||||
struct vc4_hvs_state *hvs_new_state;
|
||||
struct vc4_load_tracker_state *load_state;
|
||||
struct drm_crtc_state *old_crtc_state, *new_crtc_state;
|
||||
struct drm_crtc *crtc;
|
||||
unsigned int num_outputs;
|
||||
unsigned long pixel_rate;
|
||||
unsigned long cob_rate;
|
||||
unsigned int i;
|
||||
|
||||
priv_state = drm_atomic_get_private_obj_state(state,
|
||||
&vc4->load_tracker);
|
||||
if (IS_ERR(priv_state))
|
||||
return PTR_ERR(priv_state);
|
||||
|
||||
load_state = to_vc4_load_tracker_state(priv_state);
|
||||
|
||||
hvs_new_state = vc4_hvs_get_global_state(state);
|
||||
if (!hvs_new_state)
|
||||
return -EINVAL;
|
||||
|
||||
for_each_oldnew_crtc_in_state(state, crtc,
|
||||
old_crtc_state,
|
||||
new_crtc_state,
|
||||
i) {
|
||||
if (old_crtc_state->active) {
|
||||
struct vc4_crtc_state *old_vc4_state =
|
||||
to_vc4_crtc_state(old_crtc_state);
|
||||
unsigned int channel = old_vc4_state->assigned_channel;
|
||||
|
||||
hvs_new_state->fifo_state[channel].fifo_load = 0;
|
||||
}
|
||||
|
||||
if (new_crtc_state->active) {
|
||||
struct vc4_crtc_state *new_vc4_state =
|
||||
to_vc4_crtc_state(new_crtc_state);
|
||||
unsigned int channel = new_vc4_state->assigned_channel;
|
||||
|
||||
hvs_new_state->fifo_state[channel].fifo_load =
|
||||
new_vc4_state->hvs_load;
|
||||
}
|
||||
}
|
||||
|
||||
cob_rate = 0;
|
||||
num_outputs = 0;
|
||||
for (i = 0; i < HVS_NUM_CHANNELS; i++) {
|
||||
if (!hvs_new_state->fifo_state[i].in_use)
|
||||
continue;
|
||||
|
||||
num_outputs++;
|
||||
cob_rate += hvs_new_state->fifo_state[i].fifo_load;
|
||||
}
|
||||
|
||||
pixel_rate = load_state->hvs_load;
|
||||
if (num_outputs > 1) {
|
||||
pixel_rate = (pixel_rate * 40) / 100;
|
||||
} else {
|
||||
pixel_rate = (pixel_rate * 60) / 100;
|
||||
}
|
||||
|
||||
hvs_new_state->core_clock_rate = max(cob_rate, pixel_rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
|
||||
{
|
||||
@ -876,7 +970,11 @@ vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vc4_load_tracker_atomic_check(state);
|
||||
ret = vc4_load_tracker_atomic_check(state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return vc4_core_clock_atomic_check(state);
|
||||
}
|
||||
|
||||
static struct drm_mode_config_helper_funcs vc4_mode_config_helpers = {
|
||||
@ -897,9 +995,12 @@ int vc4_kms_load(struct drm_device *dev)
|
||||
"brcm,bcm2711-vc5");
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The limits enforced by the load tracker aren't relevant for
|
||||
* the BCM2711, but the load tracker computations are used for
|
||||
* the core clock rate calculation.
|
||||
*/
|
||||
if (!is_vc5) {
|
||||
vc4->load_tracker_available = true;
|
||||
|
||||
/* Start with the load tracker enabled. Can be
|
||||
* disabled through the debugfs load_tracker file.
|
||||
*/
|
||||
|
@ -534,9 +534,6 @@ static void vc4_plane_calc_load(struct drm_plane_state *state)
|
||||
struct vc4_dev *vc4;
|
||||
|
||||
vc4 = to_vc4_dev(state->plane->dev);
|
||||
if (!vc4->load_tracker_available)
|
||||
return;
|
||||
|
||||
vc4_state = to_vc4_plane_state(state);
|
||||
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
|
||||
state->crtc);
|
||||
|
@ -65,12 +65,22 @@ struct hdmi_codec_ops {
|
||||
|
||||
/*
|
||||
* Configures HDMI-encoder for audio stream.
|
||||
* Mandatory
|
||||
* Having either prepare or hw_params is mandatory.
|
||||
*/
|
||||
int (*hw_params)(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms);
|
||||
|
||||
/*
|
||||
* Configures HDMI-encoder for audio stream. Can be called
|
||||
* multiple times for each setup.
|
||||
*
|
||||
* Having either prepare or hw_params is mandatory.
|
||||
*/
|
||||
int (*prepare)(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms);
|
||||
|
||||
/*
|
||||
* Shuts down the audio stream.
|
||||
* Mandatory
|
||||
|
@ -4,6 +4,14 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len);
|
||||
|
||||
int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len);
|
||||
|
||||
int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len);
|
||||
|
||||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len);
|
||||
|
||||
|
@ -1,34 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* Header file for iptables ipt_ECN target
|
||||
*
|
||||
* (C) 2002 by Harald Welte <laforge@gnumonks.org>
|
||||
*
|
||||
* This software is distributed under GNU GPL v2, 1991
|
||||
*
|
||||
* ipt_ECN.h,v 1.3 2002/05/29 12:17:40 laforge Exp
|
||||
*/
|
||||
#ifndef _IPT_ECN_TARGET_H
|
||||
#define _IPT_ECN_TARGET_H
|
||||
#ifndef _IPT_ECN_H
|
||||
#define _IPT_ECN_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/netfilter/xt_DSCP.h>
|
||||
#include <linux/netfilter/xt_ecn.h>
|
||||
#define ipt_ecn_info xt_ecn_info
|
||||
|
||||
#define IPT_ECN_IP_MASK (~XT_DSCP_MASK)
|
||||
|
||||
#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */
|
||||
#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */
|
||||
#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */
|
||||
|
||||
#define IPT_ECN_OP_MASK 0xce
|
||||
|
||||
struct ipt_ECN_info {
|
||||
__u8 operation; /* bitset of operations */
|
||||
__u8 ip_ect; /* ECT codepoint of IPv4 header, pre-shifted */
|
||||
union {
|
||||
struct {
|
||||
__u8 ece:1, cwr:1; /* TCP ECT bits */
|
||||
} tcp;
|
||||
} proto;
|
||||
enum {
|
||||
IPT_ECN_IP_MASK = XT_ECN_IP_MASK,
|
||||
IPT_ECN_OP_MATCH_IP = XT_ECN_OP_MATCH_IP,
|
||||
IPT_ECN_OP_MATCH_ECE = XT_ECN_OP_MATCH_ECE,
|
||||
IPT_ECN_OP_MATCH_CWR = XT_ECN_OP_MATCH_CWR,
|
||||
IPT_ECN_OP_MATCH_MASK = XT_ECN_OP_MATCH_MASK,
|
||||
};
|
||||
|
||||
#endif /* _IPT_ECN_TARGET_H */
|
||||
#endif /* IPT_ECN_H */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/* IP tables module for matching the value of the TTL
|
||||
* (C) 2000 by Harald Welte <laforge@gnumonks.org> */
|
||||
/* TTL modification module for IP tables
|
||||
* (C) 2000 by Harald Welte <laforge@netfilter.org> */
|
||||
|
||||
#ifndef _IPT_TTL_H
|
||||
#define _IPT_TTL_H
|
||||
@ -8,14 +8,14 @@
|
||||
#include <linux/types.h>
|
||||
|
||||
enum {
|
||||
IPT_TTL_EQ = 0, /* equals */
|
||||
IPT_TTL_NE, /* not equals */
|
||||
IPT_TTL_LT, /* less than */
|
||||
IPT_TTL_GT, /* greater than */
|
||||
IPT_TTL_SET = 0,
|
||||
IPT_TTL_INC,
|
||||
IPT_TTL_DEC
|
||||
};
|
||||
|
||||
#define IPT_TTL_MAXMODE IPT_TTL_DEC
|
||||
|
||||
struct ipt_ttl_info {
|
||||
struct ipt_TTL_info {
|
||||
__u8 mode;
|
||||
__u8 ttl;
|
||||
};
|
||||
|
@ -9,41 +9,68 @@
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/pcm_iec958.h>
|
||||
|
||||
static int create_iec958_consumer(uint rate, uint sample_width,
|
||||
u8 *cs, size_t len)
|
||||
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
|
||||
{
|
||||
unsigned int fs, ws;
|
||||
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
fs = IEC958_AES3_CON_FS_32000;
|
||||
break;
|
||||
case 44100:
|
||||
fs = IEC958_AES3_CON_FS_44100;
|
||||
break;
|
||||
case 48000:
|
||||
fs = IEC958_AES3_CON_FS_48000;
|
||||
break;
|
||||
case 88200:
|
||||
fs = IEC958_AES3_CON_FS_88200;
|
||||
break;
|
||||
case 96000:
|
||||
fs = IEC958_AES3_CON_FS_96000;
|
||||
break;
|
||||
case 176400:
|
||||
fs = IEC958_AES3_CON_FS_176400;
|
||||
break;
|
||||
case 192000:
|
||||
fs = IEC958_AES3_CON_FS_192000;
|
||||
break;
|
||||
default:
|
||||
memset(cs, 0, len);
|
||||
|
||||
cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
|
||||
cs[1] = IEC958_AES1_CON_GENERAL;
|
||||
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
|
||||
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | IEC958_AES3_CON_FS_NOTID;
|
||||
|
||||
if (len > 4)
|
||||
cs[4] = IEC958_AES4_CON_WORDLEN_NOTID;
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_default);
|
||||
|
||||
static int fill_iec958_consumer(uint rate, uint sample_width,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
if (len < 4)
|
||||
return -EINVAL;
|
||||
|
||||
if ((cs[3] & IEC958_AES3_CON_FS) == IEC958_AES3_CON_FS_NOTID) {
|
||||
unsigned int fs;
|
||||
|
||||
switch (rate) {
|
||||
case 32000:
|
||||
fs = IEC958_AES3_CON_FS_32000;
|
||||
break;
|
||||
case 44100:
|
||||
fs = IEC958_AES3_CON_FS_44100;
|
||||
break;
|
||||
case 48000:
|
||||
fs = IEC958_AES3_CON_FS_48000;
|
||||
break;
|
||||
case 88200:
|
||||
fs = IEC958_AES3_CON_FS_88200;
|
||||
break;
|
||||
case 96000:
|
||||
fs = IEC958_AES3_CON_FS_96000;
|
||||
break;
|
||||
case 176400:
|
||||
fs = IEC958_AES3_CON_FS_176400;
|
||||
break;
|
||||
case 192000:
|
||||
fs = IEC958_AES3_CON_FS_192000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs[3] &= ~IEC958_AES3_CON_FS;
|
||||
cs[3] |= fs;
|
||||
}
|
||||
|
||||
if (len > 4) {
|
||||
if (len > 4 &&
|
||||
(cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
|
||||
unsigned int ws;
|
||||
|
||||
switch (sample_width) {
|
||||
case 16:
|
||||
ws = IEC958_AES4_CON_WORDLEN_20_16;
|
||||
@ -64,21 +91,30 @@ static int create_iec958_consumer(uint rate, uint sample_width,
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cs[4] &= ~IEC958_AES4_CON_WORDLEN;
|
||||
cs[4] |= ws;
|
||||
}
|
||||
|
||||
memset(cs, 0, len);
|
||||
|
||||
cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
|
||||
cs[1] = IEC958_AES1_CON_GENERAL;
|
||||
cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC | IEC958_AES2_CON_CHANNEL_UNSPEC;
|
||||
cs[3] = IEC958_AES3_CON_CLOCK_1000PPM | fs;
|
||||
|
||||
if (len > 4)
|
||||
cs[4] = ws;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
int snd_pcm_fill_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer_hw_params);
|
||||
|
||||
int snd_pcm_fill_iec958_consumer(struct snd_pcm_runtime *runtime,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
return fill_iec958_consumer(runtime->rate,
|
||||
snd_pcm_format_width(runtime->format),
|
||||
cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_fill_iec958_consumer);
|
||||
|
||||
/**
|
||||
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
|
||||
* @runtime: pcm runtime structure with ->rate filled in
|
||||
@ -95,9 +131,13 @@ static int create_iec958_consumer(uint rate, uint sample_width,
|
||||
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||
size_t len)
|
||||
{
|
||||
return create_iec958_consumer(runtime->rate,
|
||||
snd_pcm_format_width(runtime->format),
|
||||
cs, len);
|
||||
int ret;
|
||||
|
||||
ret = snd_pcm_create_iec958_consumer_default(cs, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snd_pcm_fill_iec958_consumer(runtime, cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
|
||||
|
||||
@ -117,7 +157,12 @@ EXPORT_SYMBOL(snd_pcm_create_iec958_consumer);
|
||||
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||
u8 *cs, size_t len)
|
||||
{
|
||||
return create_iec958_consumer(params_rate(params), params_width(params),
|
||||
cs, len);
|
||||
int ret;
|
||||
|
||||
ret = snd_pcm_create_iec958_consumer_default(cs, len);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return fill_iec958_consumer(params_rate(params), params_width(params), cs, len);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
|
||||
|
@ -278,6 +278,7 @@ struct hdmi_codec_priv {
|
||||
bool busy;
|
||||
struct snd_soc_jack *jack;
|
||||
unsigned int jack_status;
|
||||
u8 iec_status[5];
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
||||
@ -386,6 +387,47 @@ static int hdmi_codec_chmap_ctl_get(struct snd_kcontrol *kcontrol,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_iec958_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
||||
uinfo->count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_iec958_default_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
|
||||
memcpy(ucontrol->value.iec958.status, hcp->iec_status,
|
||||
sizeof(hcp->iec_status));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_iec958_default_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
||||
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
||||
|
||||
memcpy(hcp->iec_status, ucontrol->value.iec958.status,
|
||||
sizeof(hcp->iec_status));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_iec958_mask_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
memset(ucontrol->value.iec958.status, 0xff,
|
||||
sizeof_field(struct hdmi_codec_priv, iec_status));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
@ -440,6 +482,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
||||
mutex_unlock(&hcp->lock);
|
||||
}
|
||||
|
||||
static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai,
|
||||
unsigned int sample_width,
|
||||
unsigned int sample_rate,
|
||||
unsigned int channels,
|
||||
struct hdmi_codec_params *hp)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
int idx;
|
||||
|
||||
/* Select a channel allocation that matches with ELD and pcm channels */
|
||||
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
|
||||
if (idx < 0) {
|
||||
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
|
||||
idx);
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
return idx;
|
||||
}
|
||||
|
||||
memset(hp, 0, sizeof(*hp));
|
||||
|
||||
hdmi_audio_infoframe_init(&hp->cea);
|
||||
hp->cea.channels = channels;
|
||||
hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
||||
hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
|
||||
hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
|
||||
hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
|
||||
hp->sample_width = sample_width;
|
||||
hp->sample_rate = sample_rate;
|
||||
hp->channels = channels;
|
||||
|
||||
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
@ -454,46 +532,73 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||
.dig_subframe = { 0 },
|
||||
}
|
||||
};
|
||||
int ret, idx;
|
||||
int ret;
|
||||
|
||||
if (!hcp->hcd.ops->hw_params)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
||||
params_width(params), params_rate(params),
|
||||
params_channels(params));
|
||||
|
||||
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
|
||||
sizeof(hp.iec.status));
|
||||
ret = hdmi_codec_fill_codec_params(dai,
|
||||
params_width(params),
|
||||
params_rate(params),
|
||||
params_channels(params),
|
||||
&hp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
|
||||
ret = snd_pcm_fill_iec958_consumer_hw_params(params, hp.iec.status,
|
||||
sizeof(hp.iec.status));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hdmi_audio_infoframe_init(&hp.cea);
|
||||
hp.cea.channels = params_channels(params);
|
||||
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
||||
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
|
||||
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
|
||||
|
||||
/* Select a channel allocation that matches with ELD and pcm channels */
|
||||
idx = hdmi_codec_get_ch_alloc_table_idx(hcp, hp.cea.channels);
|
||||
if (idx < 0) {
|
||||
dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
|
||||
idx);
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
return idx;
|
||||
}
|
||||
hp.cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
|
||||
|
||||
hp.sample_width = params_width(params);
|
||||
hp.sample_rate = params_rate(params);
|
||||
hp.channels = params_channels(params);
|
||||
|
||||
cf->bit_fmt = params_format(params);
|
||||
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
|
||||
cf, &hp);
|
||||
}
|
||||
|
||||
static int hdmi_codec_prepare(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
struct hdmi_codec_daifmt *cf = dai->playback_dma_data;
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
unsigned int channels = runtime->channels;
|
||||
unsigned int width = snd_pcm_format_width(runtime->format);
|
||||
unsigned int rate = runtime->rate;
|
||||
struct hdmi_codec_params hp;
|
||||
int ret;
|
||||
|
||||
if (!hcp->hcd.ops->prepare)
|
||||
return 0;
|
||||
|
||||
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
||||
width, rate, channels);
|
||||
|
||||
ret = hdmi_codec_fill_codec_params(dai, width, rate, channels, &hp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
memcpy(hp.iec.status, hcp->iec_status, sizeof(hp.iec.status));
|
||||
ret = snd_pcm_fill_iec958_consumer(runtime, hp.iec.status,
|
||||
sizeof(hp.iec.status));
|
||||
if (ret < 0) {
|
||||
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
cf->bit_fmt = runtime->format;
|
||||
return hcp->hcd.ops->prepare(dai->dev->parent, hcp->hcd.data,
|
||||
cf, &hp);
|
||||
}
|
||||
|
||||
static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
|
||||
unsigned int fmt)
|
||||
{
|
||||
@ -585,6 +690,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
|
||||
.startup = hdmi_codec_startup,
|
||||
.shutdown = hdmi_codec_shutdown,
|
||||
.hw_params = hdmi_codec_hw_params,
|
||||
.prepare = hdmi_codec_prepare,
|
||||
.set_fmt = hdmi_codec_i2s_set_fmt,
|
||||
.mute_stream = hdmi_codec_mute,
|
||||
};
|
||||
@ -621,21 +727,37 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
|
||||
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
|
||||
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
|
||||
|
||||
struct snd_kcontrol_new hdmi_codec_controls[] = {
|
||||
{
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK),
|
||||
.info = hdmi_codec_iec958_info,
|
||||
.get = hdmi_codec_iec958_mask_get,
|
||||
},
|
||||
{
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
|
||||
.info = hdmi_codec_iec958_info,
|
||||
.get = hdmi_codec_iec958_default_get,
|
||||
.put = hdmi_codec_iec958_default_put,
|
||||
},
|
||||
{
|
||||
.access = (SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE),
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "ELD",
|
||||
.info = hdmi_eld_ctl_info,
|
||||
.get = hdmi_eld_ctl_get,
|
||||
},
|
||||
};
|
||||
|
||||
static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_dai_driver *drv = dai->driver;
|
||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||
struct snd_kcontrol *kctl;
|
||||
struct snd_kcontrol_new hdmi_eld_ctl = {
|
||||
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
||||
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
||||
.name = "ELD",
|
||||
.info = hdmi_eld_ctl_info,
|
||||
.get = hdmi_eld_ctl_get,
|
||||
.device = rtd->pcm->device,
|
||||
};
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
||||
@ -652,12 +774,21 @@ static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
|
||||
hcp->chmap_info->chmap = hdmi_codec_stereo_chmaps;
|
||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||
|
||||
/* add ELD ctl with the device number corresponding to the PCM stream */
|
||||
kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component);
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
|
||||
struct snd_kcontrol *kctl;
|
||||
|
||||
return snd_ctl_add(rtd->card->snd_card, kctl);
|
||||
/* add ELD ctl with the device number corresponding to the PCM stream */
|
||||
kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
|
||||
if (!kctl)
|
||||
return -ENOMEM;
|
||||
|
||||
kctl->id.device = rtd->pcm->device;
|
||||
ret = snd_ctl_add(rtd->card->snd_card, kctl);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_dai_probe(struct snd_soc_dai *dai)
|
||||
@ -850,7 +981,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
dai_count = hcd->i2s + hcd->spdif;
|
||||
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
|
||||
if (dai_count < 1 || !hcd->ops ||
|
||||
(!hcd->ops->hw_params && !hcd->ops->prepare) ||
|
||||
!hcd->ops->audio_shutdown) {
|
||||
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
||||
return -EINVAL;
|
||||
@ -863,6 +995,11 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
||||
hcp->hcd = *hcd;
|
||||
mutex_init(&hcp->lock);
|
||||
|
||||
ret = snd_pcm_create_iec958_consumer_default(hcp->iec_status,
|
||||
sizeof(hcp->iec_status));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
|
||||
if (!daidrv)
|
||||
return -ENOMEM;
|
||||
|
Loading…
Reference in New Issue
Block a user