drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
/*
|
2014-11-18 17:49:48 +00:00
|
|
|
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
* Copyright (C) 2013 Red Hat
|
|
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
|
|
* the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along with
|
|
|
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2016-06-14 12:53:52 +00:00
|
|
|
#include <linux/of_irq.h>
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
|
|
|
#include "msm_drv.h"
|
2016-09-28 23:58:32 +00:00
|
|
|
#include "msm_gem.h"
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
#include "msm_mmu.h"
|
|
|
|
#include "mdp5_kms.h"
|
|
|
|
|
2014-06-17 14:32:37 +00:00
|
|
|
static const char *iommu_ports[] = {
|
|
|
|
"mdp_0",
|
|
|
|
};
|
|
|
|
|
2014-07-07 14:34:01 +00:00
|
|
|
static int mdp5_hw_init(struct msm_kms *kms)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2017-07-28 10:47:01 +00:00
|
|
|
struct device *dev = &mdp5_kms->pdev->dev;
|
2014-11-18 17:49:49 +00:00
|
|
|
unsigned long flags;
|
2014-07-07 14:34:01 +00:00
|
|
|
|
2017-07-28 10:47:01 +00:00
|
|
|
pm_runtime_get_sync(dev);
|
2014-07-07 14:34:01 +00:00
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
/* Magic unknown register writes:
|
|
|
|
*
|
|
|
|
* W VBIF:0x004 00000001 (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x2e0 0xe9 (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x2e4 0x55 (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x3ac 0xc0000ccc (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x3b4 0xc0000ccc (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x3bc 0xcccccc (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x4a8 0xcccc0c0 (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x4b0 0xccccc0c0 (mdss_mdp.c:839)
|
|
|
|
* W MDP5:0x4b8 0xccccc000 (mdss_mdp.c:839)
|
|
|
|
*
|
|
|
|
* Downstream fbdev driver gets these register offsets/values
|
|
|
|
* from DT.. not really sure what these registers are or if
|
|
|
|
* different values for different boards/SoC's, etc. I guess
|
|
|
|
* they are the golden registers.
|
|
|
|
*
|
|
|
|
* Not setting these does not seem to cause any problem. But
|
|
|
|
* we may be getting lucky with the bootloader initializing
|
|
|
|
* them for us. OTOH, if we can always count on the bootloader
|
|
|
|
* setting the golden registers, then perhaps we don't need to
|
|
|
|
* care.
|
|
|
|
*/
|
|
|
|
|
2014-11-18 17:49:49 +00:00
|
|
|
spin_lock_irqsave(&mdp5_kms->resource_lock, flags);
|
2016-05-10 05:35:58 +00:00
|
|
|
mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0);
|
2014-11-18 17:49:49 +00:00
|
|
|
spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
2014-11-18 19:28:43 +00:00
|
|
|
mdp5_ctlm_hw_reset(mdp5_kms->ctlm);
|
2014-07-07 14:34:01 +00:00
|
|
|
|
2017-07-28 10:47:01 +00:00
|
|
|
pm_runtime_put_sync(dev);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
2014-07-07 14:34:01 +00:00
|
|
|
return 0;
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
}
|
|
|
|
|
2018-02-21 14:37:22 +00:00
|
|
|
/* Global/shared object state funcs */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a helper that returns the private state currently in operation.
|
|
|
|
* Note that this would return the "old_state" if called in the atomic check
|
|
|
|
* path, and the "new_state" after the atomic swap has been done.
|
|
|
|
*/
|
|
|
|
struct mdp5_global_state *
|
|
|
|
mdp5_get_existing_global_state(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
return to_mdp5_global_state(mdp5_kms->glob_state.state);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This acquires the modeset lock set aside for global state, creates
|
|
|
|
* a new duplicated private object state.
|
|
|
|
*/
|
|
|
|
struct mdp5_global_state *mdp5_get_global_state(struct drm_atomic_state *s)
|
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = s->dev->dev_private;
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
|
|
|
|
struct drm_private_state *priv_state;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = drm_modeset_lock(&mdp5_kms->glob_state_lock, s->acquire_ctx);
|
|
|
|
if (ret)
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
|
|
|
|
priv_state = drm_atomic_get_private_obj_state(s, &mdp5_kms->glob_state);
|
|
|
|
if (IS_ERR(priv_state))
|
|
|
|
return ERR_CAST(priv_state);
|
|
|
|
|
|
|
|
return to_mdp5_global_state(priv_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_private_state *
|
|
|
|
mdp5_global_duplicate_state(struct drm_private_obj *obj)
|
|
|
|
{
|
|
|
|
struct mdp5_global_state *state;
|
|
|
|
|
|
|
|
state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
|
|
|
|
if (!state)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
|
|
|
|
|
|
|
|
return &state->base;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mdp5_global_destroy_state(struct drm_private_obj *obj,
|
|
|
|
struct drm_private_state *state)
|
|
|
|
{
|
|
|
|
struct mdp5_global_state *mdp5_state = to_mdp5_global_state(state);
|
|
|
|
|
|
|
|
kfree(mdp5_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct drm_private_state_funcs mdp5_global_state_funcs = {
|
|
|
|
.atomic_duplicate_state = mdp5_global_duplicate_state,
|
|
|
|
.atomic_destroy_state = mdp5_global_destroy_state,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mdp5_global_obj_init(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
struct mdp5_global_state *state;
|
|
|
|
|
|
|
|
drm_modeset_lock_init(&mdp5_kms->glob_state_lock);
|
|
|
|
|
|
|
|
state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
|
|
if (!state)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
state->mdp5_kms = mdp5_kms;
|
|
|
|
|
2018-10-22 12:31:22 +00:00
|
|
|
drm_atomic_private_obj_init(mdp5_kms->dev, &mdp5_kms->glob_state,
|
2018-02-21 14:37:22 +00:00
|
|
|
&state->base,
|
|
|
|
&mdp5_global_state_funcs);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-30 22:04:45 +00:00
|
|
|
static void mdp5_prepare_commit(struct msm_kms *kms, struct drm_atomic_state *state)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2017-07-28 10:47:01 +00:00
|
|
|
struct device *dev = &mdp5_kms->pdev->dev;
|
2018-02-21 14:37:23 +00:00
|
|
|
struct mdp5_global_state *global_state;
|
|
|
|
|
|
|
|
global_state = mdp5_get_existing_global_state(mdp5_kms);
|
2016-11-01 20:35:32 +00:00
|
|
|
|
2017-07-28 10:47:01 +00:00
|
|
|
pm_runtime_get_sync(dev);
|
2016-11-01 20:35:32 +00:00
|
|
|
|
|
|
|
if (mdp5_kms->smp)
|
2018-02-21 14:37:23 +00:00
|
|
|
mdp5_smp_prepare_commit(mdp5_kms->smp, &global_state->smp);
|
2015-01-30 22:04:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2017-07-28 10:47:01 +00:00
|
|
|
struct device *dev = &mdp5_kms->pdev->dev;
|
2018-02-21 14:37:23 +00:00
|
|
|
struct mdp5_global_state *global_state;
|
|
|
|
|
2018-04-13 14:09:11 +00:00
|
|
|
drm_atomic_helper_wait_for_vblanks(mdp5_kms->dev, state);
|
|
|
|
|
2018-02-21 14:37:23 +00:00
|
|
|
global_state = mdp5_get_existing_global_state(mdp5_kms);
|
2015-06-19 18:03:42 +00:00
|
|
|
|
2016-11-01 20:35:32 +00:00
|
|
|
if (mdp5_kms->smp)
|
2018-02-21 14:37:23 +00:00
|
|
|
mdp5_smp_complete_commit(mdp5_kms->smp, &global_state->smp);
|
2016-11-01 20:35:32 +00:00
|
|
|
|
2017-10-20 12:17:43 +00:00
|
|
|
pm_runtime_put_sync(dev);
|
2015-01-30 22:04:45 +00:00
|
|
|
}
|
|
|
|
|
2015-04-28 23:35:37 +00:00
|
|
|
static void mdp5_wait_for_crtc_commit_done(struct msm_kms *kms,
|
|
|
|
struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
mdp5_crtc_wait_for_commit_done(crtc);
|
|
|
|
}
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
static long mdp5_round_pixclk(struct msm_kms *kms, unsigned long rate,
|
|
|
|
struct drm_encoder *encoder)
|
|
|
|
{
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:17 +00:00
|
|
|
static int mdp5_set_split_display(struct msm_kms *kms,
|
|
|
|
struct drm_encoder *encoder,
|
|
|
|
struct drm_encoder *slave_encoder,
|
|
|
|
bool is_cmd_mode)
|
|
|
|
{
|
|
|
|
if (is_cmd_mode)
|
|
|
|
return mdp5_cmd_encoder_set_split_display(encoder,
|
|
|
|
slave_encoder);
|
|
|
|
else
|
2016-12-06 03:51:21 +00:00
|
|
|
return mdp5_vid_encoder_set_split_display(encoder,
|
|
|
|
slave_encoder);
|
2015-03-26 23:25:17 +00:00
|
|
|
}
|
|
|
|
|
2016-12-05 09:54:53 +00:00
|
|
|
static void mdp5_set_encoder_mode(struct msm_kms *kms,
|
|
|
|
struct drm_encoder *encoder,
|
|
|
|
bool cmd_mode)
|
|
|
|
{
|
|
|
|
mdp5_encoder_set_intf_mode(encoder, cmd_mode);
|
|
|
|
}
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
static void mdp5_kms_destroy(struct msm_kms *kms)
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2017-06-13 14:22:37 +00:00
|
|
|
struct msm_gem_address_space *aspace = kms->aspace;
|
2016-11-01 13:56:51 +00:00
|
|
|
int i;
|
|
|
|
|
2017-03-23 10:27:56 +00:00
|
|
|
for (i = 0; i < mdp5_kms->num_hwmixers; i++)
|
|
|
|
mdp5_mixer_destroy(mdp5_kms->hwmixers[i]);
|
|
|
|
|
2016-11-01 13:56:51 +00:00
|
|
|
for (i = 0; i < mdp5_kms->num_hwpipes; i++)
|
|
|
|
mdp5_pipe_destroy(mdp5_kms->hwpipes[i]);
|
2014-06-17 14:32:37 +00:00
|
|
|
|
2016-09-28 23:58:32 +00:00
|
|
|
if (aspace) {
|
|
|
|
aspace->mmu->funcs->detach(aspace->mmu,
|
|
|
|
iommu_ports, ARRAY_SIZE(iommu_ports));
|
2017-03-07 17:02:52 +00:00
|
|
|
msm_gem_address_space_put(aspace);
|
2014-06-17 14:32:37 +00:00
|
|
|
}
|
2016-06-14 12:53:52 +00:00
|
|
|
}
|
|
|
|
|
2016-10-26 18:06:55 +00:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
static int smp_show(struct seq_file *m, void *arg)
|
|
|
|
{
|
|
|
|
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
|
|
|
struct drm_device *dev = node->minor->dev;
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
|
|
|
|
struct drm_printer p = drm_seq_file_printer(m);
|
|
|
|
|
|
|
|
if (!mdp5_kms->smp) {
|
|
|
|
drm_printf(&p, "no SMP pool\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mdp5_smp_dump(mdp5_kms->smp, &p);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct drm_info_list mdp5_debugfs_list[] = {
|
|
|
|
{"smp", smp_show },
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mdp5_kms_debugfs_init(struct msm_kms *kms, struct drm_minor *minor)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = minor->dev;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = drm_debugfs_create_files(mdp5_debugfs_list,
|
|
|
|
ARRAY_SIZE(mdp5_debugfs_list),
|
|
|
|
minor->debugfs_root, minor);
|
|
|
|
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev->dev, "could not install mdp5_debugfs_list\n");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
static const struct mdp_kms_funcs kms_funcs = {
|
|
|
|
.base = {
|
|
|
|
.hw_init = mdp5_hw_init,
|
|
|
|
.irq_preinstall = mdp5_irq_preinstall,
|
|
|
|
.irq_postinstall = mdp5_irq_postinstall,
|
|
|
|
.irq_uninstall = mdp5_irq_uninstall,
|
|
|
|
.irq = mdp5_irq,
|
|
|
|
.enable_vblank = mdp5_enable_vblank,
|
|
|
|
.disable_vblank = mdp5_disable_vblank,
|
2015-01-30 22:04:45 +00:00
|
|
|
.prepare_commit = mdp5_prepare_commit,
|
|
|
|
.complete_commit = mdp5_complete_commit,
|
2015-04-28 23:35:37 +00:00
|
|
|
.wait_for_crtc_commit_done = mdp5_wait_for_crtc_commit_done,
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
.get_format = mdp_get_format,
|
|
|
|
.round_pixclk = mdp5_round_pixclk,
|
2015-03-26 23:25:17 +00:00
|
|
|
.set_split_display = mdp5_set_split_display,
|
2016-12-05 09:54:53 +00:00
|
|
|
.set_encoder_mode = mdp5_set_encoder_mode,
|
2016-06-14 12:54:54 +00:00
|
|
|
.destroy = mdp5_kms_destroy,
|
2016-10-26 18:06:55 +00:00
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
|
|
.debugfs_init = mdp5_kms_debugfs_init,
|
|
|
|
#endif
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
},
|
|
|
|
.set_irqmask = mdp5_set_irqmask,
|
|
|
|
};
|
|
|
|
|
|
|
|
int mdp5_disable(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
|
2017-07-03 17:13:57 +00:00
|
|
|
mdp5_kms->enable_count--;
|
|
|
|
WARN_ON(mdp5_kms->enable_count < 0);
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
clk_disable_unprepare(mdp5_kms->ahb_clk);
|
|
|
|
clk_disable_unprepare(mdp5_kms->axi_clk);
|
|
|
|
clk_disable_unprepare(mdp5_kms->core_clk);
|
2015-06-19 20:04:47 +00:00
|
|
|
if (mdp5_kms->lut_clk)
|
|
|
|
clk_disable_unprepare(mdp5_kms->lut_clk);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mdp5_enable(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
|
2017-07-03 17:13:57 +00:00
|
|
|
mdp5_kms->enable_count++;
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
clk_prepare_enable(mdp5_kms->ahb_clk);
|
|
|
|
clk_prepare_enable(mdp5_kms->axi_clk);
|
|
|
|
clk_prepare_enable(mdp5_kms->core_clk);
|
2015-06-19 20:04:47 +00:00
|
|
|
if (mdp5_kms->lut_clk)
|
|
|
|
clk_prepare_enable(mdp5_kms->lut_clk);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
static struct drm_encoder *construct_encoder(struct mdp5_kms *mdp5_kms,
|
2017-03-23 10:27:59 +00:00
|
|
|
struct mdp5_interface *intf,
|
|
|
|
struct mdp5_ctl *ctl)
|
2015-03-13 19:49:34 +00:00
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct drm_encoder *encoder;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
encoder = mdp5_encoder_init(dev, intf, ctl);
|
2015-03-13 19:49:34 +00:00
|
|
|
if (IS_ERR(encoder)) {
|
2015-03-26 23:25:14 +00:00
|
|
|
dev_err(dev->dev, "failed to construct encoder\n");
|
|
|
|
return encoder;
|
2015-03-13 19:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
priv->encoders[priv->num_encoders++] = encoder;
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
return encoder;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:17 +00:00
|
|
|
static int get_dsi_id_from_intf(const struct mdp5_cfg_hw *hw_cfg, int intf_num)
|
|
|
|
{
|
2015-04-30 17:45:52 +00:00
|
|
|
const enum mdp5_intf_type *intfs = hw_cfg->intf.connect;
|
|
|
|
const int intf_cnt = ARRAY_SIZE(hw_cfg->intf.connect);
|
2015-03-26 23:25:17 +00:00
|
|
|
int id = 0, i;
|
|
|
|
|
|
|
|
for (i = 0; i < intf_cnt; i++) {
|
|
|
|
if (intfs[i] == INTF_DSI) {
|
|
|
|
if (intf_num == i)
|
|
|
|
return id;
|
|
|
|
|
|
|
|
id++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
static int modeset_init_intf(struct mdp5_kms *mdp5_kms,
|
|
|
|
struct mdp5_interface *intf)
|
2015-03-26 23:25:14 +00:00
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
2015-06-26 20:03:25 +00:00
|
|
|
struct mdp5_ctl_manager *ctlm = mdp5_kms->ctlm;
|
|
|
|
struct mdp5_ctl *ctl;
|
2015-03-26 23:25:14 +00:00
|
|
|
struct drm_encoder *encoder;
|
|
|
|
int ret = 0;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
switch (intf->type) {
|
2015-03-26 23:25:14 +00:00
|
|
|
case INTF_eDP:
|
|
|
|
if (!priv->edp)
|
|
|
|
break;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
2015-06-26 20:03:25 +00:00
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
2015-03-26 23:25:14 +00:00
|
|
|
if (IS_ERR(encoder)) {
|
|
|
|
ret = PTR_ERR(encoder);
|
|
|
|
break;
|
|
|
|
}
|
2015-03-13 19:49:34 +00:00
|
|
|
|
|
|
|
ret = msm_edp_modeset_init(priv->edp, dev, encoder);
|
2015-03-26 23:25:14 +00:00
|
|
|
break;
|
|
|
|
case INTF_HDMI:
|
|
|
|
if (!priv->hdmi)
|
|
|
|
break;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
2015-06-26 20:03:25 +00:00
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
2015-03-26 23:25:14 +00:00
|
|
|
if (IS_ERR(encoder)) {
|
|
|
|
ret = PTR_ERR(encoder);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-02-22 21:08:35 +00:00
|
|
|
ret = msm_hdmi_modeset_init(priv->hdmi, dev, encoder);
|
2015-03-26 23:25:14 +00:00
|
|
|
break;
|
2015-03-26 23:25:17 +00:00
|
|
|
case INTF_DSI:
|
|
|
|
{
|
2017-03-23 10:27:59 +00:00
|
|
|
const struct mdp5_cfg_hw *hw_cfg =
|
|
|
|
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
|
|
|
int dsi_id = get_dsi_id_from_intf(hw_cfg, intf->num);
|
2015-03-26 23:25:17 +00:00
|
|
|
|
|
|
|
if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) {
|
|
|
|
dev_err(dev->dev, "failed to find dsi from intf %d\n",
|
2017-03-23 10:27:59 +00:00
|
|
|
intf->num);
|
2015-03-26 23:25:17 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->dsi[dsi_id])
|
|
|
|
break;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf->num);
|
2015-06-26 20:03:25 +00:00
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
encoder = construct_encoder(mdp5_kms, intf, ctl);
|
2017-01-16 04:12:03 +00:00
|
|
|
if (IS_ERR(encoder)) {
|
|
|
|
ret = PTR_ERR(encoder);
|
|
|
|
break;
|
2015-03-26 23:25:17 +00:00
|
|
|
}
|
|
|
|
|
2017-01-16 04:12:03 +00:00
|
|
|
ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, encoder);
|
2015-03-26 23:25:17 +00:00
|
|
|
break;
|
|
|
|
}
|
2015-03-26 23:25:14 +00:00
|
|
|
default:
|
2017-03-23 10:27:59 +00:00
|
|
|
dev_err(dev->dev, "unknown intf: %d\n", intf->type);
|
2015-03-26 23:25:14 +00:00
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
2015-03-13 19:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
static int modeset_init(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
2014-11-18 17:49:48 +00:00
|
|
|
const struct mdp5_cfg_hw *hw_cfg;
|
2016-12-06 06:45:54 +00:00
|
|
|
unsigned int num_crtcs;
|
2016-12-16 06:30:30 +00:00
|
|
|
int i, ret, pi = 0, ci = 0;
|
|
|
|
struct drm_plane *primary[MAX_BASES] = { NULL };
|
|
|
|
struct drm_plane *cursor[MAX_BASES] = { NULL };
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
2014-11-18 19:28:43 +00:00
|
|
|
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
2014-11-18 17:49:48 +00:00
|
|
|
|
2016-12-06 06:45:54 +00:00
|
|
|
/*
|
|
|
|
* Construct encoders and modeset initialize connector devices
|
|
|
|
* for each external display interface.
|
|
|
|
*/
|
2017-03-23 10:27:59 +00:00
|
|
|
for (i = 0; i < mdp5_kms->num_intfs; i++) {
|
|
|
|
ret = modeset_init_intf(mdp5_kms, mdp5_kms->intfs[i]);
|
2016-12-06 06:45:54 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We should ideally have less number of encoders (set up by parsing
|
|
|
|
* the MDP5 interfaces) than the number of layer mixers present in HW,
|
|
|
|
* but let's be safe here anyway
|
|
|
|
*/
|
2017-03-23 10:27:57 +00:00
|
|
|
num_crtcs = min(priv->num_encoders, mdp5_kms->num_hwmixers);
|
2016-12-06 06:45:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct planes equaling the number of hw pipes, and CRTCs for the
|
|
|
|
* N encoders set up by the driver. The first N planes become primary
|
2016-11-01 13:56:51 +00:00
|
|
|
* planes for the CRTCs, with the remainder as overlay planes:
|
|
|
|
*/
|
|
|
|
for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
|
2016-12-16 06:30:30 +00:00
|
|
|
struct mdp5_hw_pipe *hwpipe = mdp5_kms->hwpipes[i];
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
struct drm_plane *plane;
|
2017-01-16 06:27:04 +00:00
|
|
|
enum drm_plane_type type;
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
2016-12-16 06:30:30 +00:00
|
|
|
if (i < num_crtcs)
|
2017-01-16 06:27:04 +00:00
|
|
|
type = DRM_PLANE_TYPE_PRIMARY;
|
2016-12-16 06:30:30 +00:00
|
|
|
else if (hwpipe->caps & MDP_PIPE_CAP_CURSOR)
|
|
|
|
type = DRM_PLANE_TYPE_CURSOR;
|
2017-01-16 06:27:04 +00:00
|
|
|
else
|
|
|
|
type = DRM_PLANE_TYPE_OVERLAY;
|
|
|
|
|
|
|
|
plane = mdp5_plane_init(dev, type);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
if (IS_ERR(plane)) {
|
|
|
|
ret = PTR_ERR(plane);
|
2016-11-01 13:56:51 +00:00
|
|
|
dev_err(dev->dev, "failed to construct plane %d (%d)\n", i, ret);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
2016-10-26 18:06:55 +00:00
|
|
|
priv->planes[priv->num_planes++] = plane;
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
|
2016-12-16 06:30:30 +00:00
|
|
|
if (type == DRM_PLANE_TYPE_PRIMARY)
|
|
|
|
primary[pi++] = plane;
|
|
|
|
if (type == DRM_PLANE_TYPE_CURSOR)
|
|
|
|
cursor[ci++] = plane;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < num_crtcs; i++) {
|
|
|
|
struct drm_crtc *crtc;
|
2016-11-01 13:56:51 +00:00
|
|
|
|
2016-12-16 06:30:30 +00:00
|
|
|
crtc = mdp5_crtc_init(dev, primary[i], cursor[i], i);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
if (IS_ERR(crtc)) {
|
|
|
|
ret = PTR_ERR(crtc);
|
2016-11-01 13:56:51 +00:00
|
|
|
dev_err(dev->dev, "failed to construct crtc %d (%d)\n", i, ret);
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
priv->crtcs[priv->num_crtcs++] = crtc;
|
|
|
|
}
|
|
|
|
|
2016-12-06 06:45:54 +00:00
|
|
|
/*
|
|
|
|
* Now that we know the number of crtcs we've created, set the possible
|
|
|
|
* crtcs for the encoders
|
2015-03-26 23:25:14 +00:00
|
|
|
*/
|
2016-12-06 06:45:54 +00:00
|
|
|
for (i = 0; i < priv->num_encoders; i++) {
|
|
|
|
struct drm_encoder *encoder = priv->encoders[i];
|
|
|
|
|
|
|
|
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
2014-12-12 19:41:17 +00:00
|
|
|
}
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
static void read_mdp_hw_revision(struct mdp5_kms *mdp5_kms,
|
|
|
|
u32 *major, u32 *minor)
|
|
|
|
{
|
2017-07-28 10:47:01 +00:00
|
|
|
struct device *dev = &mdp5_kms->pdev->dev;
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
u32 version;
|
|
|
|
|
2017-07-28 10:47:01 +00:00
|
|
|
pm_runtime_get_sync(dev);
|
2016-05-10 05:35:58 +00:00
|
|
|
version = mdp5_read(mdp5_kms, REG_MDP5_HW_VERSION);
|
2017-10-20 12:17:43 +00:00
|
|
|
pm_runtime_put_sync(dev);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
|
2016-05-10 05:35:58 +00:00
|
|
|
*major = FIELD(version, MDP5_HW_VERSION_MAJOR);
|
|
|
|
*minor = FIELD(version, MDP5_HW_VERSION_MINOR);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
|
2017-10-16 16:52:48 +00:00
|
|
|
dev_info(dev, "MDP5 version v%d.%d", *major, *minor);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
static int get_clk(struct platform_device *pdev, struct clk **clkp,
|
2015-09-15 12:41:47 +00:00
|
|
|
const char *name, bool mandatory)
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
{
|
|
|
|
struct device *dev = &pdev->dev;
|
2017-07-28 10:47:00 +00:00
|
|
|
struct clk *clk = msm_clk_get(pdev, name);
|
2015-09-15 12:41:47 +00:00
|
|
|
if (IS_ERR(clk) && mandatory) {
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
dev_err(dev, "failed to get %s (%ld)\n", name, PTR_ERR(clk));
|
|
|
|
return PTR_ERR(clk);
|
|
|
|
}
|
2015-09-15 12:41:47 +00:00
|
|
|
if (IS_ERR(clk))
|
|
|
|
DBG("skipping %s", name);
|
|
|
|
else
|
|
|
|
*clkp = clk;
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-10-26 09:13:57 +00:00
|
|
|
static struct drm_encoder *get_encoder_from_crtc(struct drm_crtc *crtc)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = crtc->dev;
|
|
|
|
struct drm_encoder *encoder;
|
|
|
|
|
|
|
|
drm_for_each_encoder(encoder, dev)
|
|
|
|
if (encoder->crtc == crtc)
|
|
|
|
return encoder;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 14:03:28 +00:00
|
|
|
static bool mdp5_get_scanoutpos(struct drm_device *dev, unsigned int pipe,
|
|
|
|
bool in_vblank_irq, int *vpos, int *hpos,
|
|
|
|
ktime_t *stime, ktime_t *etime,
|
|
|
|
const struct drm_display_mode *mode)
|
2015-10-26 09:13:57 +00:00
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
struct drm_encoder *encoder;
|
|
|
|
int line, vsw, vbp, vactive_start, vactive_end, vfp_end;
|
|
|
|
|
|
|
|
crtc = priv->crtcs[pipe];
|
|
|
|
if (!crtc) {
|
|
|
|
DRM_ERROR("Invalid crtc %d\n", pipe);
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 14:03:28 +00:00
|
|
|
return false;
|
2015-10-26 09:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
encoder = get_encoder_from_crtc(crtc);
|
|
|
|
if (!encoder) {
|
|
|
|
DRM_ERROR("no encoder found for crtc %d\n", pipe);
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 14:03:28 +00:00
|
|
|
return false;
|
2015-10-26 09:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vsw = mode->crtc_vsync_end - mode->crtc_vsync_start;
|
|
|
|
vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* the line counter is 1 at the start of the VSYNC pulse and VTOTAL at
|
|
|
|
* the end of VFP. Translate the porch values relative to the line
|
|
|
|
* counter positions.
|
|
|
|
*/
|
|
|
|
|
|
|
|
vactive_start = vsw + vbp + 1;
|
|
|
|
|
|
|
|
vactive_end = vactive_start + mode->crtc_vdisplay;
|
|
|
|
|
|
|
|
/* last scan line before VSYNC */
|
|
|
|
vfp_end = mode->crtc_vtotal;
|
|
|
|
|
|
|
|
if (stime)
|
|
|
|
*stime = ktime_get();
|
|
|
|
|
|
|
|
line = mdp5_encoder_get_linecount(encoder);
|
|
|
|
|
|
|
|
if (line < vactive_start) {
|
|
|
|
line -= vactive_start;
|
|
|
|
} else if (line > vactive_end) {
|
|
|
|
line = line - vfp_end - vactive_start;
|
|
|
|
} else {
|
|
|
|
line -= vactive_start;
|
|
|
|
}
|
|
|
|
|
|
|
|
*vpos = line;
|
|
|
|
*hpos = 0;
|
|
|
|
|
|
|
|
if (etime)
|
|
|
|
*etime = ktime_get();
|
|
|
|
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 14:03:28 +00:00
|
|
|
return true;
|
2015-10-26 09:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static u32 mdp5_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
|
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
struct drm_encoder *encoder;
|
|
|
|
|
2017-10-10 18:42:07 +00:00
|
|
|
if (pipe >= priv->num_crtcs)
|
2015-10-26 09:13:57 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
crtc = priv->crtcs[pipe];
|
|
|
|
if (!crtc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
encoder = get_encoder_from_crtc(crtc);
|
|
|
|
if (!encoder)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return mdp5_encoder_get_framecount(encoder);
|
|
|
|
}
|
|
|
|
|
drm/msm: add mdp5/apq8x74
Add support for the new MDP5 display controller block. The mapping
between parts of the display controller and KMS is:
plane -> PIPE{RGBn,VIGn} \
crtc -> LM (layer mixer) |-> MDP "device"
encoder -> INTF /
connector -> HDMI/DSI/eDP/etc --> other device(s)
Unlike MDP4, it appears we can get by with a single encoder, rather
than needing a different implementation for DTV, DSI, etc. (Ie. the
register interface is same, just different bases.)
Also unlike MDP4, all the IRQs for other blocks (HDMI, DSI, etc) are
routed through MDP.
And finally, MDP5 has this "Shared Memory Pool" (called "SMP"), from
which blocks need to be allocated to the active pipes based on fetch
stride.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2013-11-30 22:51:47 +00:00
|
|
|
struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
2016-06-14 12:53:52 +00:00
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct platform_device *pdev;
|
|
|
|
struct mdp5_kms *mdp5_kms;
|
|
|
|
struct mdp5_cfg *config;
|
|
|
|
struct msm_kms *kms;
|
2016-09-28 23:58:32 +00:00
|
|
|
struct msm_gem_address_space *aspace;
|
2016-06-14 12:53:52 +00:00
|
|
|
int irq, i, ret;
|
|
|
|
|
|
|
|
/* priv->kms would have been populated by the MDP5 driver */
|
|
|
|
kms = priv->kms;
|
|
|
|
if (!kms)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
|
|
|
|
|
|
|
mdp_kms_init(&mdp5_kms->base, &kms_funcs);
|
|
|
|
|
|
|
|
pdev = mdp5_kms->pdev;
|
|
|
|
|
|
|
|
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
|
|
|
|
if (irq < 0) {
|
|
|
|
ret = irq;
|
|
|
|
dev_err(&pdev->dev, "failed to get irq: %d\n", ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
kms->irq = irq;
|
|
|
|
|
|
|
|
config = mdp5_cfg_get_config(mdp5_kms->cfg);
|
|
|
|
|
|
|
|
/* make sure things are off before attaching iommu (bootloader could
|
|
|
|
* have left things on, in which case we'll start getting faults if
|
|
|
|
* we don't disable):
|
|
|
|
*/
|
2017-07-28 10:47:01 +00:00
|
|
|
pm_runtime_get_sync(&pdev->dev);
|
2016-06-14 12:53:52 +00:00
|
|
|
for (i = 0; i < MDP5_INTF_NUM_MAX; i++) {
|
|
|
|
if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) ||
|
|
|
|
!config->hw->intf.base[i])
|
|
|
|
continue;
|
|
|
|
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
|
|
|
|
|
|
|
|
mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(i), 0x3);
|
|
|
|
}
|
|
|
|
mdelay(16);
|
|
|
|
|
|
|
|
if (config->platform.iommu) {
|
2016-09-28 23:58:32 +00:00
|
|
|
aspace = msm_gem_address_space_create(&pdev->dev,
|
|
|
|
config->platform.iommu, "mdp5");
|
|
|
|
if (IS_ERR(aspace)) {
|
|
|
|
ret = PTR_ERR(aspace);
|
2016-06-14 12:53:52 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2017-06-13 14:22:37 +00:00
|
|
|
kms->aspace = aspace;
|
2016-09-28 23:58:32 +00:00
|
|
|
|
|
|
|
ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports,
|
2016-06-14 12:53:52 +00:00
|
|
|
ARRAY_SIZE(iommu_ports));
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&pdev->dev, "failed to attach iommu: %d\n",
|
|
|
|
ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dev_info(&pdev->dev,
|
|
|
|
"no iommu, fallback to phys contig buffers for scanout\n");
|
2018-01-17 18:55:47 +00:00
|
|
|
aspace = NULL;
|
2016-06-14 12:53:52 +00:00
|
|
|
}
|
|
|
|
|
2017-10-20 12:17:43 +00:00
|
|
|
pm_runtime_put_sync(&pdev->dev);
|
2017-07-28 10:47:01 +00:00
|
|
|
|
2016-06-14 12:53:52 +00:00
|
|
|
ret = modeset_init(mdp5_kms);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(&pdev->dev, "modeset_init failed: %d\n", ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev->mode_config.min_width = 0;
|
|
|
|
dev->mode_config.min_height = 0;
|
2016-11-05 14:43:55 +00:00
|
|
|
dev->mode_config.max_width = 0xffff;
|
|
|
|
dev->mode_config.max_height = 0xffff;
|
2016-06-14 12:53:52 +00:00
|
|
|
|
drm/vblank: drop the mode argument from drm_calc_vbltimestamp_from_scanoutpos
If we restrict this helper to only kms drivers (which is the case) we
can look up the correct mode easily ourselves. But it's a bit tricky:
- All legacy drivers look at crtc->hwmode. But that is updated already
at the beginning of the modeset helper, which means when we disable
a pipe. Hence the final timestamps might be a bit off. But since
this is an existing bug I'm not going to change it, but just try to
be bug-for-bug compatible with the current code. This only applies
to radeon&amdgpu.
- i915 tries to get it perfect by updating crtc->hwmode when the pipe
is off (i.e. vblank->enabled = false).
- All other atomic drivers look at crtc->state->adjusted_mode. Those
that look at state->requested_mode simply don't adjust their mode,
so it's the same. That has two problems: Accessing crtc->state from
interrupt handling code is unsafe, and it's updated before we shut
down the pipe. For nonblocking modesets it's even worse.
For atomic drivers try to implement what i915 does. To do that we add
a new hwmode field to the vblank structure, and update it from
drm_calc_timestamping_constants(). For atomic drivers that's called
from the right spot by the helper library already, so all fine. But
for safety let's enforce that.
For legacy driver this function is only called at the end (oh the
fun), which is broken, so again let's not bother and just stay
bug-for-bug compatible.
The benefit is that we can use drm_calc_vbltimestamp_from_scanoutpos
directly to implement ->get_vblank_timestamp in every driver, deleting
a lot of code.
v2: Completely new approach, trying to mimick the i915 solution.
v3: Fixup kerneldoc.
v4: Drop the WARN_ON to check that the vblank is off, atomic helpers
currently unconditionally call this. Recomputing the same stuff should
be harmless.
v5: Fix typos and move misplaced hunks to the right patches (Neil).
v6: Undo hunk movement (kbuild).
Cc: Mario Kleiner <mario.kleiner@tuebingen.mpg.de>
Cc: Eric Anholt <eric@anholt.net>
Cc: Rob Clark <robdclark@gmail.com>
Cc: linux-arm-msm@vger.kernel.org
Cc: freedreno@lists.freedesktop.org
Cc: Alex Deucher <alexander.deucher@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Ben Skeggs <bskeggs@redhat.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Acked-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/20170509140329.24114-4-daniel.vetter@ffwll.ch
2017-05-09 14:03:28 +00:00
|
|
|
dev->driver->get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos;
|
2016-06-14 12:53:52 +00:00
|
|
|
dev->driver->get_scanout_position = mdp5_get_scanoutpos;
|
|
|
|
dev->driver->get_vblank_counter = mdp5_get_vblank_counter;
|
|
|
|
dev->max_vblank_count = 0xffffffff;
|
|
|
|
dev->vblank_disable_immediate = true;
|
|
|
|
|
|
|
|
return kms;
|
|
|
|
fail:
|
|
|
|
if (kms)
|
2016-06-14 12:54:54 +00:00
|
|
|
mdp5_kms_destroy(kms);
|
2016-06-14 12:53:52 +00:00
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
static void mdp5_destroy(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev);
|
2017-03-23 10:27:59 +00:00
|
|
|
int i;
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
|
|
|
|
if (mdp5_kms->ctlm)
|
|
|
|
mdp5_ctlm_destroy(mdp5_kms->ctlm);
|
|
|
|
if (mdp5_kms->smp)
|
|
|
|
mdp5_smp_destroy(mdp5_kms->smp);
|
|
|
|
if (mdp5_kms->cfg)
|
|
|
|
mdp5_cfg_destroy(mdp5_kms->cfg);
|
2016-06-15 12:34:31 +00:00
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
for (i = 0; i < mdp5_kms->num_intfs; i++)
|
|
|
|
kfree(mdp5_kms->intfs[i]);
|
|
|
|
|
2016-06-15 12:34:31 +00:00
|
|
|
if (mdp5_kms->rpm_enabled)
|
|
|
|
pm_runtime_disable(&pdev->dev);
|
2016-11-04 18:50:08 +00:00
|
|
|
|
2018-02-21 14:37:22 +00:00
|
|
|
drm_atomic_private_obj_fini(&mdp5_kms->glob_state);
|
|
|
|
drm_modeset_lock_fini(&mdp5_kms->glob_state_lock);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
}
|
|
|
|
|
2016-11-01 13:56:51 +00:00
|
|
|
static int construct_pipes(struct mdp5_kms *mdp5_kms, int cnt,
|
|
|
|
const enum mdp5_pipe *pipes, const uint32_t *offsets,
|
|
|
|
uint32_t caps)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
struct mdp5_hw_pipe *hwpipe;
|
|
|
|
|
|
|
|
hwpipe = mdp5_pipe_init(pipes[i], offsets[i], caps);
|
|
|
|
if (IS_ERR(hwpipe)) {
|
|
|
|
ret = PTR_ERR(hwpipe);
|
|
|
|
dev_err(dev->dev, "failed to construct pipe for %s (%d)\n",
|
|
|
|
pipe2name(pipes[i]), ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
hwpipe->idx = mdp5_kms->num_hwpipes;
|
|
|
|
mdp5_kms->hwpipes[mdp5_kms->num_hwpipes++] = hwpipe;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int hwpipe_init(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
static const enum mdp5_pipe rgb_planes[] = {
|
|
|
|
SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3,
|
|
|
|
};
|
|
|
|
static const enum mdp5_pipe vig_planes[] = {
|
|
|
|
SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3,
|
|
|
|
};
|
|
|
|
static const enum mdp5_pipe dma_planes[] = {
|
|
|
|
SSPP_DMA0, SSPP_DMA1,
|
|
|
|
};
|
2016-12-16 06:30:30 +00:00
|
|
|
static const enum mdp5_pipe cursor_planes[] = {
|
|
|
|
SSPP_CURSOR0, SSPP_CURSOR1,
|
|
|
|
};
|
2016-11-01 13:56:51 +00:00
|
|
|
const struct mdp5_cfg_hw *hw_cfg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
|
|
|
|
|
|
|
/* Construct RGB pipes: */
|
|
|
|
ret = construct_pipes(mdp5_kms, hw_cfg->pipe_rgb.count, rgb_planes,
|
|
|
|
hw_cfg->pipe_rgb.base, hw_cfg->pipe_rgb.caps);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Construct video (VIG) pipes: */
|
|
|
|
ret = construct_pipes(mdp5_kms, hw_cfg->pipe_vig.count, vig_planes,
|
|
|
|
hw_cfg->pipe_vig.base, hw_cfg->pipe_vig.caps);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Construct DMA pipes: */
|
|
|
|
ret = construct_pipes(mdp5_kms, hw_cfg->pipe_dma.count, dma_planes,
|
|
|
|
hw_cfg->pipe_dma.base, hw_cfg->pipe_dma.caps);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-12-16 06:30:30 +00:00
|
|
|
/* Construct cursor pipes: */
|
|
|
|
ret = construct_pipes(mdp5_kms, hw_cfg->pipe_cursor.count,
|
|
|
|
cursor_planes, hw_cfg->pipe_cursor.base,
|
|
|
|
hw_cfg->pipe_cursor.caps);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2016-11-01 13:56:51 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:56 +00:00
|
|
|
static int hwmixer_init(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
const struct mdp5_cfg_hw *hw_cfg;
|
|
|
|
int i, ret;
|
|
|
|
|
|
|
|
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
|
|
|
|
|
|
|
for (i = 0; i < hw_cfg->lm.count; i++) {
|
|
|
|
struct mdp5_hw_mixer *mixer;
|
|
|
|
|
|
|
|
mixer = mdp5_mixer_init(&hw_cfg->lm.instances[i]);
|
|
|
|
if (IS_ERR(mixer)) {
|
|
|
|
ret = PTR_ERR(mixer);
|
|
|
|
dev_err(dev->dev, "failed to construct LM%d (%d)\n",
|
|
|
|
i, ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
mixer->idx = mdp5_kms->num_hwmixers;
|
|
|
|
mdp5_kms->hwmixers[mdp5_kms->num_hwmixers++] = mixer;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
static int interface_init(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
const struct mdp5_cfg_hw *hw_cfg;
|
|
|
|
const enum mdp5_intf_type *intf_types;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
|
|
|
intf_types = hw_cfg->intf.connect;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) {
|
|
|
|
struct mdp5_interface *intf;
|
|
|
|
|
|
|
|
if (intf_types[i] == INTF_DISABLED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
intf = kzalloc(sizeof(*intf), GFP_KERNEL);
|
|
|
|
if (!intf) {
|
|
|
|
dev_err(dev->dev, "failed to construct INTF%d\n", i);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
intf->num = i;
|
|
|
|
intf->type = intf_types[i];
|
|
|
|
intf->mode = MDP5_INTF_MODE_NONE;
|
|
|
|
intf->idx = mdp5_kms->num_intfs;
|
|
|
|
mdp5_kms->intfs[mdp5_kms->num_intfs++] = intf;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
struct mdp5_kms *mdp5_kms;
|
|
|
|
struct mdp5_cfg *config;
|
|
|
|
u32 major, minor;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
mdp5_kms = devm_kzalloc(&pdev->dev, sizeof(*mdp5_kms), GFP_KERNEL);
|
|
|
|
if (!mdp5_kms) {
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
platform_set_drvdata(pdev, mdp5_kms);
|
|
|
|
|
|
|
|
spin_lock_init(&mdp5_kms->resource_lock);
|
|
|
|
|
|
|
|
mdp5_kms->dev = dev;
|
|
|
|
mdp5_kms->pdev = pdev;
|
|
|
|
|
2018-02-21 14:37:22 +00:00
|
|
|
ret = mdp5_global_obj_init(mdp5_kms);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5");
|
|
|
|
if (IS_ERR(mdp5_kms->mmio)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->mmio);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mandatory clocks: */
|
2017-07-28 10:47:00 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus", true);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2017-07-28 10:47:00 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface", true);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2017-07-28 10:47:00 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->core_clk, "core", true);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2017-07-28 10:47:00 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync", true);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* optional clocks: */
|
2017-07-28 10:47:00 +00:00
|
|
|
get_clk(pdev, &mdp5_kms->lut_clk, "lut", false);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
|
|
|
|
/* we need to set a default rate before enabling. Set a safe
|
|
|
|
* rate first, then figure out hw revision, and then set a
|
|
|
|
* more optimal rate:
|
|
|
|
*/
|
|
|
|
clk_set_rate(mdp5_kms->core_clk, 200000000);
|
|
|
|
|
2016-06-15 12:34:31 +00:00
|
|
|
pm_runtime_enable(&pdev->dev);
|
|
|
|
mdp5_kms->rpm_enabled = true;
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
read_mdp_hw_revision(mdp5_kms, &major, &minor);
|
|
|
|
|
|
|
|
mdp5_kms->cfg = mdp5_cfg_init(mdp5_kms, major, minor);
|
|
|
|
if (IS_ERR(mdp5_kms->cfg)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->cfg);
|
|
|
|
mdp5_kms->cfg = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
config = mdp5_cfg_get_config(mdp5_kms->cfg);
|
|
|
|
mdp5_kms->caps = config->hw->mdp.caps;
|
|
|
|
|
|
|
|
/* TODO: compute core clock rate at runtime */
|
|
|
|
clk_set_rate(mdp5_kms->core_clk, config->hw->max_clk);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some chipsets have a Shared Memory Pool (SMP), while others
|
|
|
|
* have dedicated latency buffering per source pipe instead;
|
|
|
|
* this section initializes the SMP:
|
|
|
|
*/
|
|
|
|
if (mdp5_kms->caps & MDP_CAP_SMP) {
|
2016-11-01 20:35:32 +00:00
|
|
|
mdp5_kms->smp = mdp5_smp_init(mdp5_kms, &config->hw->smp);
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
if (IS_ERR(mdp5_kms->smp)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->smp);
|
|
|
|
mdp5_kms->smp = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, mdp5_kms->cfg);
|
|
|
|
if (IS_ERR(mdp5_kms->ctlm)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->ctlm);
|
|
|
|
mdp5_kms->ctlm = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2016-11-01 13:56:51 +00:00
|
|
|
ret = hwpipe_init(mdp5_kms);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2017-03-23 10:27:56 +00:00
|
|
|
ret = hwmixer_init(mdp5_kms);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2017-03-23 10:27:59 +00:00
|
|
|
ret = interface_init(mdp5_kms);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
/* set uninit-ed kms */
|
|
|
|
priv->kms = &mdp5_kms->base.base;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
mdp5_destroy(pdev);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mdp5_bind(struct device *dev, struct device *master, void *data)
|
|
|
|
{
|
|
|
|
struct drm_device *ddev = dev_get_drvdata(master);
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
|
|
|
|
DBG("");
|
|
|
|
|
|
|
|
return mdp5_init(pdev, ddev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mdp5_unbind(struct device *dev, struct device *master,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
|
|
|
|
mdp5_destroy(pdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct component_ops mdp5_ops = {
|
|
|
|
.bind = mdp5_bind,
|
|
|
|
.unbind = mdp5_unbind,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int mdp5_dev_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
return component_add(&pdev->dev, &mdp5_ops);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mdp5_dev_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
component_del(&pdev->dev, &mdp5_ops);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-08-03 11:50:47 +00:00
|
|
|
static __maybe_unused int mdp5_runtime_suspend(struct device *dev)
|
2017-07-28 10:47:01 +00:00
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
DBG("");
|
|
|
|
|
|
|
|
return mdp5_disable(mdp5_kms);
|
|
|
|
}
|
|
|
|
|
2017-08-03 11:50:47 +00:00
|
|
|
static __maybe_unused int mdp5_runtime_resume(struct device *dev)
|
2017-07-28 10:47:01 +00:00
|
|
|
{
|
|
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
|
|
struct mdp5_kms *mdp5_kms = platform_get_drvdata(pdev);
|
|
|
|
|
|
|
|
DBG("");
|
|
|
|
|
|
|
|
return mdp5_enable(mdp5_kms);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct dev_pm_ops mdp5_pm_ops = {
|
|
|
|
SET_RUNTIME_PM_OPS(mdp5_runtime_suspend, mdp5_runtime_resume, NULL)
|
|
|
|
};
|
|
|
|
|
2016-05-30 11:32:00 +00:00
|
|
|
static const struct of_device_id mdp5_dt_match[] = {
|
|
|
|
{ .compatible = "qcom,mdp5", },
|
|
|
|
/* to support downstream DT files */
|
|
|
|
{ .compatible = "qcom,mdss_mdp", },
|
|
|
|
{}
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, mdp5_dt_match);
|
|
|
|
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
static struct platform_driver mdp5_driver = {
|
|
|
|
.probe = mdp5_dev_probe,
|
|
|
|
.remove = mdp5_dev_remove,
|
|
|
|
.driver = {
|
|
|
|
.name = "msm_mdp",
|
2016-05-30 11:32:00 +00:00
|
|
|
.of_match_table = mdp5_dt_match,
|
2017-07-28 10:47:01 +00:00
|
|
|
.pm = &mdp5_pm_ops,
|
drm/msm/mdp5: Create a separate MDP5 device
In order to have a tree-like device hierarchy between MDSS and its
sub-blocks (MDP5, DSI, HDMI, eDP etc), we need to create a separate
device/driver for MDP5. Currently, MDP5 and MDSS are squashed
together are are tied to the top level platform_device, which is
also the one used to create drm_device.
The mdp5_kms_init code is split into two parts. The part where device
resources are allocated are associated with the MDP5 driver's probe,
the rest is executed later when we initialize modeset.
With this change, unlike MDP4, the MDP5 platform_device isn't tied to
the top level drm_device anymore. The top level drm_device is now
associated with a platform device that corresponds to MDSS wrapper
hardware.
Create mdp5_init/destroy funcs that will be used by the MDP5 driver
probe/remove. Use the HW_VERSION register in the MDP5 register address
space. Both the MDSS and MDP VERSION registers give out identical
version info.
The older mdp5_kms_init code is left as is for now, this would be removed
later when we have all the pieces to support the new device hierarchy.
Signed-off-by: Archit Taneja <architt@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
2016-05-30 11:06:50 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
void __init msm_mdp_register(void)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
platform_driver_register(&mdp5_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
void __exit msm_mdp_unregister(void)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
platform_driver_unregister(&mdp5_driver);
|
|
|
|
}
|