mirror of
https://github.com/Qortal/Brooklyn.git
synced 2025-02-07 06:44:18 +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",
|
interrupt-names = "cec-tx", "cec-rx", "cec-low",
|
||||||
"wakeup", "hpd-connected", "hpd-removed";
|
"wakeup", "hpd-connected", "hpd-removed";
|
||||||
ddc = <&ddc0>;
|
ddc = <&ddc0>;
|
||||||
dmas = <&dma 10>;
|
dmas = <&dma (10 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
|
||||||
dma-names = "audio-rx";
|
dma-names = "audio-rx";
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
@ -406,7 +406,7 @@ hdmi1: hdmi@7ef05700 {
|
|||||||
<9>, <10>, <11>;
|
<9>, <10>, <11>;
|
||||||
interrupt-names = "cec-tx", "cec-rx", "cec-low",
|
interrupt-names = "cec-tx", "cec-rx", "cec-low",
|
||||||
"wakeup", "hpd-connected", "hpd-removed";
|
"wakeup", "hpd-connected", "hpd-removed";
|
||||||
dmas = <&dma 17>;
|
dmas = <&dma (17 | (1 << 27) | (1 << 24)| (15 << 20) | (10 << 16))>;
|
||||||
dma-names = "audio-rx";
|
dma-names = "audio-rx";
|
||||||
status = "disabled";
|
status = "disabled";
|
||||||
};
|
};
|
||||||
|
@ -126,7 +126,7 @@ boardid: gpio@3a {
|
|||||||
compatible = "nxp,pca8574";
|
compatible = "nxp,pca8574";
|
||||||
reg = <0x3a>;
|
reg = <0x3a>;
|
||||||
gpio-controller;
|
gpio-controller;
|
||||||
#gpio-cells = <1>;
|
#gpio-cells = <2>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1318,6 +1318,7 @@ CONFIG_R8188EU=m
|
|||||||
CONFIG_VT6656=m
|
CONFIG_VT6656=m
|
||||||
CONFIG_STAGING_MEDIA=y
|
CONFIG_STAGING_MEDIA=y
|
||||||
CONFIG_VIDEO_RPIVID=m
|
CONFIG_VIDEO_RPIVID=m
|
||||||
|
CONFIG_ASHMEM=y
|
||||||
CONFIG_FB_TFT=m
|
CONFIG_FB_TFT=m
|
||||||
CONFIG_FB_TFT_AGM1264K_FL=m
|
CONFIG_FB_TFT_AGM1264K_FL=m
|
||||||
CONFIG_FB_TFT_BD663474=m
|
CONFIG_FB_TFT_BD663474=m
|
||||||
@ -1384,6 +1385,9 @@ CONFIG_MAX31856=m
|
|||||||
CONFIG_PWM_BCM2835=m
|
CONFIG_PWM_BCM2835=m
|
||||||
CONFIG_PWM_PCA9685=m
|
CONFIG_PWM_PCA9685=m
|
||||||
CONFIG_RPI_AXIPERF=m
|
CONFIG_RPI_AXIPERF=m
|
||||||
|
CONFIG_ANDROID=y
|
||||||
|
CONFIG_ANDROID_BINDER_IPC=y
|
||||||
|
CONFIG_ANDROID_BINDERFS=y
|
||||||
CONFIG_NVMEM_RMEM=m
|
CONFIG_NVMEM_RMEM=m
|
||||||
CONFIG_EXT4_FS=y
|
CONFIG_EXT4_FS=y
|
||||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||||
|
@ -1175,6 +1175,7 @@ CONFIG_R8712U=m
|
|||||||
CONFIG_R8188EU=m
|
CONFIG_R8188EU=m
|
||||||
CONFIG_VT6656=m
|
CONFIG_VT6656=m
|
||||||
CONFIG_STAGING_MEDIA=y
|
CONFIG_STAGING_MEDIA=y
|
||||||
|
CONFIG_ASHMEM=y
|
||||||
CONFIG_FB_TFT=m
|
CONFIG_FB_TFT=m
|
||||||
CONFIG_FB_TFT_AGM1264K_FL=m
|
CONFIG_FB_TFT_AGM1264K_FL=m
|
||||||
CONFIG_FB_TFT_BD663474=m
|
CONFIG_FB_TFT_BD663474=m
|
||||||
@ -1234,6 +1235,9 @@ CONFIG_MAXIM_THERMOCOUPLE=m
|
|||||||
CONFIG_MAX31856=m
|
CONFIG_MAX31856=m
|
||||||
CONFIG_PWM_BCM2835=m
|
CONFIG_PWM_BCM2835=m
|
||||||
CONFIG_PWM_PCA9685=m
|
CONFIG_PWM_PCA9685=m
|
||||||
|
CONFIG_ANDROID=y
|
||||||
|
CONFIG_ANDROID_BINDER_IPC=y
|
||||||
|
CONFIG_ANDROID_BINDERFS=y
|
||||||
CONFIG_EXT4_FS=y
|
CONFIG_EXT4_FS=y
|
||||||
CONFIG_EXT4_FS_POSIX_ACL=y
|
CONFIG_EXT4_FS_POSIX_ACL=y
|
||||||
CONFIG_EXT4_FS_SECURITY=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
|
* allows drivers to push pixels to more than one encoder from the
|
||||||
* same CRTC.
|
* same CRTC.
|
||||||
*/
|
*/
|
||||||
static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
|
||||||
struct drm_atomic_state *state,
|
struct drm_crtc_state *state)
|
||||||
struct drm_connector_state *(*get_state)(struct drm_atomic_state *state,
|
|
||||||
struct drm_connector *connector))
|
|
||||||
{
|
{
|
||||||
struct drm_connector *connector;
|
struct drm_encoder *encoder;
|
||||||
struct drm_connector_list_iter conn_iter;
|
|
||||||
|
|
||||||
drm_connector_list_iter_begin(crtc->dev, &conn_iter);
|
WARN_ON(hweight32(state->encoder_mask) > 1);
|
||||||
drm_for_each_connector_iter(connector, &conn_iter) {
|
|
||||||
struct drm_connector_state *conn_state = get_state(state, connector);
|
|
||||||
|
|
||||||
if (!conn_state)
|
drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask)
|
||||||
continue;
|
return encoder;
|
||||||
|
|
||||||
if (conn_state->crtc == crtc) {
|
|
||||||
drm_connector_list_iter_end(&conn_iter);
|
|
||||||
return connector->encoder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drm_connector_list_iter_end(&conn_iter);
|
|
||||||
|
|
||||||
return NULL;
|
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);
|
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 drm_device *dev = crtc->dev;
|
||||||
struct vc4_dev *vc4 = to_vc4_dev(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_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
||||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||||
const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_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,
|
struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state,
|
||||||
crtc);
|
crtc);
|
||||||
struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
|
struct vc4_crtc_state *old_vc4_state = to_vc4_crtc_state(old_state);
|
||||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
|
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, old_state);
|
||||||
drm_atomic_get_old_connector_state);
|
|
||||||
struct drm_device *dev = crtc->dev;
|
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);
|
require_hvs_enabled(dev);
|
||||||
|
|
||||||
/* Disable vblank irq handling before crtc is disabled. */
|
/* 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,
|
static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
|
||||||
struct drm_atomic_state *state)
|
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 drm_device *dev = crtc->dev;
|
||||||
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
|
||||||
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, state,
|
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state);
|
||||||
drm_atomic_get_new_connector_state);
|
|
||||||
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
|
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);
|
require_hvs_enabled(dev);
|
||||||
|
|
||||||
/* Enable vblank irq handling before crtc is started otherwise
|
/* 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)
|
if (vc4_encoder->pre_crtc_configure)
|
||||||
vc4_encoder->pre_crtc_configure(encoder, state);
|
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);
|
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 vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
|
||||||
struct drm_connector *conn;
|
struct drm_connector *conn;
|
||||||
struct drm_connector_state *conn_state;
|
struct drm_connector_state *conn_state;
|
||||||
|
struct drm_encoder *encoder;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
ret = vc4_hvs_atomic_check(crtc, state);
|
ret = vc4_hvs_atomic_check(crtc, state);
|
||||||
if (ret)
|
if (ret)
|
||||||
return 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,
|
for_each_new_connector_in_state(state, conn, conn_state,
|
||||||
i) {
|
i) {
|
||||||
if (conn_state->crtc != crtc)
|
if (conn_state->crtc != crtc)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <linux/circ_buf.h>
|
#include <linux/circ_buf.h>
|
||||||
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
||||||
#include <linux/debugfs.h>
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
|
||||||
#include "vc4_drv.h"
|
#include "vc4_drv.h"
|
||||||
#include "vc4_regs.h"
|
#include "vc4_regs.h"
|
||||||
@ -26,6 +27,8 @@ vc4_debugfs_init(struct drm_minor *minor)
|
|||||||
struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
|
struct vc4_dev *vc4 = to_vc4_dev(minor->dev);
|
||||||
struct vc4_debugfs_info_entry *entry;
|
struct vc4_debugfs_info_entry *entry;
|
||||||
|
|
||||||
|
if (!of_device_is_compatible(vc4->hvs->pdev->dev.of_node,
|
||||||
|
"brcm,bcm2711-vc5"))
|
||||||
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
|
debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
|
||||||
minor->debugfs_root, &vc4->load_tracker_enabled);
|
minor->debugfs_root, &vc4->load_tracker_enabled);
|
||||||
|
|
||||||
|
@ -205,9 +205,6 @@ struct vc4_dev {
|
|||||||
|
|
||||||
int power_refcount;
|
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. */
|
/* Set to true when the load tracker is active. */
|
||||||
bool load_tracker_enabled;
|
bool load_tracker_enabled;
|
||||||
|
|
||||||
@ -327,6 +324,7 @@ struct vc4_hvs {
|
|||||||
u32 __iomem *dlist;
|
u32 __iomem *dlist;
|
||||||
|
|
||||||
struct clk *core_clk;
|
struct clk *core_clk;
|
||||||
|
struct clk_request *core_req;
|
||||||
|
|
||||||
/* Memory manager for CRTCs to allocate space in the display
|
/* Memory manager for CRTCs to allocate space in the display
|
||||||
* list. Units are dwords.
|
* 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);
|
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 vc4_crtc_state {
|
||||||
struct drm_crtc_state base;
|
struct drm_crtc_state base;
|
||||||
/* Dlist area for this CRTC configuration. */
|
/* Dlist area for this CRTC configuration. */
|
||||||
@ -535,6 +536,8 @@ struct vc4_crtc_state {
|
|||||||
unsigned int bottom;
|
unsigned int bottom;
|
||||||
} margins;
|
} margins;
|
||||||
|
|
||||||
|
unsigned long hvs_load;
|
||||||
|
|
||||||
/* Transitional state below, only valid during atomic commits */
|
/* Transitional state below, only valid during atomic commits */
|
||||||
bool update_muxing;
|
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 vc4_hvs_state {
|
||||||
struct drm_private_state base;
|
struct drm_private_state base;
|
||||||
|
unsigned long core_clock_rate;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
unsigned in_use: 1;
|
unsigned in_use: 1;
|
||||||
|
unsigned long fifo_load;
|
||||||
struct drm_crtc_commit *pending_commit;
|
struct drm_crtc_commit *pending_commit;
|
||||||
} fifo_state[HVS_NUM_CHANNELS];
|
} 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 vc4_hvs *hvs = vc4->hvs;
|
||||||
struct drm_crtc_state *old_crtc_state;
|
struct drm_crtc_state *old_crtc_state;
|
||||||
struct drm_crtc_state *new_crtc_state;
|
struct drm_crtc_state *new_crtc_state;
|
||||||
|
struct vc4_hvs_state *new_hvs_state;
|
||||||
struct drm_crtc *crtc;
|
struct drm_crtc *crtc;
|
||||||
struct vc4_hvs_state *old_hvs_state;
|
struct vc4_hvs_state *old_hvs_state;
|
||||||
struct clk_request *core_req;
|
struct clk_request *core_req;
|
||||||
int i;
|
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) {
|
for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
|
||||||
struct vc4_crtc_state *vc4_crtc_state;
|
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);
|
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);
|
core_req = clk_request_start(hvs->core_clk, 500000000);
|
||||||
|
|
||||||
old_hvs_state = vc4_hvs_get_old_global_state(state);
|
/*
|
||||||
if (!old_hvs_state)
|
* And remove the previous one based on the HVS
|
||||||
return;
|
* requirements if any.
|
||||||
|
*/
|
||||||
|
clk_request_done(hvs->core_req);
|
||||||
|
}
|
||||||
|
|
||||||
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
|
for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
|
||||||
struct vc4_crtc_state *vc4_crtc_state =
|
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);
|
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);
|
clk_request_done(core_req);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vc4_atomic_commit_setup(struct drm_atomic_state *state)
|
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;
|
struct drm_plane *plane;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (!vc4->load_tracker_available)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
priv_state = drm_atomic_get_private_obj_state(state,
|
priv_state = drm_atomic_get_private_obj_state(state,
|
||||||
&vc4->load_tracker);
|
&vc4->load_tracker);
|
||||||
if (IS_ERR(priv_state))
|
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);
|
struct vc4_dev *vc4 = to_vc4_dev(dev);
|
||||||
|
|
||||||
if (!vc4->load_tracker_available)
|
|
||||||
return;
|
|
||||||
|
|
||||||
drm_atomic_private_obj_fini(&vc4->load_tracker);
|
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;
|
struct vc4_load_tracker_state *load_state;
|
||||||
|
|
||||||
if (!vc4->load_tracker_available)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
|
load_state = kzalloc(sizeof(*load_state), GFP_KERNEL);
|
||||||
if (!load_state)
|
if (!load_state)
|
||||||
return -ENOMEM;
|
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);
|
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < HVS_NUM_CHANNELS; i++) {
|
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].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)
|
if (!old_state->fifo_state[i].pending_commit)
|
||||||
continue;
|
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);
|
drm_crtc_commit_get(old_state->fifo_state[i].pending_commit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state->core_clock_rate = old_state->core_clock_rate;
|
||||||
|
|
||||||
|
|
||||||
return &state->base;
|
return &state->base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -859,6 +883,76 @@ static int vc4_pv_muxing_atomic_check(struct drm_device *dev,
|
|||||||
return 0;
|
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
|
static int
|
||||||
vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
|
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)
|
if (ret)
|
||||||
return 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 = {
|
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");
|
"brcm,bcm2711-vc5");
|
||||||
int ret;
|
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) {
|
if (!is_vc5) {
|
||||||
vc4->load_tracker_available = true;
|
|
||||||
|
|
||||||
/* Start with the load tracker enabled. Can be
|
/* Start with the load tracker enabled. Can be
|
||||||
* disabled through the debugfs load_tracker file.
|
* 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;
|
struct vc4_dev *vc4;
|
||||||
|
|
||||||
vc4 = to_vc4_dev(state->plane->dev);
|
vc4 = to_vc4_dev(state->plane->dev);
|
||||||
if (!vc4->load_tracker_available)
|
|
||||||
return;
|
|
||||||
|
|
||||||
vc4_state = to_vc4_plane_state(state);
|
vc4_state = to_vc4_plane_state(state);
|
||||||
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
|
crtc_state = drm_atomic_get_existing_crtc_state(state->state,
|
||||||
state->crtc);
|
state->crtc);
|
||||||
|
@ -65,12 +65,22 @@ struct hdmi_codec_ops {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Configures HDMI-encoder for audio stream.
|
* Configures HDMI-encoder for audio stream.
|
||||||
* Mandatory
|
* Having either prepare or hw_params is mandatory.
|
||||||
*/
|
*/
|
||||||
int (*hw_params)(struct device *dev, void *data,
|
int (*hw_params)(struct device *dev, void *data,
|
||||||
struct hdmi_codec_daifmt *fmt,
|
struct hdmi_codec_daifmt *fmt,
|
||||||
struct hdmi_codec_params *hparms);
|
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.
|
* Shuts down the audio stream.
|
||||||
* Mandatory
|
* Mandatory
|
||||||
|
@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#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,
|
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||||
size_t len);
|
size_t len);
|
||||||
|
|
||||||
|
@ -1,34 +1,16 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
/* Header file for iptables ipt_ECN target
|
#ifndef _IPT_ECN_H
|
||||||
*
|
#define _IPT_ECN_H
|
||||||
* (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
|
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/netfilter/xt_ecn.h>
|
||||||
#include <linux/netfilter/xt_DSCP.h>
|
#define ipt_ecn_info xt_ecn_info
|
||||||
|
|
||||||
#define IPT_ECN_IP_MASK (~XT_DSCP_MASK)
|
enum {
|
||||||
|
IPT_ECN_IP_MASK = XT_ECN_IP_MASK,
|
||||||
#define IPT_ECN_OP_SET_IP 0x01 /* set ECN bits of IPv4 header */
|
IPT_ECN_OP_MATCH_IP = XT_ECN_OP_MATCH_IP,
|
||||||
#define IPT_ECN_OP_SET_ECE 0x10 /* set ECE bit of TCP header */
|
IPT_ECN_OP_MATCH_ECE = XT_ECN_OP_MATCH_ECE,
|
||||||
#define IPT_ECN_OP_SET_CWR 0x20 /* set CWR bit of TCP header */
|
IPT_ECN_OP_MATCH_CWR = XT_ECN_OP_MATCH_CWR,
|
||||||
|
IPT_ECN_OP_MATCH_MASK = XT_ECN_OP_MATCH_MASK,
|
||||||
#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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* _IPT_ECN_TARGET_H */
|
#endif /* IPT_ECN_H */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||||
/* IP tables module for matching the value of the TTL
|
/* TTL modification module for IP tables
|
||||||
* (C) 2000 by Harald Welte <laforge@gnumonks.org> */
|
* (C) 2000 by Harald Welte <laforge@netfilter.org> */
|
||||||
|
|
||||||
#ifndef _IPT_TTL_H
|
#ifndef _IPT_TTL_H
|
||||||
#define _IPT_TTL_H
|
#define _IPT_TTL_H
|
||||||
@ -8,14 +8,14 @@
|
|||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
IPT_TTL_EQ = 0, /* equals */
|
IPT_TTL_SET = 0,
|
||||||
IPT_TTL_NE, /* not equals */
|
IPT_TTL_INC,
|
||||||
IPT_TTL_LT, /* less than */
|
IPT_TTL_DEC
|
||||||
IPT_TTL_GT, /* greater than */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define IPT_TTL_MAXMODE IPT_TTL_DEC
|
||||||
|
|
||||||
struct ipt_ttl_info {
|
struct ipt_TTL_info {
|
||||||
__u8 mode;
|
__u8 mode;
|
||||||
__u8 ttl;
|
__u8 ttl;
|
||||||
};
|
};
|
||||||
|
@ -9,14 +9,34 @@
|
|||||||
#include <sound/pcm_params.h>
|
#include <sound/pcm_params.h>
|
||||||
#include <sound/pcm_iec958.h>
|
#include <sound/pcm_iec958.h>
|
||||||
|
|
||||||
static int create_iec958_consumer(uint rate, uint sample_width,
|
int snd_pcm_create_iec958_consumer_default(u8 *cs, size_t len)
|
||||||
u8 *cs, size_t len)
|
|
||||||
{
|
{
|
||||||
unsigned int fs, ws;
|
|
||||||
|
|
||||||
if (len < 4)
|
if (len < 4)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
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) {
|
switch (rate) {
|
||||||
case 32000:
|
case 32000:
|
||||||
fs = IEC958_AES3_CON_FS_32000;
|
fs = IEC958_AES3_CON_FS_32000;
|
||||||
@ -43,7 +63,14 @@ static int create_iec958_consumer(uint rate, uint sample_width,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > 4) {
|
cs[3] &= ~IEC958_AES3_CON_FS;
|
||||||
|
cs[3] |= fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len > 4 &&
|
||||||
|
(cs[4] & IEC958_AES4_CON_WORDLEN) == IEC958_AES4_CON_WORDLEN_NOTID) {
|
||||||
|
unsigned int ws;
|
||||||
|
|
||||||
switch (sample_width) {
|
switch (sample_width) {
|
||||||
case 16:
|
case 16:
|
||||||
ws = IEC958_AES4_CON_WORDLEN_20_16;
|
ws = IEC958_AES4_CON_WORDLEN_20_16;
|
||||||
@ -64,21 +91,30 @@ static int create_iec958_consumer(uint rate, uint sample_width,
|
|||||||
default:
|
default:
|
||||||
return -EINVAL;
|
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;
|
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
|
* snd_pcm_create_iec958_consumer - create consumer format IEC958 channel status
|
||||||
* @runtime: pcm runtime structure with ->rate filled in
|
* @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,
|
int snd_pcm_create_iec958_consumer(struct snd_pcm_runtime *runtime, u8 *cs,
|
||||||
size_t len)
|
size_t len)
|
||||||
{
|
{
|
||||||
return create_iec958_consumer(runtime->rate,
|
int ret;
|
||||||
snd_pcm_format_width(runtime->format),
|
|
||||||
cs, len);
|
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);
|
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,
|
int snd_pcm_create_iec958_consumer_hw_params(struct snd_pcm_hw_params *params,
|
||||||
u8 *cs, size_t len)
|
u8 *cs, size_t len)
|
||||||
{
|
{
|
||||||
return create_iec958_consumer(params_rate(params), params_width(params),
|
int ret;
|
||||||
cs, len);
|
|
||||||
|
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);
|
EXPORT_SYMBOL(snd_pcm_create_iec958_consumer_hw_params);
|
||||||
|
@ -278,6 +278,7 @@ struct hdmi_codec_priv {
|
|||||||
bool busy;
|
bool busy;
|
||||||
struct snd_soc_jack *jack;
|
struct snd_soc_jack *jack;
|
||||||
unsigned int jack_status;
|
unsigned int jack_status;
|
||||||
|
u8 iec_status[5];
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
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;
|
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,
|
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
@ -440,6 +482,42 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
|||||||
mutex_unlock(&hcp->lock);
|
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,
|
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
||||||
struct snd_pcm_hw_params *params,
|
struct snd_pcm_hw_params *params,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
@ -454,13 +532,25 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
|||||||
.dig_subframe = { 0 },
|
.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__,
|
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
||||||
params_width(params), params_rate(params),
|
params_width(params), params_rate(params),
|
||||||
params_channels(params));
|
params_channels(params));
|
||||||
|
|
||||||
ret = snd_pcm_create_iec958_consumer_hw_params(params, 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));
|
sizeof(hp.iec.status));
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
||||||
@ -468,32 +558,47 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
|||||||
return 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);
|
cf->bit_fmt = params_format(params);
|
||||||
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
|
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
|
||||||
cf, &hp);
|
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,
|
static int hdmi_codec_i2s_set_fmt(struct snd_soc_dai *dai,
|
||||||
unsigned int fmt)
|
unsigned int fmt)
|
||||||
{
|
{
|
||||||
@ -585,6 +690,7 @@ static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
|
|||||||
.startup = hdmi_codec_startup,
|
.startup = hdmi_codec_startup,
|
||||||
.shutdown = hdmi_codec_shutdown,
|
.shutdown = hdmi_codec_shutdown,
|
||||||
.hw_params = hdmi_codec_hw_params,
|
.hw_params = hdmi_codec_hw_params,
|
||||||
|
.prepare = hdmi_codec_prepare,
|
||||||
.set_fmt = hdmi_codec_i2s_set_fmt,
|
.set_fmt = hdmi_codec_i2s_set_fmt,
|
||||||
.mute_stream = hdmi_codec_mute,
|
.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_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\
|
||||||
SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
|
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,
|
static int hdmi_codec_pcm_new(struct snd_soc_pcm_runtime *rtd,
|
||||||
struct snd_soc_dai *dai)
|
struct snd_soc_dai *dai)
|
||||||
{
|
{
|
||||||
struct snd_soc_dai_driver *drv = dai->driver;
|
struct snd_soc_dai_driver *drv = dai->driver;
|
||||||
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
||||||
struct snd_kcontrol *kctl;
|
unsigned int i;
|
||||||
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,
|
|
||||||
};
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
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_info->chmap = hdmi_codec_stereo_chmaps;
|
||||||
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(hdmi_codec_controls); i++) {
|
||||||
|
struct snd_kcontrol *kctl;
|
||||||
|
|
||||||
/* add ELD ctl with the device number corresponding to the PCM stream */
|
/* add ELD ctl with the device number corresponding to the PCM stream */
|
||||||
kctl = snd_ctl_new1(&hdmi_eld_ctl, dai->component);
|
kctl = snd_ctl_new1(&hdmi_codec_controls[i], dai->component);
|
||||||
if (!kctl)
|
if (!kctl)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
return snd_ctl_add(rtd->card->snd_card, kctl);
|
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)
|
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;
|
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) {
|
!hcd->ops->audio_shutdown) {
|
||||||
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -863,6 +995,11 @@ static int hdmi_codec_probe(struct platform_device *pdev)
|
|||||||
hcp->hcd = *hcd;
|
hcp->hcd = *hcd;
|
||||||
mutex_init(&hcp->lock);
|
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);
|
daidrv = devm_kcalloc(dev, dai_count, sizeof(*daidrv), GFP_KERNEL);
|
||||||
if (!daidrv)
|
if (!daidrv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
Loading…
Reference in New Issue
Block a user