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/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include "msm_drv.h"
|
|
|
|
#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));
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
2014-11-18 17:49:49 +00:00
|
|
|
unsigned long flags;
|
2014-07-07 14:34:01 +00:00
|
|
|
|
|
|
|
pm_runtime_get_sync(dev->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
|
|
|
/* 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);
|
2015-03-09 13:11:04 +00:00
|
|
|
mdp5_write(mdp5_kms, REG_MDP5_MDP_DISP_INTF_SEL(0), 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
|
|
|
|
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
|
|
|
pm_runtime_put_sync(dev->dev);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
mdp5_enable(mdp5_kms);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mdp5_complete_commit(struct msm_kms *kms, struct drm_atomic_state *state)
|
|
|
|
{
|
2015-06-19 18:03:42 +00:00
|
|
|
int i;
|
2015-01-30 22:04:45 +00:00
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2015-06-19 18:03:42 +00:00
|
|
|
int nplanes = mdp5_kms->dev->mode_config.num_total_plane;
|
|
|
|
|
|
|
|
for (i = 0; i < nplanes; i++) {
|
|
|
|
struct drm_plane *plane = state->planes[i];
|
|
|
|
struct drm_plane_state *plane_state = state->plane_states[i];
|
|
|
|
|
|
|
|
if (!plane)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
mdp5_plane_complete_commit(plane, plane_state);
|
|
|
|
}
|
|
|
|
|
2015-01-30 22:04:45 +00:00
|
|
|
mdp5_disable(mdp5_kms);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
return mdp5_encoder_set_split_display(encoder, slave_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
|
|
|
static void mdp5_preclose(struct msm_kms *kms, struct drm_file *file)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
|
|
|
struct msm_drm_private *priv = mdp5_kms->dev->dev_private;
|
|
|
|
unsigned i;
|
|
|
|
|
|
|
|
for (i = 0; i < priv->num_crtcs; i++)
|
|
|
|
mdp5_crtc_cancel_pending_flip(priv->crtcs[i], file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mdp5_destroy(struct msm_kms *kms)
|
|
|
|
{
|
|
|
|
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
|
2014-06-17 14:32:37 +00:00
|
|
|
struct msm_mmu *mmu = mdp5_kms->mmu;
|
|
|
|
|
drm/msm/mdp5: use irqdomains
For mdp5, the irqs of hdmi/eDP/dsi0/dsi1 blocks get routed through the
mdp block. In order to decouple hdmi/eDP/etc, register an irq domain
in mdp5. When hdmi/dsi/etc are used with mdp4, they can directly setup
their irqs in their DT nodes as normal. When used with mdp5, instead
set the mdp device as the interrupt-parent, as in:
mdp: qcom,mdss_mdp@fd900000 {
compatible = "qcom,mdss_mdp";
interrupt-controller;
#interrupt-cells = <1>;
...
};
hdmi: qcom,hdmi_tx@fd922100 {
compatible = "qcom,hdmi-tx-8074";
interrupt-parent = <&mdp>;
interrupts = <8 0>; /* MDP5_HW_INTR_STATUS.INTR_HDMI */
...
};
There is a slight awkwardness, in that we cannot disable child irqs
at the mdp level, they can only be cleared in the child block. So
you must not use threaded irq handlers in the child. I'm not sure
if there is a better way to deal with that.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2014-11-17 20:28:07 +00:00
|
|
|
mdp5_irq_domain_fini(mdp5_kms);
|
|
|
|
|
2014-06-17 14:32:37 +00:00
|
|
|
if (mmu) {
|
|
|
|
mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports));
|
|
|
|
mmu->funcs->destroy(mmu);
|
|
|
|
}
|
2014-11-18 19:28:43 +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);
|
2014-11-18 17:49:47 +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
|
|
|
kfree(mdp5_kms);
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
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
|
|
|
.preclose = mdp5_preclose,
|
|
|
|
.destroy = mdp5_destroy,
|
|
|
|
},
|
|
|
|
.set_irqmask = mdp5_set_irqmask,
|
|
|
|
};
|
|
|
|
|
|
|
|
int mdp5_disable(struct mdp5_kms *mdp5_kms)
|
|
|
|
{
|
|
|
|
DBG("");
|
|
|
|
|
|
|
|
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("");
|
|
|
|
|
|
|
|
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,
|
|
|
|
enum mdp5_intf_type intf_type, int intf_num,
|
2015-06-26 20:03:25 +00:00
|
|
|
enum mdp5_intf_mode intf_mode, 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;
|
|
|
|
struct mdp5_interface intf = {
|
|
|
|
.num = intf_num,
|
|
|
|
.type = intf_type,
|
2015-03-26 23:25:14 +00:00
|
|
|
.mode = intf_mode,
|
2015-03-13 19:49:34 +00:00
|
|
|
};
|
|
|
|
|
2015-03-26 23:25:17 +00:00
|
|
|
if ((intf_type == INTF_DSI) &&
|
|
|
|
(intf_mode == MDP5_INTF_DSI_MODE_COMMAND))
|
2015-06-26 20:03:25 +00:00
|
|
|
encoder = mdp5_cmd_encoder_init(dev, &intf, ctl);
|
2015-03-26 23:25:17 +00:00
|
|
|
else
|
2015-06-26 20:03:25 +00:00
|
|
|
encoder = mdp5_encoder_init(dev, &intf, ctl);
|
2015-03-26 23:25:17 +00:00
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
static int modeset_init_intf(struct mdp5_kms *mdp5_kms, int intf_num)
|
|
|
|
{
|
|
|
|
struct drm_device *dev = mdp5_kms->dev;
|
|
|
|
struct msm_drm_private *priv = dev->dev_private;
|
|
|
|
const struct mdp5_cfg_hw *hw_cfg =
|
|
|
|
mdp5_cfg_get_hw_config(mdp5_kms->cfg);
|
2015-04-30 17:45:52 +00:00
|
|
|
enum mdp5_intf_type intf_type = hw_cfg->intf.connect[intf_num];
|
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;
|
|
|
|
|
|
|
|
switch (intf_type) {
|
|
|
|
case INTF_DISABLED:
|
|
|
|
break;
|
|
|
|
case INTF_eDP:
|
|
|
|
if (!priv->edp)
|
|
|
|
break;
|
|
|
|
|
2015-06-26 20:03:25 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
encoder = construct_encoder(mdp5_kms, INTF_eDP, intf_num,
|
2015-06-26 20:03:25 +00:00
|
|
|
MDP5_INTF_MODE_NONE, 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;
|
|
|
|
|
2015-06-26 20:03:25 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
encoder = construct_encoder(mdp5_kms, INTF_HDMI, intf_num,
|
2015-06-26 20:03:25 +00:00
|
|
|
MDP5_INTF_MODE_NONE, ctl);
|
2015-03-26 23:25:14 +00:00
|
|
|
if (IS_ERR(encoder)) {
|
|
|
|
ret = PTR_ERR(encoder);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = hdmi_modeset_init(priv->hdmi, dev, encoder);
|
|
|
|
break;
|
2015-03-26 23:25:17 +00:00
|
|
|
case INTF_DSI:
|
|
|
|
{
|
|
|
|
int dsi_id = get_dsi_id_from_intf(hw_cfg, intf_num);
|
|
|
|
struct drm_encoder *dsi_encs[MSM_DSI_ENCODER_NUM];
|
|
|
|
enum mdp5_intf_mode mode;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if ((dsi_id >= ARRAY_SIZE(priv->dsi)) || (dsi_id < 0)) {
|
|
|
|
dev_err(dev->dev, "failed to find dsi from intf %d\n",
|
|
|
|
intf_num);
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!priv->dsi[dsi_id])
|
|
|
|
break;
|
|
|
|
|
2015-06-26 20:03:25 +00:00
|
|
|
ctl = mdp5_ctlm_request(ctlm, intf_num);
|
|
|
|
if (!ctl) {
|
|
|
|
ret = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:17 +00:00
|
|
|
for (i = 0; i < MSM_DSI_ENCODER_NUM; i++) {
|
|
|
|
mode = (i == MSM_DSI_CMD_ENCODER_ID) ?
|
|
|
|
MDP5_INTF_DSI_MODE_COMMAND :
|
|
|
|
MDP5_INTF_DSI_MODE_VIDEO;
|
|
|
|
dsi_encs[i] = construct_encoder(mdp5_kms, INTF_DSI,
|
2015-06-26 20:03:25 +00:00
|
|
|
intf_num, mode, ctl);
|
|
|
|
if (IS_ERR(dsi_encs[i])) {
|
|
|
|
ret = PTR_ERR(dsi_encs[i]);
|
2015-03-26 23:25:17 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = msm_dsi_modeset_init(priv->dsi[dsi_id], dev, dsi_encs);
|
|
|
|
break;
|
|
|
|
}
|
2015-03-26 23:25:14 +00:00
|
|
|
default:
|
|
|
|
dev_err(dev->dev, "unknown intf: %d\n", intf_type);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
static const enum mdp5_pipe crtcs[] = {
|
2014-07-07 14:34:01 +00:00
|
|
|
SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3,
|
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
|
|
|
};
|
2015-07-07 21:17:28 +00:00
|
|
|
static const enum mdp5_pipe vig_planes[] = {
|
2014-11-18 17:49:49 +00:00
|
|
|
SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3,
|
|
|
|
};
|
2015-07-07 21:17:28 +00:00
|
|
|
static const enum mdp5_pipe dma_planes[] = {
|
|
|
|
SSPP_DMA0, SSPP_DMA1,
|
|
|
|
};
|
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_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;
|
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
|
|
|
int i, ret;
|
|
|
|
|
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
|
|
|
|
drm/msm/mdp5: use irqdomains
For mdp5, the irqs of hdmi/eDP/dsi0/dsi1 blocks get routed through the
mdp block. In order to decouple hdmi/eDP/etc, register an irq domain
in mdp5. When hdmi/dsi/etc are used with mdp4, they can directly setup
their irqs in their DT nodes as normal. When used with mdp5, instead
set the mdp device as the interrupt-parent, as in:
mdp: qcom,mdss_mdp@fd900000 {
compatible = "qcom,mdss_mdp";
interrupt-controller;
#interrupt-cells = <1>;
...
};
hdmi: qcom,hdmi_tx@fd922100 {
compatible = "qcom,hdmi-tx-8074";
interrupt-parent = <&mdp>;
interrupts = <8 0>; /* MDP5_HW_INTR_STATUS.INTR_HDMI */
...
};
There is a slight awkwardness, in that we cannot disable child irqs
at the mdp level, they can only be cleared in the child block. So
you must not use threaded irq handlers in the child. I'm not sure
if there is a better way to deal with that.
Signed-off-by: Rob Clark <robdclark@gmail.com>
2014-11-17 20:28:07 +00:00
|
|
|
/* register our interrupt-controller for hdmi/eDP/dsi/etc
|
|
|
|
* to use for irqs routed through mdp:
|
|
|
|
*/
|
|
|
|
ret = mdp5_irq_domain_init(mdp5_kms);
|
|
|
|
if (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2014-11-18 17:49:49 +00:00
|
|
|
/* construct CRTCs and their private planes: */
|
2014-11-18 17:49:48 +00:00
|
|
|
for (i = 0; i < hw_cfg->pipe_rgb.count; 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;
|
|
|
|
struct drm_crtc *crtc;
|
|
|
|
|
2014-11-18 17:49:49 +00:00
|
|
|
plane = mdp5_plane_init(dev, crtcs[i], true,
|
2015-07-08 22:12:40 +00:00
|
|
|
hw_cfg->pipe_rgb.base[i], hw_cfg->pipe_rgb.caps);
|
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);
|
|
|
|
dev_err(dev->dev, "failed to construct plane for %s (%d)\n",
|
|
|
|
pipe2name(crtcs[i]), ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
crtc = mdp5_crtc_init(dev, plane, i);
|
|
|
|
if (IS_ERR(crtc)) {
|
|
|
|
ret = PTR_ERR(crtc);
|
|
|
|
dev_err(dev->dev, "failed to construct crtc for %s (%d)\n",
|
|
|
|
pipe2name(crtcs[i]), ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
priv->crtcs[priv->num_crtcs++] = crtc;
|
|
|
|
}
|
|
|
|
|
2015-07-07 21:17:28 +00:00
|
|
|
/* Construct video planes: */
|
2014-11-18 17:49:49 +00:00
|
|
|
for (i = 0; i < hw_cfg->pipe_vig.count; i++) {
|
|
|
|
struct drm_plane *plane;
|
|
|
|
|
2015-07-07 21:17:28 +00:00
|
|
|
plane = mdp5_plane_init(dev, vig_planes[i], false,
|
2015-07-08 22:12:40 +00:00
|
|
|
hw_cfg->pipe_vig.base[i], hw_cfg->pipe_vig.caps);
|
2014-11-18 17:49:49 +00:00
|
|
|
if (IS_ERR(plane)) {
|
|
|
|
ret = PTR_ERR(plane);
|
|
|
|
dev_err(dev->dev, "failed to construct %s plane: %d\n",
|
2015-07-07 21:17:28 +00:00
|
|
|
pipe2name(vig_planes[i]), ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DMA planes */
|
|
|
|
for (i = 0; i < hw_cfg->pipe_dma.count; i++) {
|
|
|
|
struct drm_plane *plane;
|
|
|
|
|
|
|
|
plane = mdp5_plane_init(dev, dma_planes[i], false,
|
|
|
|
hw_cfg->pipe_dma.base[i], hw_cfg->pipe_dma.caps);
|
|
|
|
if (IS_ERR(plane)) {
|
|
|
|
ret = PTR_ERR(plane);
|
|
|
|
dev_err(dev->dev, "failed to construct %s plane: %d\n",
|
|
|
|
pipe2name(dma_planes[i]), ret);
|
2014-11-18 17:49:49 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-26 23:25:14 +00:00
|
|
|
/* Construct encoders and modeset initialize connector devices
|
|
|
|
* for each external display interface.
|
|
|
|
*/
|
2015-04-30 17:45:52 +00:00
|
|
|
for (i = 0; i < ARRAY_SIZE(hw_cfg->intf.connect); i++) {
|
2015-03-26 23:25:14 +00:00
|
|
|
ret = modeset_init_intf(mdp5_kms, i);
|
2015-03-13 19:49:34 +00:00
|
|
|
if (ret)
|
2014-12-12 19:41:17 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-11-18 17:49:48 +00:00
|
|
|
static void read_hw_revision(struct mdp5_kms *mdp5_kms,
|
|
|
|
uint32_t *major, uint32_t *minor)
|
|
|
|
{
|
|
|
|
uint32_t version;
|
|
|
|
|
|
|
|
mdp5_enable(mdp5_kms);
|
2015-03-09 13:11:04 +00:00
|
|
|
version = mdp5_read(mdp5_kms, REG_MDSS_HW_VERSION);
|
2014-11-18 17:49:48 +00:00
|
|
|
mdp5_disable(mdp5_kms);
|
|
|
|
|
2015-03-09 13:11:04 +00:00
|
|
|
*major = FIELD(version, MDSS_HW_VERSION_MAJOR);
|
|
|
|
*minor = FIELD(version, MDSS_HW_VERSION_MINOR);
|
2014-11-18 17:49:48 +00:00
|
|
|
|
|
|
|
DBG("MDP5 version v%d.%d", *major, *minor);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
struct clk *clk = devm_clk_get(dev, 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct msm_kms *mdp5_kms_init(struct drm_device *dev)
|
|
|
|
{
|
|
|
|
struct platform_device *pdev = dev->platformdev;
|
2014-11-18 17:49:48 +00:00
|
|
|
struct mdp5_cfg *config;
|
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;
|
|
|
|
struct msm_kms *kms = NULL;
|
|
|
|
struct msm_mmu *mmu;
|
2014-11-18 17:49:48 +00:00
|
|
|
uint32_t major, minor;
|
2014-07-07 14:34:01 +00:00
|
|
|
int 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
|
|
|
|
|
|
|
mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL);
|
|
|
|
if (!mdp5_kms) {
|
|
|
|
dev_err(dev->dev, "failed to allocate kms\n");
|
|
|
|
ret = -ENOMEM;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2014-11-18 17:49:49 +00:00
|
|
|
spin_lock_init(&mdp5_kms->resource_lock);
|
|
|
|
|
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
|
|
|
mdp_kms_init(&mdp5_kms->base, &kms_funcs);
|
|
|
|
|
|
|
|
kms = &mdp5_kms->base.base;
|
|
|
|
|
|
|
|
mdp5_kms->dev = dev;
|
|
|
|
|
2015-03-09 13:11:04 +00:00
|
|
|
/* mdp5_kms->mmio actually represents the MDSS base address */
|
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
|
|
|
mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5");
|
|
|
|
if (IS_ERR(mdp5_kms->mmio)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->mmio);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
mdp5_kms->vbif = msm_ioremap(pdev, "vbif_phys", "VBIF");
|
|
|
|
if (IS_ERR(mdp5_kms->vbif)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->vbif);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
mdp5_kms->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
|
|
|
if (IS_ERR(mdp5_kms->vdd)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->vdd);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = regulator_enable(mdp5_kms->vdd);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev->dev, "failed to enable regulator vdd: %d\n", ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-09-15 12:41:47 +00:00
|
|
|
/* mandatory clocks: */
|
|
|
|
ret = get_clk(pdev, &mdp5_kms->axi_clk, "bus_clk", true);
|
2014-06-02 11:24:27 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2015-09-15 12:41:47 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->ahb_clk, "iface_clk", true);
|
2014-06-02 11:24:27 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2015-09-15 12:41:47 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->src_clk, "core_clk_src", true);
|
2014-06-02 11:24:27 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2015-09-15 12:41:47 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->core_clk, "core_clk", true);
|
2014-06-02 11:24:27 +00:00
|
|
|
if (ret)
|
|
|
|
goto fail;
|
2015-09-15 12:41:47 +00:00
|
|
|
ret = get_clk(pdev, &mdp5_kms->vsync_clk, "vsync_clk", true);
|
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 (ret)
|
|
|
|
goto fail;
|
|
|
|
|
2015-09-15 12:41:47 +00:00
|
|
|
/* optional clocks: */
|
|
|
|
get_clk(pdev, &mdp5_kms->lut_clk, "lut_clk", false);
|
|
|
|
|
2014-11-20 22:08:06 +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->src_clk, 200000000);
|
|
|
|
|
2014-11-18 17:49:48 +00:00
|
|
|
read_hw_revision(mdp5_kms, &major, &minor);
|
2014-11-18 19:28:43 +00:00
|
|
|
|
|
|
|
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;
|
2014-07-07 14:34:01 +00:00
|
|
|
goto fail;
|
2014-11-18 17:49:48 +00:00
|
|
|
}
|
2014-11-18 19:28:43 +00:00
|
|
|
|
|
|
|
config = mdp5_cfg_get_config(mdp5_kms->cfg);
|
2015-09-15 12:41:53 +00:00
|
|
|
mdp5_kms->caps = config->hw->mdp.caps;
|
2014-07-07 14:34:01 +00:00
|
|
|
|
2014-11-17 18:39:34 +00:00
|
|
|
/* TODO: compute core clock rate at runtime */
|
2014-11-18 17:49:48 +00:00
|
|
|
clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk);
|
2014-11-17 18:39:34 +00:00
|
|
|
|
2015-09-15 12:41:53 +00:00
|
|
|
/*
|
|
|
|
* 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) {
|
|
|
|
mdp5_kms->smp = mdp5_smp_init(mdp5_kms->dev, &config->hw->smp);
|
|
|
|
if (IS_ERR(mdp5_kms->smp)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->smp);
|
|
|
|
mdp5_kms->smp = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
2014-11-18 17:49:47 +00:00
|
|
|
}
|
|
|
|
|
2015-06-26 20:03:26 +00:00
|
|
|
mdp5_kms->ctlm = mdp5_ctlm_init(dev, mdp5_kms->mmio, mdp5_kms->cfg);
|
2014-11-18 19:28:43 +00:00
|
|
|
if (IS_ERR(mdp5_kms->ctlm)) {
|
|
|
|
ret = PTR_ERR(mdp5_kms->ctlm);
|
|
|
|
mdp5_kms->ctlm = NULL;
|
2014-11-18 17:49:49 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
/* 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):
|
|
|
|
*/
|
|
|
|
mdp5_enable(mdp5_kms);
|
2015-03-13 19:49:34 +00:00
|
|
|
for (i = 0; i < MDP5_INTF_NUM_MAX; i++) {
|
2015-04-30 17:45:52 +00:00
|
|
|
if (mdp5_cfg_intf_is_virtual(config->hw->intf.connect[i]) ||
|
|
|
|
!config->hw->intf.base[i])
|
2015-03-13 19:49:34 +00:00
|
|
|
continue;
|
2014-07-07 14:34:01 +00:00
|
|
|
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
|
2015-03-13 19:49:34 +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
|
|
|
mdp5_disable(mdp5_kms);
|
|
|
|
mdelay(16);
|
|
|
|
|
2014-11-18 17:49:48 +00:00
|
|
|
if (config->platform.iommu) {
|
|
|
|
mmu = msm_iommu_new(&pdev->dev, config->platform.iommu);
|
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(mmu)) {
|
|
|
|
ret = PTR_ERR(mmu);
|
2014-06-17 14:32:37 +00:00
|
|
|
dev_err(dev->dev, "failed to init iommu: %d\n", ret);
|
2015-09-15 12:41:46 +00:00
|
|
|
iommu_domain_free(config->platform.iommu);
|
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;
|
|
|
|
}
|
2014-06-17 14:32:37 +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
|
|
|
ret = mmu->funcs->attach(mmu, iommu_ports,
|
|
|
|
ARRAY_SIZE(iommu_ports));
|
2014-06-17 14:32:37 +00:00
|
|
|
if (ret) {
|
|
|
|
dev_err(dev->dev, "failed to attach iommu: %d\n", ret);
|
|
|
|
mmu->funcs->destroy(mmu);
|
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;
|
2014-06-17 14:32:37 +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
|
|
|
} else {
|
|
|
|
dev_info(dev->dev, "no iommu, fallback to phys "
|
|
|
|
"contig buffers for scanout\n");
|
|
|
|
mmu = NULL;
|
|
|
|
}
|
2014-06-17 14:32:37 +00:00
|
|
|
mdp5_kms->mmu = mmu;
|
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
|
|
|
|
|
|
|
mdp5_kms->id = msm_register_mmu(dev, mmu);
|
|
|
|
if (mdp5_kms->id < 0) {
|
|
|
|
ret = mdp5_kms->id;
|
|
|
|
dev_err(dev->dev, "failed to register mdp5 iommu: %d\n", ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = modeset_init(mdp5_kms);
|
|
|
|
if (ret) {
|
|
|
|
dev_err(dev->dev, "modeset_init failed: %d\n", ret);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2015-06-24 23:13:40 +00:00
|
|
|
dev->mode_config.min_width = 0;
|
|
|
|
dev->mode_config.min_height = 0;
|
|
|
|
dev->mode_config.max_width = config->hw->lm.max_width;
|
|
|
|
dev->mode_config.max_height = config->hw->lm.max_height;
|
|
|
|
|
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 kms;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
if (kms)
|
|
|
|
mdp5_destroy(kms);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|