Merge tag 'sti-drm-next-2017-01-06' of https://github.com/vinceab/linux into drm-next
stih410 cleanup, create fbdev at binding, HQVDP fixes. * tag 'sti-drm-next-2017-01-06' of https://github.com/vinceab/linux: drm/sti: sti_vtg: Handle return NULL error from devm_ioremap_nocache drm/sti: remove deprecated sti_vtac.c file drm/sti: create fbdev at binding drm/sti: update fps debugfs entries drm/sti: do not post HQVDP command if no update drm/sti: load XP70 firmware only once drm/sti: allow audio playback on HDMI even if disabled.
This commit is contained in:
commit
d64a1661c8
|
@ -13,7 +13,6 @@ sti-drm-y := \
|
||||||
sti_dvo.o \
|
sti_dvo.o \
|
||||||
sti_awg_utils.o \
|
sti_awg_utils.o \
|
||||||
sti_vtg.o \
|
sti_vtg.o \
|
||||||
sti_vtac.o \
|
|
||||||
sti_hda.o \
|
sti_hda.o \
|
||||||
sti_tvout.o \
|
sti_tvout.o \
|
||||||
sti_hqvdp.o \
|
sti_hqvdp.o \
|
||||||
|
|
|
@ -252,19 +252,7 @@ static void sti_output_poll_changed(struct drm_device *ddev)
|
||||||
{
|
{
|
||||||
struct sti_private *private = ddev->dev_private;
|
struct sti_private *private = ddev->dev_private;
|
||||||
|
|
||||||
if (!ddev->mode_config.num_connector)
|
drm_fbdev_cma_hotplug_event(private->fbdev);
|
||||||
return;
|
|
||||||
|
|
||||||
if (private->fbdev) {
|
|
||||||
drm_fbdev_cma_hotplug_event(private->fbdev);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
private->fbdev = drm_fbdev_cma_init(ddev, 32,
|
|
||||||
ddev->mode_config.num_crtc,
|
|
||||||
ddev->mode_config.num_connector);
|
|
||||||
if (IS_ERR(private->fbdev))
|
|
||||||
private->fbdev = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
|
static const struct drm_mode_config_funcs sti_mode_config_funcs = {
|
||||||
|
@ -382,6 +370,8 @@ static void sti_cleanup(struct drm_device *ddev)
|
||||||
static int sti_bind(struct device *dev)
|
static int sti_bind(struct device *dev)
|
||||||
{
|
{
|
||||||
struct drm_device *ddev;
|
struct drm_device *ddev;
|
||||||
|
struct sti_private *private;
|
||||||
|
struct drm_fbdev_cma *fbdev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ddev = drm_dev_alloc(&sti_driver, dev);
|
ddev = drm_dev_alloc(&sti_driver, dev);
|
||||||
|
@ -404,6 +394,17 @@ static int sti_bind(struct device *dev)
|
||||||
|
|
||||||
drm_mode_config_reset(ddev);
|
drm_mode_config_reset(ddev);
|
||||||
|
|
||||||
|
private = ddev->dev_private;
|
||||||
|
if (ddev->mode_config.num_connector) {
|
||||||
|
fbdev = drm_fbdev_cma_init(ddev, 32, ddev->mode_config.num_crtc,
|
||||||
|
ddev->mode_config.num_connector);
|
||||||
|
if (IS_ERR(fbdev)) {
|
||||||
|
DRM_DEBUG_DRIVER("Warning: fails to create fbdev\n");
|
||||||
|
fbdev = NULL;
|
||||||
|
}
|
||||||
|
private->fbdev = fbdev;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_register:
|
err_register:
|
||||||
|
@ -476,7 +477,6 @@ static struct platform_driver sti_platform_driver = {
|
||||||
|
|
||||||
static struct platform_driver * const drivers[] = {
|
static struct platform_driver * const drivers[] = {
|
||||||
&sti_tvout_driver,
|
&sti_tvout_driver,
|
||||||
&sti_vtac_driver,
|
|
||||||
&sti_hqvdp_driver,
|
&sti_hqvdp_driver,
|
||||||
&sti_hdmi_driver,
|
&sti_hdmi_driver,
|
||||||
&sti_hda_driver,
|
&sti_hda_driver,
|
||||||
|
|
|
@ -34,7 +34,6 @@ struct sti_private {
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct platform_driver sti_tvout_driver;
|
extern struct platform_driver sti_tvout_driver;
|
||||||
extern struct platform_driver sti_vtac_driver;
|
|
||||||
extern struct platform_driver sti_hqvdp_driver;
|
extern struct platform_driver sti_hqvdp_driver;
|
||||||
extern struct platform_driver sti_hdmi_driver;
|
extern struct platform_driver sti_hdmi_driver;
|
||||||
extern struct platform_driver sti_hda_driver;
|
extern struct platform_driver sti_hda_driver;
|
||||||
|
|
|
@ -788,6 +788,95 @@ static void sti_hdmi_disable(struct drm_bridge *bridge)
|
||||||
hdmi->enabled = false;
|
hdmi->enabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
|
||||||
|
* clocks. None-coherent clocks means that audio and TMDS clocks have not the
|
||||||
|
* same source (drifts between clocks). In this case assumption is that CTS is
|
||||||
|
* automatically calculated by hardware.
|
||||||
|
*
|
||||||
|
* @audio_fs: audio frame clock frequency in Hz
|
||||||
|
*
|
||||||
|
* Values computed are based on table described in HDMI specification 1.4b
|
||||||
|
*
|
||||||
|
* Returns n value.
|
||||||
|
*/
|
||||||
|
static int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
|
||||||
|
{
|
||||||
|
unsigned int n;
|
||||||
|
|
||||||
|
switch (audio_fs) {
|
||||||
|
case 32000:
|
||||||
|
n = 4096;
|
||||||
|
break;
|
||||||
|
case 44100:
|
||||||
|
n = 6272;
|
||||||
|
break;
|
||||||
|
case 48000:
|
||||||
|
n = 6144;
|
||||||
|
break;
|
||||||
|
case 88200:
|
||||||
|
n = 6272 * 2;
|
||||||
|
break;
|
||||||
|
case 96000:
|
||||||
|
n = 6144 * 2;
|
||||||
|
break;
|
||||||
|
case 176400:
|
||||||
|
n = 6272 * 4;
|
||||||
|
break;
|
||||||
|
case 192000:
|
||||||
|
n = 6144 * 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Not pre-defined, recommended value: 128 * fs / 1000 */
|
||||||
|
n = (audio_fs * 128) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hdmi_audio_configure(struct sti_hdmi *hdmi)
|
||||||
|
{
|
||||||
|
int audio_cfg, n;
|
||||||
|
struct hdmi_audio_params *params = &hdmi->audio;
|
||||||
|
struct hdmi_audio_infoframe *info = ¶ms->cea;
|
||||||
|
|
||||||
|
DRM_DEBUG_DRIVER("\n");
|
||||||
|
|
||||||
|
if (!hdmi->enabled)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* update N parameter */
|
||||||
|
n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate);
|
||||||
|
|
||||||
|
DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n",
|
||||||
|
params->sample_rate, hdmi->mode.clock * 1000, n);
|
||||||
|
hdmi_write(hdmi, n, HDMI_AUDN);
|
||||||
|
|
||||||
|
/* update HDMI registers according to configuration */
|
||||||
|
audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID |
|
||||||
|
HDMI_AUD_CFG_ONE_BIT_INVALID;
|
||||||
|
|
||||||
|
switch (info->channels) {
|
||||||
|
case 8:
|
||||||
|
audio_cfg |= HDMI_AUD_CFG_CH78_VALID;
|
||||||
|
case 6:
|
||||||
|
audio_cfg |= HDMI_AUD_CFG_CH56_VALID;
|
||||||
|
case 4:
|
||||||
|
audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH;
|
||||||
|
case 2:
|
||||||
|
audio_cfg |= HDMI_AUD_CFG_CH12_VALID;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n",
|
||||||
|
info->channels);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
|
||||||
|
|
||||||
|
return hdmi_audio_infoframe_config(hdmi);
|
||||||
|
}
|
||||||
|
|
||||||
static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
|
static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
|
||||||
{
|
{
|
||||||
struct sti_hdmi *hdmi = bridge->driver_private;
|
struct sti_hdmi *hdmi = bridge->driver_private;
|
||||||
|
@ -826,9 +915,12 @@ static void sti_hdmi_pre_enable(struct drm_bridge *bridge)
|
||||||
if (hdmi_avi_infoframe_config(hdmi))
|
if (hdmi_avi_infoframe_config(hdmi))
|
||||||
DRM_ERROR("Unable to configure AVI infoframe\n");
|
DRM_ERROR("Unable to configure AVI infoframe\n");
|
||||||
|
|
||||||
/* Program AUDIO infoframe */
|
if (hdmi->audio.enabled) {
|
||||||
if (hdmi_audio_infoframe_config(hdmi))
|
if (hdmi_audio_configure(hdmi))
|
||||||
DRM_ERROR("Unable to configure AUDIO infoframe\n");
|
DRM_ERROR("Unable to configure audio\n");
|
||||||
|
} else {
|
||||||
|
hdmi_audio_infoframe_config(hdmi);
|
||||||
|
}
|
||||||
|
|
||||||
/* Program VS infoframe */
|
/* Program VS infoframe */
|
||||||
if (hdmi_vendor_infoframe_config(hdmi))
|
if (hdmi_vendor_infoframe_config(hdmi))
|
||||||
|
@ -1078,97 +1170,6 @@ static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* sti_hdmi_audio_get_non_coherent_n() - get N parameter for non-coherent
|
|
||||||
* clocks. None-coherent clocks means that audio and TMDS clocks have not the
|
|
||||||
* same source (drifts between clocks). In this case assumption is that CTS is
|
|
||||||
* automatically calculated by hardware.
|
|
||||||
*
|
|
||||||
* @audio_fs: audio frame clock frequency in Hz
|
|
||||||
*
|
|
||||||
* Values computed are based on table described in HDMI specification 1.4b
|
|
||||||
*
|
|
||||||
* Returns n value.
|
|
||||||
*/
|
|
||||||
static int sti_hdmi_audio_get_non_coherent_n(unsigned int audio_fs)
|
|
||||||
{
|
|
||||||
unsigned int n;
|
|
||||||
|
|
||||||
switch (audio_fs) {
|
|
||||||
case 32000:
|
|
||||||
n = 4096;
|
|
||||||
break;
|
|
||||||
case 44100:
|
|
||||||
n = 6272;
|
|
||||||
break;
|
|
||||||
case 48000:
|
|
||||||
n = 6144;
|
|
||||||
break;
|
|
||||||
case 88200:
|
|
||||||
n = 6272 * 2;
|
|
||||||
break;
|
|
||||||
case 96000:
|
|
||||||
n = 6144 * 2;
|
|
||||||
break;
|
|
||||||
case 176400:
|
|
||||||
n = 6272 * 4;
|
|
||||||
break;
|
|
||||||
case 192000:
|
|
||||||
n = 6144 * 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Not pre-defined, recommended value: 128 * fs / 1000 */
|
|
||||||
n = (audio_fs * 128) / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hdmi_audio_configure(struct sti_hdmi *hdmi,
|
|
||||||
struct hdmi_audio_params *params)
|
|
||||||
{
|
|
||||||
int audio_cfg, n;
|
|
||||||
struct hdmi_audio_infoframe *info = ¶ms->cea;
|
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("\n");
|
|
||||||
|
|
||||||
if (!hdmi->enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* update N parameter */
|
|
||||||
n = sti_hdmi_audio_get_non_coherent_n(params->sample_rate);
|
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("Audio rate = %d Hz, TMDS clock = %d Hz, n = %d\n",
|
|
||||||
params->sample_rate, hdmi->mode.clock * 1000, n);
|
|
||||||
hdmi_write(hdmi, n, HDMI_AUDN);
|
|
||||||
|
|
||||||
/* update HDMI registers according to configuration */
|
|
||||||
audio_cfg = HDMI_AUD_CFG_SPDIF_DIV_2 | HDMI_AUD_CFG_DTS_INVALID |
|
|
||||||
HDMI_AUD_CFG_ONE_BIT_INVALID;
|
|
||||||
|
|
||||||
switch (info->channels) {
|
|
||||||
case 8:
|
|
||||||
audio_cfg |= HDMI_AUD_CFG_CH78_VALID;
|
|
||||||
case 6:
|
|
||||||
audio_cfg |= HDMI_AUD_CFG_CH56_VALID;
|
|
||||||
case 4:
|
|
||||||
audio_cfg |= HDMI_AUD_CFG_CH34_VALID | HDMI_AUD_CFG_8CH;
|
|
||||||
case 2:
|
|
||||||
audio_cfg |= HDMI_AUD_CFG_CH12_VALID;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DRM_ERROR("ERROR: Unsupported number of channels (%d)!\n",
|
|
||||||
info->channels);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hdmi_write(hdmi, audio_cfg, HDMI_AUDIO_CFG);
|
|
||||||
|
|
||||||
hdmi->audio = *params;
|
|
||||||
|
|
||||||
return hdmi_audio_infoframe_config(hdmi);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hdmi_audio_shutdown(struct device *dev, void *data)
|
static void hdmi_audio_shutdown(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct sti_hdmi *hdmi = dev_get_drvdata(dev);
|
struct sti_hdmi *hdmi = dev_get_drvdata(dev);
|
||||||
|
@ -1192,17 +1193,9 @@ static int hdmi_audio_hw_params(struct device *dev,
|
||||||
{
|
{
|
||||||
struct sti_hdmi *hdmi = dev_get_drvdata(dev);
|
struct sti_hdmi *hdmi = dev_get_drvdata(dev);
|
||||||
int ret;
|
int ret;
|
||||||
struct hdmi_audio_params audio = {
|
|
||||||
.sample_width = params->sample_width,
|
|
||||||
.sample_rate = params->sample_rate,
|
|
||||||
.cea = params->cea,
|
|
||||||
};
|
|
||||||
|
|
||||||
DRM_DEBUG_DRIVER("\n");
|
DRM_DEBUG_DRIVER("\n");
|
||||||
|
|
||||||
if (!hdmi->enabled)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ((daifmt->fmt != HDMI_I2S) || daifmt->bit_clk_inv ||
|
if ((daifmt->fmt != HDMI_I2S) || daifmt->bit_clk_inv ||
|
||||||
daifmt->frame_clk_inv || daifmt->bit_clk_master ||
|
daifmt->frame_clk_inv || daifmt->bit_clk_master ||
|
||||||
daifmt->frame_clk_master) {
|
daifmt->frame_clk_master) {
|
||||||
|
@ -1213,9 +1206,13 @@ static int hdmi_audio_hw_params(struct device *dev,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio.enabled = true;
|
hdmi->audio.sample_width = params->sample_width;
|
||||||
|
hdmi->audio.sample_rate = params->sample_rate;
|
||||||
|
hdmi->audio.cea = params->cea;
|
||||||
|
|
||||||
ret = hdmi_audio_configure(hdmi, &audio);
|
hdmi->audio.enabled = true;
|
||||||
|
|
||||||
|
ret = hdmi_audio_configure(hdmi);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -332,6 +332,7 @@ struct sti_hqvdp_cmd {
|
||||||
* @hqvdp_cmd_paddr: physical address of hqvdp_cmd
|
* @hqvdp_cmd_paddr: physical address of hqvdp_cmd
|
||||||
* @vtg: vtg for main data path
|
* @vtg: vtg for main data path
|
||||||
* @xp70_initialized: true if xp70 is already initialized
|
* @xp70_initialized: true if xp70 is already initialized
|
||||||
|
* @vtg_registered: true if registered to VTG
|
||||||
*/
|
*/
|
||||||
struct sti_hqvdp {
|
struct sti_hqvdp {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
@ -347,6 +348,7 @@ struct sti_hqvdp {
|
||||||
u32 hqvdp_cmd_paddr;
|
u32 hqvdp_cmd_paddr;
|
||||||
struct sti_vtg *vtg;
|
struct sti_vtg *vtg;
|
||||||
bool xp70_initialized;
|
bool xp70_initialized;
|
||||||
|
bool vtg_registered;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, plane)
|
#define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, plane)
|
||||||
|
@ -771,7 +773,7 @@ static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp)
|
||||||
DRM_ERROR("XP70 could not revert to idle\n");
|
DRM_ERROR("XP70 could not revert to idle\n");
|
||||||
|
|
||||||
hqvdp->plane.status = STI_PLANE_DISABLED;
|
hqvdp->plane.status = STI_PLANE_DISABLED;
|
||||||
hqvdp->xp70_initialized = false;
|
hqvdp->vtg_registered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1064,10 +1066,11 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hqvdp->xp70_initialized) {
|
if (!hqvdp->xp70_initialized)
|
||||||
/* Start HQVDP XP70 coprocessor */
|
/* Start HQVDP XP70 coprocessor */
|
||||||
sti_hqvdp_start_xp70(hqvdp);
|
sti_hqvdp_start_xp70(hqvdp);
|
||||||
|
|
||||||
|
if (!hqvdp->vtg_registered) {
|
||||||
/* Prevent VTG shutdown */
|
/* Prevent VTG shutdown */
|
||||||
if (clk_prepare_enable(hqvdp->clk_pix_main)) {
|
if (clk_prepare_enable(hqvdp->clk_pix_main)) {
|
||||||
DRM_ERROR("Failed to prepare/enable pix main clk\n");
|
DRM_ERROR("Failed to prepare/enable pix main clk\n");
|
||||||
|
@ -1081,6 +1084,7 @@ static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,
|
||||||
DRM_ERROR("Cannot register VTG notifier\n");
|
DRM_ERROR("Cannot register VTG notifier\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
hqvdp->vtg_registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
|
||||||
|
@ -1113,6 +1117,21 @@ static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,
|
||||||
if (!crtc || !fb)
|
if (!crtc || !fb)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if ((oldstate->fb == state->fb) &&
|
||||||
|
(oldstate->crtc_x == state->crtc_x) &&
|
||||||
|
(oldstate->crtc_y == state->crtc_y) &&
|
||||||
|
(oldstate->crtc_w == state->crtc_w) &&
|
||||||
|
(oldstate->crtc_h == state->crtc_h) &&
|
||||||
|
(oldstate->src_x == state->src_x) &&
|
||||||
|
(oldstate->src_y == state->src_y) &&
|
||||||
|
(oldstate->src_w == state->src_w) &&
|
||||||
|
(oldstate->src_h == state->src_h)) {
|
||||||
|
/* No change since last update, do not post cmd */
|
||||||
|
DRM_DEBUG_DRIVER("No change, not posting cmd\n");
|
||||||
|
plane->status = STI_PLANE_UPDATED;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mode = &crtc->mode;
|
mode = &crtc->mode;
|
||||||
dst_x = state->crtc_x;
|
dst_x = state->crtc_x;
|
||||||
dst_y = state->crtc_y;
|
dst_y = state->crtc_y;
|
||||||
|
|
|
@ -65,9 +65,18 @@ void sti_plane_update_fps(struct sti_plane *plane,
|
||||||
|
|
||||||
fps->last_timestamp = now;
|
fps->last_timestamp = now;
|
||||||
fps->last_frame_counter = fps->curr_frame_counter;
|
fps->last_frame_counter = fps->curr_frame_counter;
|
||||||
fpks = (num_frames * 1000000) / ms_since_last;
|
|
||||||
snprintf(plane->fps_info.fps_str, FPS_LENGTH, "%-6s @ %d.%.3d fps",
|
if (plane->drm_plane.fb) {
|
||||||
sti_plane_to_str(plane), fpks / 1000, fpks % 1000);
|
fpks = (num_frames * 1000000) / ms_since_last;
|
||||||
|
snprintf(plane->fps_info.fps_str, FPS_LENGTH,
|
||||||
|
"%-8s %4dx%-4d %.4s @ %3d.%-3.3d fps (%s)",
|
||||||
|
plane->drm_plane.name,
|
||||||
|
plane->drm_plane.fb->width,
|
||||||
|
plane->drm_plane.fb->height,
|
||||||
|
(char *)&plane->drm_plane.fb->pixel_format,
|
||||||
|
fpks / 1000, fpks % 1000,
|
||||||
|
sti_plane_to_str(plane));
|
||||||
|
}
|
||||||
|
|
||||||
if (fps->curr_field_counter) {
|
if (fps->curr_field_counter) {
|
||||||
/* Compute number of field updates */
|
/* Compute number of field updates */
|
||||||
|
@ -75,7 +84,7 @@ void sti_plane_update_fps(struct sti_plane *plane,
|
||||||
fps->last_field_counter = fps->curr_field_counter;
|
fps->last_field_counter = fps->curr_field_counter;
|
||||||
fipks = (num_fields * 1000000) / ms_since_last;
|
fipks = (num_fields * 1000000) / ms_since_last;
|
||||||
snprintf(plane->fps_info.fips_str,
|
snprintf(plane->fps_info.fips_str,
|
||||||
FPS_LENGTH, " - %d.%.3d field/sec",
|
FPS_LENGTH, " - %3d.%-3.3d field/sec",
|
||||||
fipks / 1000, fipks % 1000);
|
fipks / 1000, fipks % 1000);
|
||||||
} else {
|
} else {
|
||||||
plane->fps_info.fips_str[0] = '\0';
|
plane->fps_info.fips_str[0] = '\0';
|
||||||
|
|
|
@ -48,7 +48,7 @@ enum sti_plane_status {
|
||||||
STI_PLANE_DISABLED,
|
STI_PLANE_DISABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FPS_LENGTH 64
|
#define FPS_LENGTH 128
|
||||||
struct sti_fps_info {
|
struct sti_fps_info {
|
||||||
bool output;
|
bool output;
|
||||||
unsigned int curr_frame_counter;
|
unsigned int curr_frame_counter;
|
||||||
|
|
|
@ -1,223 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) STMicroelectronics SA 2014
|
|
||||||
* Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
|
|
||||||
* License terms: GNU General Public License (GPL), version 2
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/clk.h>
|
|
||||||
#include <linux/io.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
|
|
||||||
#include <drm/drmP.h>
|
|
||||||
|
|
||||||
#include "sti_drv.h"
|
|
||||||
|
|
||||||
/* registers offset */
|
|
||||||
#define VTAC_CONFIG 0x00
|
|
||||||
#define VTAC_RX_FIFO_CONFIG 0x04
|
|
||||||
#define VTAC_FIFO_CONFIG_VAL 0x04
|
|
||||||
|
|
||||||
#define VTAC_SYS_CFG8521 0x824
|
|
||||||
#define VTAC_SYS_CFG8522 0x828
|
|
||||||
|
|
||||||
/* Number of phyts per pixel */
|
|
||||||
#define VTAC_2_5_PPP 0x0005
|
|
||||||
#define VTAC_3_PPP 0x0006
|
|
||||||
#define VTAC_4_PPP 0x0008
|
|
||||||
#define VTAC_5_PPP 0x000A
|
|
||||||
#define VTAC_6_PPP 0x000C
|
|
||||||
#define VTAC_13_PPP 0x001A
|
|
||||||
#define VTAC_14_PPP 0x001C
|
|
||||||
#define VTAC_15_PPP 0x001E
|
|
||||||
#define VTAC_16_PPP 0x0020
|
|
||||||
#define VTAC_17_PPP 0x0022
|
|
||||||
#define VTAC_18_PPP 0x0024
|
|
||||||
|
|
||||||
/* enable bits */
|
|
||||||
#define VTAC_ENABLE 0x3003
|
|
||||||
|
|
||||||
#define VTAC_TX_PHY_ENABLE_CLK_PHY BIT(0)
|
|
||||||
#define VTAC_TX_PHY_ENABLE_CLK_DLL BIT(1)
|
|
||||||
#define VTAC_TX_PHY_PLL_NOT_OSC_MODE BIT(3)
|
|
||||||
#define VTAC_TX_PHY_RST_N_DLL_SWITCH BIT(4)
|
|
||||||
#define VTAC_TX_PHY_PROG_N3 BIT(9)
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VTAC mode structure
|
|
||||||
*
|
|
||||||
* @vid_in_width: Video Data Resolution
|
|
||||||
* @phyts_width: Width of phyt buses(phyt low and phyt high).
|
|
||||||
* @phyts_per_pixel: Number of phyts sent per pixel
|
|
||||||
*/
|
|
||||||
struct sti_vtac_mode {
|
|
||||||
u32 vid_in_width;
|
|
||||||
u32 phyts_width;
|
|
||||||
u32 phyts_per_pixel;
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct sti_vtac_mode vtac_mode_main = {
|
|
||||||
.vid_in_width = 0x2,
|
|
||||||
.phyts_width = 0x2,
|
|
||||||
.phyts_per_pixel = VTAC_5_PPP,
|
|
||||||
};
|
|
||||||
static const struct sti_vtac_mode vtac_mode_aux = {
|
|
||||||
.vid_in_width = 0x1,
|
|
||||||
.phyts_width = 0x0,
|
|
||||||
.phyts_per_pixel = VTAC_17_PPP,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* VTAC structure
|
|
||||||
*
|
|
||||||
* @dev: pointer to device structure
|
|
||||||
* @regs: ioremapped registers for RX and TX devices
|
|
||||||
* @phy_regs: phy registers for TX device
|
|
||||||
* @clk: clock
|
|
||||||
* @mode: main or auxillary configuration mode
|
|
||||||
*/
|
|
||||||
struct sti_vtac {
|
|
||||||
struct device *dev;
|
|
||||||
void __iomem *regs;
|
|
||||||
void __iomem *phy_regs;
|
|
||||||
struct clk *clk;
|
|
||||||
const struct sti_vtac_mode *mode;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void sti_vtac_rx_set_config(struct sti_vtac *vtac)
|
|
||||||
{
|
|
||||||
u32 config;
|
|
||||||
|
|
||||||
/* Enable VTAC clock */
|
|
||||||
if (clk_prepare_enable(vtac->clk))
|
|
||||||
DRM_ERROR("Failed to prepare/enable vtac_rx clock.\n");
|
|
||||||
|
|
||||||
writel(VTAC_FIFO_CONFIG_VAL, vtac->regs + VTAC_RX_FIFO_CONFIG);
|
|
||||||
|
|
||||||
config = VTAC_ENABLE;
|
|
||||||
config |= vtac->mode->vid_in_width << 4;
|
|
||||||
config |= vtac->mode->phyts_width << 16;
|
|
||||||
config |= vtac->mode->phyts_per_pixel << 23;
|
|
||||||
writel(config, vtac->regs + VTAC_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sti_vtac_tx_set_config(struct sti_vtac *vtac)
|
|
||||||
{
|
|
||||||
u32 phy_config;
|
|
||||||
u32 config;
|
|
||||||
|
|
||||||
/* Enable VTAC clock */
|
|
||||||
if (clk_prepare_enable(vtac->clk))
|
|
||||||
DRM_ERROR("Failed to prepare/enable vtac_tx clock.\n");
|
|
||||||
|
|
||||||
/* Configure vtac phy */
|
|
||||||
phy_config = 0x00000000;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8522);
|
|
||||||
phy_config = VTAC_TX_PHY_ENABLE_CLK_PHY;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config |= VTAC_TX_PHY_PROG_N3;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config |= VTAC_TX_PHY_ENABLE_CLK_DLL;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config |= VTAC_TX_PHY_RST_N_DLL_SWITCH;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config = readl(vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
phy_config |= VTAC_TX_PHY_PLL_NOT_OSC_MODE;
|
|
||||||
writel(phy_config, vtac->phy_regs + VTAC_SYS_CFG8521);
|
|
||||||
|
|
||||||
/* Configure vtac tx */
|
|
||||||
config = VTAC_ENABLE;
|
|
||||||
config |= vtac->mode->vid_in_width << 4;
|
|
||||||
config |= vtac->mode->phyts_width << 16;
|
|
||||||
config |= vtac->mode->phyts_per_pixel << 23;
|
|
||||||
writel(config, vtac->regs + VTAC_CONFIG);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct of_device_id vtac_of_match[] = {
|
|
||||||
{
|
|
||||||
.compatible = "st,vtac-main",
|
|
||||||
.data = &vtac_mode_main,
|
|
||||||
}, {
|
|
||||||
.compatible = "st,vtac-aux",
|
|
||||||
.data = &vtac_mode_aux,
|
|
||||||
}, {
|
|
||||||
/* end node */
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MODULE_DEVICE_TABLE(of, vtac_of_match);
|
|
||||||
|
|
||||||
static int sti_vtac_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
struct device *dev = &pdev->dev;
|
|
||||||
struct device_node *np = dev->of_node;
|
|
||||||
const struct of_device_id *id;
|
|
||||||
struct sti_vtac *vtac;
|
|
||||||
struct resource *res;
|
|
||||||
|
|
||||||
vtac = devm_kzalloc(dev, sizeof(*vtac), GFP_KERNEL);
|
|
||||||
if (!vtac)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
vtac->dev = dev;
|
|
||||||
|
|
||||||
id = of_match_node(vtac_of_match, np);
|
|
||||||
if (!id)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
vtac->mode = id->data;
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
||||||
if (!res) {
|
|
||||||
DRM_ERROR("Invalid resource\n");
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
vtac->regs = devm_ioremap_resource(dev, res);
|
|
||||||
if (IS_ERR(vtac->regs))
|
|
||||||
return PTR_ERR(vtac->regs);
|
|
||||||
|
|
||||||
|
|
||||||
vtac->clk = devm_clk_get(dev, "vtac");
|
|
||||||
if (IS_ERR(vtac->clk)) {
|
|
||||||
DRM_ERROR("Cannot get vtac clock\n");
|
|
||||||
return PTR_ERR(vtac->clk);
|
|
||||||
}
|
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
|
||||||
if (res) {
|
|
||||||
vtac->phy_regs = devm_ioremap_nocache(dev, res->start,
|
|
||||||
resource_size(res));
|
|
||||||
sti_vtac_tx_set_config(vtac);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
sti_vtac_rx_set_config(vtac);
|
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, vtac);
|
|
||||||
DRM_INFO("%s %s\n", __func__, dev_name(vtac->dev));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sti_vtac_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct platform_driver sti_vtac_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "sti-vtac",
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
.of_match_table = vtac_of_match,
|
|
||||||
},
|
|
||||||
.probe = sti_vtac_probe,
|
|
||||||
.remove = sti_vtac_remove,
|
|
||||||
};
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
|
|
||||||
MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
|
@ -429,6 +429,10 @@ static int vtg_probe(struct platform_device *pdev)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
||||||
|
if (!vtg->regs) {
|
||||||
|
DRM_ERROR("failed to remap I/O memory\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0);
|
np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0);
|
||||||
if (np) {
|
if (np) {
|
||||||
|
|
Loading…
Reference in New Issue