#include <linux/of_graph.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
+#include <linux/phy/phy-mixel-mipi-dsi.h>
#include <linux/spinlock.h>
#include <video/mipi_display.h>
#include <video/videomode.h>
static const char IRQ_NAME[] = "nwl-dsi";
+/* Possible valid PHY reference clock rates*/
+static u32 phyref_rates[] = {
+ 27000000,
+ 25000000,
+ 24000000,
+};
+
enum {
CLK_PHY_REF = BIT(1),
CLK_RX_ESC = BIT(2),
bool enabled;
};
+struct mode_config {
+ int pixclock;
+ unsigned int lanes;
+ unsigned long bitclock;
+ u32 phyref_rate;
+ struct list_head list;
+};
+
struct nwl_mipi_dsi {
struct device *dev;
struct drm_panel *panel;
void __iomem *base;
int irq;
- struct videomode vm;
struct mipi_dsi_transfer *xfer;
+ struct drm_display_mode *curr_mode;
+ struct list_head valid_modes;
u32 lanes;
bool no_clk_reset;
bool enabled;
}
}
-unsigned long nwl_dsi_get_bit_clock(struct drm_bridge *bridge,
- unsigned long pixclock)
+static unsigned long nwl_dsi_get_bit_clock(struct nwl_mipi_dsi *dsi,
+ unsigned long pixclock)
{
- struct nwl_mipi_dsi *dsi;
- struct mipi_dsi_device *dsi_device;
+ struct mipi_dsi_device *dsi_device = dsi->dsi_device;
int bpp;
u32 bus_fmt;
struct drm_crtc *crtc = 0;
- /* Make sure the bridge is correctly initialized */
- if (!bridge || !bridge->driver_private)
- return 0;
-
- dsi = bridge->driver_private;
- dsi_device = dsi->dsi_device;
-
if (dsi_device->lanes < 1 || dsi_device->lanes > 4)
return 0;
bpp = mipi_dsi_pixel_format_to_bpp(dsi_device->format);
- return (pixclock / dsi->lanes) * bpp;
+ return (pixclock * bpp) / dsi_device->lanes;
}
-EXPORT_SYMBOL_GPL(nwl_dsi_get_bit_clock);
static void nwl_dsi_config_host(struct nwl_mipi_dsi *dsi)
{
{
struct device *dev = dsi->dev;
struct mipi_dsi_device *dsi_device = dsi->dsi_device;
- struct videomode *vm = &dsi->vm;
+ struct videomode vm;
enum dpi_pixel_format pixel_format =
nwl_dsi_get_dpi_pixel_format(dsi_device->format);
enum dpi_interface_color_coding color_coding =
nwl_dsi_get_dpi_interface_color_coding(dsi_device->format);
bool burst_mode;
+ drm_display_mode_to_videomode(dsi->curr_mode, &vm);
+
nwl_dsi_write(dsi, INTERFACE_COLOR_CODING, color_coding);
nwl_dsi_write(dsi, PIXEL_FORMAT, pixel_format);
DRM_DEV_DEBUG_DRIVER(dev, "DSI format is: %d (CC=%d, PF=%d)\n",
nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, 256);
} else {
nwl_dsi_write(dsi, VIDEO_MODE, 0x0);
- nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, vm->hactive);
+ nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, vm.hactive);
}
- nwl_dsi_write(dsi, HFP, vm->hfront_porch);
- nwl_dsi_write(dsi, HBP, vm->hback_porch);
- nwl_dsi_write(dsi, HSA, vm->hsync_len);
+ nwl_dsi_write(dsi, HFP, vm.hfront_porch);
+ nwl_dsi_write(dsi, HBP, vm.hback_porch);
+ nwl_dsi_write(dsi, HSA, vm.hsync_len);
nwl_dsi_write(dsi, ENABLE_MULT_PKTS, 0x0);
nwl_dsi_write(dsi, BLLP_MODE, 0x1);
nwl_dsi_write(dsi, USE_NULL_PKT_BLLP, 0x0);
nwl_dsi_write(dsi, VC, 0x0);
- nwl_dsi_write(dsi, PIXEL_PAYLOAD_SIZE, vm->hactive);
- nwl_dsi_write(dsi, VACTIVE, vm->vactive);
- nwl_dsi_write(dsi, VBP, vm->vback_porch);
- nwl_dsi_write(dsi, VFP, vm->vfront_porch);
+ nwl_dsi_write(dsi, PIXEL_PAYLOAD_SIZE, vm.hactive);
+ nwl_dsi_write(dsi, VACTIVE, vm.vactive);
+ nwl_dsi_write(dsi, VBP, vm.vback_porch);
+ nwl_dsi_write(dsi, VFP, vm.vfront_porch);
}
static void nwl_dsi_enable_clocks(struct nwl_mipi_dsi *dsi, u32 clks)
nwl_dsi_write(dsi, IRQ_MASK, irq_enable);
}
+/*
+ * This function will try the required phy speed for current mode
+ * If the phy speed can be achieved, the phy will save the speed
+ * configuration
+ */
+static struct mode_config *nwl_dsi_mode_probe(struct nwl_mipi_dsi *dsi,
+ const struct drm_display_mode *mode)
+{
+ struct device *dev = dsi->dev;
+ struct mode_config *config;
+ unsigned long pixclock = mode->clock * 1000;
+ unsigned long bit_clk = 0;
+ u32 phyref_rate = 0, lanes = dsi->lanes;
+ size_t i = 0, num_rates = ARRAY_SIZE(phyref_rates);
+ int ret = 0;
+
+ list_for_each_entry(config, &dsi->valid_modes, list)
+ if (config->pixclock == pixclock)
+ return config;
+
+ while (i < num_rates) {
+ bit_clk = nwl_dsi_get_bit_clock(dsi, pixclock);
+ phyref_rate = phyref_rates[i];
+ ret = mixel_phy_mipi_set_phy_speed(dsi->phy,
+ bit_clk,
+ phyref_rate,
+ false);
+
+ /* Pick the first non-failing rate */
+ if (!ret)
+ break;
+
+ /* Reached the end of phyref_rates, try another lane config */
+ if ((i++ == num_rates - 1) && (--lanes > 1)) {
+ i = 0;
+ continue;
+ }
+ }
+
+ if (ret < 0) {
+ DRM_DEV_DEBUG_DRIVER(dev,
+ "Cannot setup PHY for mode: %ux%u @%d kHz\n",
+ mode->hdisplay,
+ mode->vdisplay,
+ mode->clock);
+ DRM_DEV_DEBUG_DRIVER(dev, "phy_ref clk: %u, bit clk: %lu\n",
+ phyref_rate, bit_clk);
+
+ return NULL;
+ }
+
+ config = devm_kzalloc(dsi->dev, sizeof(struct mode_config), GFP_KERNEL);
+ config->pixclock = pixclock;
+ config->lanes = lanes;
+ config->bitclock = bit_clk;
+ config->phyref_rate = phyref_rate;
+ list_add(&config->list, &dsi->valid_modes);
+
+ return config;
+}
+
+static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode)
+{
+ struct nwl_mipi_dsi *dsi = bridge->driver_private;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Validating mode:");
+ drm_mode_debug_printmodeline(mode);
+
+ if (!nwl_dsi_mode_probe(dsi, mode))
+ return MODE_NOCLOCK;
+
+ return MODE_OK;
+}
+
static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+ struct drm_display_mode *adjusted)
{
struct nwl_mipi_dsi *dsi = bridge->driver_private;
- int bpp = mipi_dsi_pixel_format_to_bpp(dsi->dsi_device->format);
- unsigned long pixclock = adjusted_mode->clock * 1000;
- unsigned long data_rate;
+ struct mode_config *config;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
+ drm_mode_debug_printmodeline(adjusted);
- if (dsi->dsi_device->lanes < 1 || dsi->dsi_device->lanes > 4)
+ config = nwl_dsi_mode_probe(dsi, adjusted);
+ if (!config)
return false;
- /* Data rate is in bit clock for each lane */
- data_rate = (pixclock / dsi->lanes) * bpp;
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "lanes=%u, data_rate=%lu\n",
+ config->lanes, config->bitclock);
+ if (config->lanes < 1 || config->lanes > 4)
+ return false;
/* Max data rate for this controller is 1.5Gbps */
- if (data_rate > 1500000000)
+ if (config->bitclock > 1500000000)
return false;
return true;
struct drm_display_mode *adjusted)
{
struct nwl_mipi_dsi *dsi = bridge->driver_private;
+ struct mode_config *config;
+ u32 actual_phy_rate;
DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setting mode:\n");
drm_mode_debug_printmodeline(adjusted);
- drm_display_mode_to_videomode(adjusted, &dsi->vm);
+ config = nwl_dsi_mode_probe(dsi, adjusted);
+ /* New mode? This should NOT happen */
+ if (!config) {
+ DRM_DEV_ERROR(dsi->dev, "Unsupported mode provided:\n");
+ drm_mode_debug_printmodeline(adjusted);
+ return;
+ }
+
+ mixel_phy_mipi_set_phy_speed(dsi->phy,
+ config->bitclock,
+ config->phyref_rate,
+ false);
+ clk_set_rate(dsi->phy_ref.clk, config->phyref_rate);
+ actual_phy_rate = clk_get_rate(dsi->phy_ref.clk);
+ dsi->dsi_device->lanes = config->lanes;
+ DRM_DEV_DEBUG_DRIVER(dsi->dev,
+ "Using phy_ref rate: %u (actual: %u), "
+ "bitclock: %lu, lanes: %u\n",
+ config->phyref_rate, actual_phy_rate,
+ config->bitclock, config->lanes);
+
+ dsi->curr_mode = drm_mode_duplicate(bridge->dev, adjusted);
}
static int nwl_dsi_host_attach(struct mipi_dsi_host *host,
dsi->dsi_device = device;
dsi->lanes = device->lanes;
+ /*
+ * If this happened right before a mode_set, it means that our
+ * bridge/panel doesn't like the current mode parameters and changed
+ * something on the dsi_device (lanes or format). In this case, we have
+ * to reconfigure the phy.
+ */
+ if (dsi->curr_mode) {
+ unsigned long pixclock = dsi->curr_mode->clock * 1000;
+ struct mode_config *config;
+
+ DRM_DEV_DEBUG_DRIVER(dsi->dev, "Re-setting mode:\n");
+ drm_mode_debug_printmodeline(dsi->curr_mode);
+ drm_mode_destroy(dsi->bridge.dev, dsi->curr_mode);
+ list_for_each_entry(config, &dsi->valid_modes, list)
+ if (config->pixclock == pixclock)
+ break;
+
+ if (device->lanes != config->lanes)
+ return 0;
+
+ clk_set_rate(dsi->phy_ref.clk, config->phyref_rate);
+ device->lanes = config->lanes;
+ DRM_DEV_DEBUG_DRIVER(dsi->dev,
+ "Using phy_ref rate: %d (actual: %ld), "
+ "bitclock: %lu, lanes: %d\n",
+ config->phyref_rate, clk_get_rate(dsi->phy_ref.clk),
+ config->bitclock, config->lanes);
+ }
+
if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev);
struct nwl_mipi_dsi *dsi = container_of(connector,
struct nwl_mipi_dsi,
connector);
-
if (dsi->panel)
return drm_panel_get_modes(dsi->panel);
static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
.enable = nwl_dsi_bridge_enable,
.disable = nwl_dsi_bridge_disable,
+ .mode_valid = nwl_dsi_bridge_mode_valid,
.mode_fixup = nwl_dsi_bridge_mode_fixup,
.mode_set = nwl_dsi_bridge_mode_set,
.attach = nwl_dsi_bridge_attach,
if (ret < 0)
dev_err(dev, "Failed to add nwl-dsi bridge (%d)\n", ret);
+ INIT_LIST_HEAD(&dsi->valid_modes);
+
return ret;
}
static int nwl_dsi_remove(struct platform_device *pdev)
{
struct nwl_mipi_dsi *dsi = platform_get_drvdata(pdev);
+ struct mode_config *config;
+ struct list_head *pos, *tmp;
+
drm_bridge_remove(&dsi->bridge);
+ list_for_each_safe(pos, tmp, &dsi->valid_modes) {
+ config = list_entry(pos, struct mode_config, list);
+ list_del(pos);
+ devm_kfree(dsi->dev, config);
+ }
+
pm_runtime_disable(&pdev->dev);
return 0;
#include <linux/of.h>
#include <linux/of_graph.h>
#include <linux/of_platform.h>
-#include <linux/phy/phy-mixel-mipi-dsi.h>
#include <linux/phy/phy.h>
#include <linux/regmap.h>
#include <soc/imx8/sc/sci.h>
#define CLK_BYPASS "bypass"
#define CLK_PHYREF "phy_ref"
-/* Possible valid PHY reference clock rates*/
-u32 phyref_rates[] = {
- 24000000,
- 25000000,
- 27000000,
-};
-
struct imx_mipi_dsi {
struct drm_encoder encoder;
struct drm_bridge bridge;
u32 tx_ulps_reg;
u32 pxl2dpi_reg;
- unsigned long bit_clk;
- unsigned long pix_clk;
- u32 phyref_rate;
u32 instance;
u32 sync_pol;
u32 power_on_delay;
struct device *dev = dsi->dev;
const char *id;
struct clk *clk;
- unsigned long new_rate, cur_rate;
+ unsigned long new_rate, cur_rate, phy_rate = 0;
bool enabled;
size_t i;
+ /*
+ * PHY_REF rate should be set by the nwl-dsi bridge, depending on the
+ * current mode used. We need that rate to set the PIXEL clock for
+ * QM or QXP.
+ */
+ for (i = 0; i < dsi->clk_num; i++) {
+ if (!dsi->clk_config[i].present)
+ continue;
+ id = dsi->clk_config[i].id;
+ clk = dsi->clk_config[i].clk;
+
+ if (!strcmp(id, CLK_BYPASS) || !strcmp(id, CLK_PHYREF)) {
+ dsi->clk_config[i].rate = clk_get_rate(clk);
+ phy_rate = dsi->clk_config[i].rate;
+ break;
+ }
+ }
+
for (i = 0; i < dsi->clk_num; i++) {
if (!dsi->clk_config[i].present)
continue;
cur_rate = clk_get_rate(clk);
enabled = dsi->clk_config[i].enabled;
- /* BYPASS clk must have the same rate as PHY_REF clk */
- if (!strcmp(id, CLK_BYPASS) || !strcmp(id, CLK_PHYREF))
- new_rate = dsi->phyref_rate;
+ if (!strcmp(id, CLK_PIXEL) && phy_rate)
+ new_rate = phy_rate;
if (enable) {
if (enabled && new_rate != cur_rate)
const struct of_device_id *of_id = of_match_device(imx_nwl_dsi_dt_ids,
dev);
const struct devtype *devtype = of_id->data;
- unsigned long bit_clk, min_sleep, max_sleep;
+ unsigned long min_sleep, max_sleep;
int ret;
if (dsi->enabled)
DRM_DEV_DEBUG_DRIVER(dev, "id = %s\n", (dsi->instance)?"DSI1":"DSI0");
- /*
- * TODO: we are doing this here, because the ADV7535 which is a drm
- * bridge, may change the DSI parameters in mode_set. One of the
- * changed parameter is DSI lanes, which affects the PHY settings.
- * This is why, we need run this function again, here, in order
- * to correctly set-up the PHY. Since we can't do anything here, we
- * will ignore it's status.
- * In the future, maybe it will be best to move the PHY handling
- * into the DSI host driver.
- */
- bit_clk = nwl_dsi_get_bit_clock(dsi->next_bridge, dsi->pix_clk);
- if (bit_clk != dsi->bit_clk) {
- mixel_phy_mipi_set_phy_speed(dsi->phy,
- bit_clk,
- dsi->phyref_rate,
- false);
- dsi->bit_clk = bit_clk;
- }
-
/*
* On some systems we need to wait some time before enabling the
* phy_ref clock, in order to allow the parent PLL to become stable
dsi->enabled = false;
}
-/*
- * This function will try the required phy speed for current mode
- * If the phy speed can be achieved, the phy will save the speed
- * configuration
- */
-static int imx_nwl_try_phy_speed(struct imx_mipi_dsi *dsi,
- struct drm_display_mode *mode)
-{
- struct device *dev = dsi->dev;
- unsigned long pixclock;
- unsigned long bit_clk;
- size_t i, num_rates = ARRAY_SIZE(phyref_rates);
- int ret = 0;
-
- pixclock = mode->clock * 1000;
- /*
- * DSI host should know the required bit clock, since it has info
- * about bits-per-pixel and number of lanes from DSI device
- */
- bit_clk = nwl_dsi_get_bit_clock(dsi->next_bridge, pixclock);
-
- /* If bit_clk is the same with current, we're good */
- if (bit_clk == dsi->bit_clk)
- return 0;
-
- for (i = 0; i < num_rates; i++) {
- dsi->phyref_rate = phyref_rates[i];
- DRM_DEV_DEBUG_DRIVER(dev, "Trying PHY ref rate: %u\n",
- dsi->phyref_rate);
- ret = mixel_phy_mipi_set_phy_speed(dsi->phy,
- bit_clk,
- dsi->phyref_rate,
- false);
- /* Pick the first non-failing rate */
- if (!ret)
- break;
- }
- if (ret < 0) {
- DRM_DEV_ERROR(dev,
- "Cannot setup PHY for mode: %ux%u @%d kHz\n",
- mode->hdisplay,
- mode->vdisplay,
- mode->clock);
- DRM_DEV_ERROR(dev, "PHY_REF clk: %u, bit clk: %lu\n",
- dsi->phyref_rate, bit_clk);
- } else {
- dsi->bit_clk = bit_clk;
- dsi->pix_clk = pixclock;
- }
-
- return ret;
-}
-
static void imx_nwl_dsi_encoder_enable(struct drm_encoder *encoder)
{
struct imx_mipi_dsi *dsi = encoder_to_dsi(encoder);
*flags |= DRM_MODE_FLAG_NVSYNC;
}
- return !imx_nwl_try_phy_speed(dsi, mode);
+ return true;
}
static int imx_nwl_dsi_encoder_atomic_check(struct drm_encoder *encoder,