diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2023-04-19 08:59:06 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2023-04-19 08:59:06 +0200 |
commit | 8dd99807baf0b15b74e68e4e66d93bb0fe597621 (patch) | |
tree | b67bf286ffceb440dd5634371d47e63d4f5ec969 /drivers | |
parent | c46996d29b49b9d7fc33aa4ae59ea90596da068f (diff) | |
parent | 378c97f9138ab4ec2e1257b4cc70072b459a5361 (diff) | |
download | barebox-8dd99807baf0b15b74e68e4e66d93bb0fe597621.tar.gz |
Merge branch 'for-next/misc'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/resource.c | 6 | ||||
-rw-r--r-- | drivers/firmware/zynqmp-fpga.c | 7 | ||||
-rw-r--r-- | drivers/mci/arasan-sdhci.c | 44 | ||||
-rw-r--r-- | drivers/mci/atmel-sdhci-common.c | 28 | ||||
-rw-r--r-- | drivers/mci/rockchip-dwcmshc-sdhci.c | 49 | ||||
-rw-r--r-- | drivers/mci/sdhci.c | 27 | ||||
-rw-r--r-- | drivers/mci/sdhci.h | 1 | ||||
-rw-r--r-- | drivers/net/designware_eqos.c | 13 | ||||
-rw-r--r-- | drivers/net/dsa.c | 4 | ||||
-rw-r--r-- | drivers/net/rtl8169.c | 9 | ||||
-rw-r--r-- | drivers/net/sja1105.c | 29 | ||||
-rw-r--r-- | drivers/of/base.c | 16 | ||||
-rw-r--r-- | drivers/spi/stm32_spi.c | 50 | ||||
-rw-r--r-- | drivers/usb/dwc3/host.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 8 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 6 | ||||
-rw-r--r-- | drivers/usb/misc/onboard_usb_hub.h | 19 | ||||
-rw-r--r-- | drivers/video/Kconfig | 12 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/fb.c | 6 | ||||
-rw-r--r-- | drivers/video/fbconsole.c | 115 | ||||
-rw-r--r-- | drivers/video/mipi_dbi.c | 282 | ||||
-rw-r--r-- | drivers/video/of_display_timing.c | 22 | ||||
-rw-r--r-- | drivers/video/panel-mipi-dbi.c | 331 |
24 files changed, 920 insertions, 173 deletions
diff --git a/drivers/base/resource.c b/drivers/base/resource.c index 3725c79eb9..0d6f200a9d 100644 --- a/drivers/base/resource.c +++ b/drivers/base/resource.c @@ -57,13 +57,15 @@ int device_add_resource(struct device *dev, const char *resname, return device_add_resources(dev, &res, 1); } -struct device *add_generic_device(const char* devname, int id, const char *resname, +struct device *add_child_device(struct device *parent, + const char* devname, int id, const char *resname, resource_size_t start, resource_size_t size, unsigned int flags, void *pdata) { struct device *dev; dev = device_alloc(devname, id); + dev->parent = parent; dev->platform_data = pdata; device_add_resource(dev, resname, start, size, flags); @@ -71,7 +73,7 @@ struct device *add_generic_device(const char* devname, int id, const char *resna return dev; } -EXPORT_SYMBOL(add_generic_device); +EXPORT_SYMBOL(add_child_device); struct device *add_generic_device_res(const char* devname, int id, struct resource *res, int nb, void *pdata) diff --git a/drivers/firmware/zynqmp-fpga.c b/drivers/firmware/zynqmp-fpga.c index 2544d015d6..fe3a7df6d3 100644 --- a/drivers/firmware/zynqmp-fpga.c +++ b/drivers/firmware/zynqmp-fpga.c @@ -197,6 +197,7 @@ static void zynqmp_fpga_show_header(const struct device *dev, static int fpgamgr_program_finish(struct firmware_handler *fh) { struct fpgamgr *mgr = container_of(fh, struct fpgamgr, fh); + struct device *hw_dev = mgr->dev.parent; u32 *buf_aligned; u32 buf_size; u32 *body; @@ -254,9 +255,9 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) memcpy((u32 *)buf_aligned, body, body_length); buf_aligned[body_length / sizeof(*buf_aligned)] = body_length; - addr = dma_map_single(&mgr->dev, buf_aligned, + addr = dma_map_single(hw_dev, buf_aligned, body_length + sizeof(buf_size), DMA_TO_DEVICE); - if (dma_mapping_error(&mgr->dev, addr)) { + if (dma_mapping_error(hw_dev, addr)) { status = -EFAULT; goto err_free_dma; } @@ -267,7 +268,7 @@ static int fpgamgr_program_finish(struct firmware_handler *fh) buf_size = addr + body_length; status = mgr->eemi_ops->fpga_load((u64)addr, buf_size, flags); - dma_unmap_single(&mgr->dev, addr, body_length + sizeof(buf_size), + dma_unmap_single(hw_dev, addr, body_length + sizeof(buf_size), DMA_TO_DEVICE); if (status < 0) dev_err(&mgr->dev, "unable to load fpga\n"); diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c index 00a8ceed68..0b839ee7dc 100644 --- a/drivers/mci/arasan-sdhci.c +++ b/drivers/mci/arasan-sdhci.c @@ -73,14 +73,11 @@ static int arasan_sdhci_card_write_protected(struct mci_host *mci) static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask) { - sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask); + int ret; - /* wait for reset completion */ - if (wait_on_timeout(100 * MSECOND, - !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))) { - dev_err(host->mci.hw_dev, "SDHCI reset timeout\n"); - return -ETIMEDOUT; - } + ret = sdhci_reset(&host->sdhci, mask); + if (ret) + return ret; if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) { u8 ctrl; @@ -133,33 +130,6 @@ static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios) sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); } -static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask) -{ - u64 start = get_time_ns(); - u32 stat; - - do { - stat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS); - - if (stat & SDHCI_INT_TIMEOUT) - return -ETIMEDOUT; - - if (stat & SDHCI_INT_ERROR) { - dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n", - stat); - return -EPERM; - } - - if (is_timeout(start, 1000 * MSECOND)) { - dev_err(host->mci.hw_dev, - "SDHCI timeout while waiting for done\n"); - return -ETIMEDOUT; - } - } while ((stat & mask) != mask); - - return 0; -} - static void print_error(struct arasan_sdhci_host *host, int cmdidx, int ret) { if (ret == -ETIMEDOUT) @@ -213,7 +183,7 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg); sdhci_write16(&host->sdhci, SDHCI_COMMAND, command); - ret = arasan_sdhci_wait_for_done(host, mask); + ret = sdhci_wait_for_done(&host->sdhci, mask); if (ret) goto error; @@ -226,8 +196,8 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, error: if (ret) { print_error(host, cmd->cmdidx, ret); - arasan_sdhci_reset(host, BIT(1)); // SDHCI_RESET_CMD - arasan_sdhci_reset(host, BIT(2)); // SDHCI_RESET_DATA + arasan_sdhci_reset(host, SDHCI_RESET_CMD); + arasan_sdhci_reset(host, SDHCI_RESET_DATA); } sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c index 05a019beb6..58ba0b9b3d 100644 --- a/drivers/mci/atmel-sdhci-common.c +++ b/drivers/mci/atmel-sdhci-common.c @@ -89,32 +89,6 @@ exit: return is_inserted; } -static int at91_sdhci_wait_for_done(struct at91_sdhci *host, u32 mask) -{ - struct sdhci *sdhci = &host->sdhci; - u32 status; - int ret; - - ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, status, - (status & mask) == mask || (status & SDHCI_INT_ERROR), - USEC_PER_SEC); - - if (ret < 0) { - dev_err(host->dev, "SDHCI timeout while waiting for done\n"); - return ret; - } - - if (status & SDHCI_INT_TIMEOUT) - return -ETIMEDOUT; - - if (status & SDHCI_INT_ERROR) { - dev_err(host->dev, "SDHCI_INT_STATUS: 0x%08x\n", status); - return -EPERM; - } - - return status & 0xFFFF; -} - int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd, struct mci_data *data) { @@ -158,7 +132,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd, sdhci_write32(sdhci, SDHCI_ARGUMENT, cmd->cmdarg); sdhci_write16(sdhci, SDHCI_COMMAND, command); - status = at91_sdhci_wait_for_done(host, mask); + status = sdhci_wait_for_done(&host->sdhci, mask); if (status < 0) goto error; diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c index 4b4e8b7bd6..e1eb4fc788 100644 --- a/drivers/mci/rockchip-dwcmshc-sdhci.c +++ b/drivers/mci/rockchip-dwcmshc-sdhci.c @@ -87,26 +87,12 @@ static int rk_sdhci_card_present(struct mci_host *mci) return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT); } -static int rk_sdhci_reset(struct rk_sdhci_host *host, u8 mask) -{ - sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask); - - /* wait for reset completion */ - if (wait_on_timeout(100 * MSECOND, - !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){ - dev_err(host->mci.hw_dev, "SDHCI reset timeout\n"); - return -ETIMEDOUT; - } - - return 0; -} - static int rk_sdhci_init(struct mci_host *mci, struct device *dev) { struct rk_sdhci_host *host = to_rk_sdhci_host(mci); int ret; - ret = rk_sdhci_reset(host, SDHCI_RESET_ALL); + ret = sdhci_reset(&host->sdhci, SDHCI_RESET_ALL); if (ret) return ret; @@ -216,29 +202,6 @@ static void rk_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios) sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val); } -static int rk_sdhci_wait_for_done(struct rk_sdhci_host *host, u32 mask) -{ - u64 start = get_time_ns(); - u16 stat; - - do { - stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS); - if (stat & SDHCI_INT_ERROR) { - dev_dbg(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n", - sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS)); - return -EPERM; - } - - if (is_timeout(start, 1000 * MSECOND)) { - dev_err(host->mci.hw_dev, - "SDHCI timeout while waiting for done\n"); - return -ETIMEDOUT; - } - } while ((stat & mask) != mask); - - return 0; -} - static void print_error(struct rk_sdhci_host *host, int cmdidx) { dev_dbg(host->mci.hw_dev, @@ -285,11 +248,9 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg); sdhci_write16(&host->sdhci, SDHCI_COMMAND, command); - ret = rk_sdhci_wait_for_done(host, SDHCI_INT_CMD_COMPLETE); - if (ret == -EPERM) + ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE); + if (ret) goto error; - else if (ret) - return ret; sdhci_read_response(&host->sdhci, cmd); sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE); @@ -299,8 +260,8 @@ static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, error: if (ret) { print_error(host, cmd->cmdidx); - rk_sdhci_reset(host, BIT(1)); /* SDHCI_RESET_CMD */ - rk_sdhci_reset(host, BIT(2)); /* SDHCI_RESET_DATA */ + sdhci_reset(&host->sdhci, SDHCI_RESET_CMD); + sdhci_reset(&host->sdhci, SDHCI_RESET_DATA); } sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0); diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c index 2cdd3c3c8f..635884e2a2 100644 --- a/drivers/mci/sdhci.c +++ b/drivers/mci/sdhci.c @@ -124,6 +124,33 @@ void sdhci_set_bus_width(struct sdhci *host, int width) #endif +int sdhci_wait_for_done(struct sdhci *sdhci, u32 mask) +{ + u64 start = get_time_ns(); + u32 stat; + + do { + stat = sdhci_read32(sdhci, SDHCI_INT_STATUS); + + if (stat & SDHCI_INT_TIMEOUT) + return -ETIMEDOUT; + + if (stat & SDHCI_INT_ERROR) { + dev_err(sdhci->mci->hw_dev, "SDHCI_INT_ERROR: 0x%08x\n", + stat); + return -EPERM; + } + + if (is_timeout(start, 1000 * MSECOND)) { + dev_err(sdhci->mci->hw_dev, + "SDHCI timeout while waiting for done\n"); + return -ETIMEDOUT; + } + } while ((stat & mask) != mask); + + return 0; +} + void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data) { if (!data) diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h index c538385939..fe8c25cb9c 100644 --- a/drivers/mci/sdhci.h +++ b/drivers/mci/sdhci.h @@ -257,6 +257,7 @@ static inline void sdhci_write8(struct sdhci *host, int reg, u32 val) } #define SDHCI_NO_DMA DMA_ERROR_CODE +int sdhci_wait_for_done(struct sdhci *host, u32 mask); void sdhci_read_response(struct sdhci *host, struct mci_cmd *cmd); void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data, bool dma, u32 *command, diff --git a/drivers/net/designware_eqos.c b/drivers/net/designware_eqos.c index ee5a10a007..2e2a1cf8bf 100644 --- a/drivers/net/designware_eqos.c +++ b/drivers/net/designware_eqos.c @@ -687,7 +687,6 @@ static void eqos_stop(struct eth_device *edev) static int eqos_send(struct eth_device *edev, void *packet, int length) { struct eqos *eqos = edev->priv; - struct device *dev = &eqos->netdev.dev; struct eqos_desc *tx_desc; dma_addr_t dma; u32 des3; @@ -697,8 +696,8 @@ static int eqos_send(struct eth_device *edev, void *packet, int length) eqos->tx_currdescnum++; eqos->tx_currdescnum %= EQOS_DESCRIPTORS_TX; - dma = dma_map_single(dev, packet, length, DMA_TO_DEVICE); - if (dma_mapping_error(dev, dma)) + dma = dma_map_single(edev->parent, packet, length, DMA_TO_DEVICE); + if (dma_mapping_error(edev->parent, dma)) return -EFAULT; tx_desc->des0 = (unsigned long)dma; @@ -717,7 +716,7 @@ static int eqos_send(struct eth_device *edev, void *packet, int length) !(des3 & EQOS_DESC3_OWN), 100 * USEC_PER_MSEC); - dma_unmap_single(dev, dma, length, DMA_TO_DEVICE); + dma_unmap_single(edev->parent, dma, length, DMA_TO_DEVICE); if (ret == -ETIMEDOUT) eqos_dbg(eqos, "TX timeout\n"); @@ -764,7 +763,7 @@ static int eqos_recv(struct eth_device *edev) static int eqos_init_resources(struct eqos *eqos) { - struct device *dev = eqos->netdev.parent; + struct eth_device *edev = &eqos->netdev; int ret = -ENOMEM; void *descs; void *p; @@ -785,8 +784,8 @@ static int eqos_init_resources(struct eqos *eqos) struct eqos_desc *rx_desc = &eqos->rx_descs[i]; dma_addr_t dma; - dma = dma_map_single(dev, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE); - if (dma_mapping_error(dev, dma)) { + dma = dma_map_single(edev->parent, p, EQOS_MAX_PACKET_SIZE, DMA_FROM_DEVICE); + if (dma_mapping_error(edev->parent, dma)) { ret = -EFAULT; goto err_free_rx_bufs; } diff --git a/drivers/net/dsa.c b/drivers/net/dsa.c index 6a3b829e15..ccd7d87550 100644 --- a/drivers/net/dsa.c +++ b/drivers/net/dsa.c @@ -104,8 +104,8 @@ static int dsa_port_start(struct eth_device *edev) return ret; } - ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index, NULL, 0, - interface); + ret = phy_device_connect(edev, ds->slave_mii_bus, dp->index, + ops->adjust_link, 0, interface); if (ret) return ret; diff --git a/drivers/net/rtl8169.c b/drivers/net/rtl8169.c index ffc4ef238b..cbcd065980 100644 --- a/drivers/net/rtl8169.c +++ b/drivers/net/rtl8169.c @@ -216,6 +216,7 @@ static void __set_rx_mode(struct rtl8169_priv *priv) static void rtl8169_init_ring(struct rtl8169_priv *priv) { + struct eth_device *edev = &priv->edev; int i; priv->cur_rx = priv->cur_tx = 0; @@ -223,13 +224,13 @@ static void rtl8169_init_ring(struct rtl8169_priv *priv) priv->tx_desc = dma_alloc_coherent(NUM_TX_DESC * sizeof(struct bufdesc), &priv->tx_desc_phys); priv->tx_buf = malloc(NUM_TX_DESC * PKT_BUF_SIZE); - priv->tx_buf_phys = dma_map_single(&priv->edev.dev, priv->tx_buf, + priv->tx_buf_phys = dma_map_single(edev->parent, priv->tx_buf, NUM_TX_DESC * PKT_BUF_SIZE, DMA_TO_DEVICE); priv->rx_desc = dma_alloc_coherent(NUM_RX_DESC * sizeof(struct bufdesc), &priv->rx_desc_phys); priv->rx_buf = malloc(NUM_RX_DESC * PKT_BUF_SIZE); - priv->rx_buf_phys = dma_map_single(&priv->edev.dev, priv->rx_buf, + priv->rx_buf_phys = dma_map_single(edev->parent, priv->rx_buf, NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE); for (i = 0; i < NUM_RX_DESC; i++) { @@ -479,13 +480,13 @@ static void rtl8169_eth_halt(struct eth_device *edev) pci_clear_master(priv->pci_dev); - dma_unmap_single(&edev->dev, priv->tx_buf_phys, NUM_TX_DESC * PKT_BUF_SIZE, + dma_unmap_single(edev->parent, priv->tx_buf_phys, NUM_TX_DESC * PKT_BUF_SIZE, DMA_TO_DEVICE); free(priv->tx_buf); dma_free_coherent((void *)priv->tx_desc, priv->tx_desc_phys, NUM_TX_DESC * sizeof(struct bufdesc)); - dma_unmap_single(&edev->dev, priv->rx_buf_phys, NUM_RX_DESC * PKT_BUF_SIZE, + dma_unmap_single(edev->parent, priv->rx_buf_phys, NUM_RX_DESC * PKT_BUF_SIZE, DMA_FROM_DEVICE); free(priv->rx_buf); dma_free_coherent((void *)priv->rx_desc, priv->rx_desc_phys, diff --git a/drivers/net/sja1105.c b/drivers/net/sja1105.c index b85184ed92..328e5a6369 100644 --- a/drivers/net/sja1105.c +++ b/drivers/net/sja1105.c @@ -1432,7 +1432,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) .top = {0x1FF, 0, 0, 0, 0, 0, 0}, .base = {0x0, 0, 0, 0, 0, 0, 0, 0}, .enabled = {1, 0, 0, 0, 0, 0, 0, 0}, - /* Will be overridden in sja1105_port_enable. */ + /* Will be overridden in sja1105_adjust_link. */ .speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO], .egress = true, .ingress = true, @@ -2727,14 +2727,16 @@ static int sja1105_port_pre_enable(struct dsa_port *dp, int port, return sja1105_static_config_reload(priv); } -static int sja1105_port_enable(struct dsa_port *dp, int port, - struct phy_device *phy) +static void sja1105_adjust_link(struct eth_device *edev) { + struct dsa_port *dp = edev->priv; struct device *dev = dp->ds->dev; struct sja1105_private *priv = dev_get_priv(dev); + struct phy_device *phy = dp->edev.phydev; phy_interface_t phy_mode = phy->interface; struct sja1105_xmii_params_entry *mii; struct sja1105_mac_config_entry *mac; + int port = dp->index; int ret; mii = priv->static_config.tables[BLK_IDX_XMII_PARAMS].entries; @@ -2742,7 +2744,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, ret = sja1105_port_set_mode(dp, port, phy_mode); if (ret) - return ret; + goto error; /* Let the PHY handle the RGMII delays, if present. */ if (phy->phy_id == 0) { @@ -2758,7 +2760,7 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, priv->rgmii_tx_delay[port]) && !priv->dcfg->setup_rgmii_delay) { dev_err(priv->dev, "Chip does not support internal RGMII delays\n"); - return -EINVAL; + return; } } @@ -2776,12 +2778,19 @@ static int sja1105_port_enable(struct dsa_port *dp, int port, mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_10MBPS]; } else { - dev_err(priv->dev, "Invalid PHY speed %d on port %d\n", - phy->speed, port); - return -EINVAL; + mac[port].speed = priv->dcfg->port_speed[SJA1105_SPEED_AUTO]; + return; } - return sja1105_static_config_reload(priv); + ret = sja1105_static_config_reload(priv); + if (ret) + goto error; + + return; + +error: + dev_err(priv->dev, "Failed to adjust link on port %d, error %pe\n", + port, ERR_PTR(ret)); } static int sja1105_xmit(struct dsa_port *dp, int port, void *packet, int length) @@ -2816,7 +2825,7 @@ static int sja1105_rcv(struct dsa_switch *ds, int *port, void *packet, static const struct dsa_switch_ops sja1105_dsa_ops = { .port_pre_enable = sja1105_port_pre_enable, - .port_enable = sja1105_port_enable, + .adjust_link = sja1105_adjust_link, .xmit = sja1105_xmit, .rcv = sja1105_rcv, }; diff --git a/drivers/of/base.c b/drivers/of/base.c index 01bb7e3906..5644e8e953 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -2318,6 +2318,22 @@ struct property *of_rename_property(struct device_node *np, return pp; } +struct property *of_copy_property(const struct device_node *src, + const char *propname, + struct device_node *dst) +{ + struct property *prop; + + prop = of_find_property(src, propname, NULL); + if (!prop) + return NULL; + + return of_new_property(dst, propname, + of_property_get_value(prop), prop->length); +} +EXPORT_SYMBOL_GPL(of_copy_property); + + /** * of_set_property - create a property for a given node * @node - the node diff --git a/drivers/spi/stm32_spi.c b/drivers/spi/stm32_spi.c index 0d7407c279..bdaeb6b5d0 100644 --- a/drivers/spi/stm32_spi.c +++ b/drivers/spi/stm32_spi.c @@ -111,6 +111,24 @@ static inline struct stm32_spi_priv *to_stm32_spi_priv(struct spi_master *master return container_of(master, struct stm32_spi_priv, master); } +static int stm32_spi_get_bpw_mask(struct stm32_spi_priv *priv) +{ + u32 cfg1, max_bpw; + + /* + * The most significant bit at DSIZE bit field is reserved when the + * maximum data size of periperal instances is limited to 16-bit + */ + setbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE); + + cfg1 = readl(priv->base + STM32_SPI_CFG1); + max_bpw = FIELD_GET(SPI_CFG1_DSIZE, cfg1) + 1; + + dev_dbg(priv->master.dev, "%d-bit maximum data frame\n", max_bpw); + + return SPI_BPW_RANGE_MASK(4, max_bpw); +} + static void stm32_spi_write_txfifo(struct stm32_spi_priv *priv) { while ((priv->tx_len > 0) && @@ -261,19 +279,15 @@ static void stm32_spi_set_mode(struct stm32_spi_priv *priv, unsigned mode) static void stm32_spi_set_fthlv(struct stm32_spi_priv *priv, u32 xfer_len) { - u32 fthlv, half_fifo; + u32 fthlv, packet, bpw; /* data packet should not exceed 1/2 of fifo space */ - half_fifo = (priv->fifo_size / 2); - - /* data_packet should not exceed transfer length */ - fthlv = (half_fifo > xfer_len) ? xfer_len : half_fifo; + packet = clamp(xfer_len, 1U, priv->fifo_size / 2); /* align packet size with data registers access */ - fthlv -= (fthlv % 4); + bpw = DIV_ROUND_UP(priv->cur_bpw, 8); + fthlv = DIV_ROUND_UP(packet, bpw); - if (!fthlv) - fthlv = 1; clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_FTHLV, (fthlv - 1) << SPI_CFG1_FTHLV_SHIFT); } @@ -344,9 +358,17 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, u32 ifcr = 0; u32 mode; int xfer_status = 0; + int nb_words; - if (t->len <= SPI_CR2_TSIZE) - writel(t->len, priv->base + STM32_SPI_CR2); + if (t->bits_per_word <= 8) + nb_words = t->len; + else if (t->bits_per_word <= 16) + nb_words = DIV_ROUND_UP(t->len * 8, 16); + else + nb_words = DIV_ROUND_UP(t->len * 8, 32); + + if (nb_words <= SPI_CR2_TSIZE) + writel(nb_words, priv->base + STM32_SPI_CR2); else return -EMSGSIZE; @@ -361,9 +383,11 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, else if (!priv->rx_buf) mode = SPI_SIMPLEX_TX; - if (priv->cur_xferlen != t->len || priv->cur_mode != mode) { + if (priv->cur_xferlen != t->len || priv->cur_mode != mode || + priv->cur_bpw != t->bits_per_word) { priv->cur_mode = mode; priv->cur_xferlen = t->len; + priv->cur_bpw = t->bits_per_word; /* Disable the SPI hardware to unlock CFG1/CFG2 registers */ stm32_spi_disable(priv); @@ -373,6 +397,9 @@ static int stm32_spi_transfer_one(struct stm32_spi_priv *priv, stm32_spi_set_fthlv(priv, t->len); + clrsetbits_le32(priv->base + STM32_SPI_CFG1, SPI_CFG1_DSIZE, + priv->cur_bpw - 1); + /* Enable the SPI hardware */ stm32_spi_enable(priv); } @@ -560,6 +587,7 @@ static int stm32_spi_probe(struct device *dev) if (ret) return ret; + master->bits_per_word_mask = stm32_spi_get_bpw_mask(priv); priv->fifo_size = stm32_spi_get_fifo_size(priv); priv->cur_mode = SPI_FULL_DUPLEX; diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index a3a27db6fb..09ed01fc56 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -24,13 +24,13 @@ int dwc3_host_init(struct dwc3 *dwc) return PTR_ERR(io); } - dwc->xhci = add_generic_device("xHCI", DEVICE_ID_DYNAMIC, NULL, - io->start, resource_size(io), - IORESOURCE_MEM, NULL); + dwc->xhci = add_child_device(dev, "xHCI", DEVICE_ID_DYNAMIC, NULL, + io->start, resource_size(io), + IORESOURCE_MEM, NULL); if (!dwc->xhci) { dev_err(dev, "Failed to register xHCI device\n"); return -ENODEV; } - + return 0; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 60764222af..0abe8a67a3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -594,7 +594,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, memcpy(bounce, buffer, length); } - map = addr = dma_map_single(ctrl->dev, bounce, length, direction); + map = addr = dma_map_single(ctrl->host.hw_dev, bounce, length, direction); dev_dbg(&udev->dev, "pipe=0x%lx, buffer=%p, length=%d\n", pipe, buffer, length); @@ -740,7 +740,7 @@ int xhci_bulk_tx(struct usb_device *udev, unsigned long pipe, record_transfer_result(udev, event, length); xhci_acknowledge_event(ctrl); - dma_unmap_single(ctrl->dev, map, length, direction); + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); if (usb_pipein(pipe)) memcpy(buffer, bounce, length); @@ -895,7 +895,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, if (length > 0) { if (req->requesttype & USB_DIR_IN) field |= TRB_DIR_IN; - map = buf_64 = dma_map_single(ctrl->dev, buffer, length, direction); + map = buf_64 = dma_map_single(ctrl->host.hw_dev, buffer, length, direction); trb_fields[0] = lower_32_bits(buf_64); trb_fields[1] = upper_32_bits(buf_64); @@ -947,7 +947,7 @@ int xhci_ctrl_tx(struct usb_device *udev, unsigned long pipe, /* Invalidate buffer to make it available to usb-core */ if (length > 0) - dma_unmap_single(ctrl->dev, map, length, direction); + dma_unmap_single(ctrl->host.hw_dev, map, length, direction); if (GET_COMP_CODE(le32_to_cpu(event->trans_event.transfer_len)) == COMP_SHORT_TX) { diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 55f51ca951..27fca63f96 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1359,7 +1359,11 @@ int xhci_register(struct xhci_ctrl *ctrl) */ host->no_desc_before_addr = true; - host->hw_dev = dev; + /* + * If xHCI doesn't have its own DT node, it'll be a child of a + * physical USB host controller device that should be used for DMA + */ + host->hw_dev = dev_of_node(dev) ? dev : dev->parent; host->submit_int_msg = xhci_submit_int_msg; host->submit_control_msg = xhci_submit_control_msg; host->submit_bulk_msg = xhci_submit_bulk_msg; diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h index e90be47b67..aca5f50eb0 100644 --- a/drivers/usb/misc/onboard_usb_hub.h +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -22,14 +22,31 @@ static const struct onboard_hub_pdata ti_tusb8041_data = { .reset_us = 3000, }; -const struct of_device_id onboard_hub_match[] = { +static const struct onboard_hub_pdata genesys_gl850g_data = { + .reset_us = 3, +}; + +static const struct onboard_hub_pdata genesys_gl852g_data = { + .reset_us = 50, +}; + +static const struct onboard_hub_pdata vialab_vl817_data = { + .reset_us = 10, +}; + +static const struct of_device_id onboard_hub_match[] = { { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb424,2517", .data = µchip_usb424_data, }, { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, }, + { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, }, { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + { .compatible = "usb2109,817", .data = &vialab_vl817_data, }, + { .compatible = "usb2109,2817", .data = &vialab_vl817_data, }, {} }; diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f20cc0befc..b4e37a9258 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -185,4 +185,16 @@ config DRIVER_VIDEO_PANEL_ILITEK_ILI9341 QVGA (240x320) RGB panels. support serial & parallel rgb interface. +config DRIVER_VIDEO_PANEL_MIPI_DBI + tristate "DRM support for MIPI DBI compatible panels" + depends on OFTREE && SPI + select DRIVER_VIDEO_MIPI_DBI + select FIRMWARE + select VIDEO_VPL + help + Say Y here if you want to enable support for MIPI DBI compatible + panels. The controller command setup can be provided using a + firmware file. For more information see + https://github.com/notro/panel-mipi-dbi/wiki. + endif diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 1b6d2986d7..85cffb5a33 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_DRIVER_VIDEO_TC358767) += tc358767.o obj-$(CONFIG_DRIVER_VIDEO_SIMPLE_PANEL) += simple-panel.o obj-$(CONFIG_DRIVER_VIDEO_MIPI_DBI) += mipi_dbi.o obj-$(CONFIG_DRIVER_VIDEO_PANEL_ILITEK_ILI9341) += panel-ilitek-ili9341.o +obj-$(CONFIG_DRIVER_VIDEO_PANEL_MIPI_DBI) += panel-mipi-dbi.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL) += atmel_lcdfb.o atmel_lcdfb_core.o obj-$(CONFIG_DRIVER_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o atmel_lcdfb_core.o diff --git a/drivers/video/fb.c b/drivers/video/fb.c index 44754065e7..6f412d62c4 100644 --- a/drivers/video/fb.c +++ b/drivers/video/fb.c @@ -43,6 +43,12 @@ static int fb_close(struct cdev *cdev) return 0; } +void fb_damage(struct fb_info *info, struct fb_rect *rect) +{ + if (info->fbops->fb_damage) + info->fbops->fb_damage(info, rect); +} + static int fb_op_flush(struct cdev *cdev) { struct fb_info *info = cdev->priv; diff --git a/drivers/video/fbconsole.c b/drivers/video/fbconsole.c index 070378aa23..6c85e8e06a 100644 --- a/drivers/video/fbconsole.c +++ b/drivers/video/fbconsole.c @@ -18,6 +18,12 @@ enum state_t { struct fbc_priv { struct console_device cdev; struct fb_info *fb; + struct { + u32 top; + u32 left; + u32 bottom; + u32 right; + } margin; struct screen *sc; @@ -60,9 +66,26 @@ static int fbc_tstc(struct console_device *cdev) static void cls(struct fbc_priv *priv) { void *buf = gui_screen_render_buffer(priv->sc); + struct fb_info *fb = priv->fb; + int width = fb->xres - priv->margin.left - priv->margin.right; + int height = fb->yres - priv->margin.top - priv->margin.bottom; + void *adr; + + adr = buf + priv->fb->line_length * priv->margin.top; + + if (!priv->margin.left && !priv->margin.right) { + memset(adr, 0, priv->fb->line_length * height); + } else { + int bpp = priv->fb->bits_per_pixel >> 3; + int y; - memset(buf, 0, priv->fb->line_length * priv->fb->yres); - gu_screen_blit(priv->sc); + for (y = 0; y < height; y++) { + memset(adr + priv->margin.left * bpp, 0, width * bpp); + adr += priv->fb->line_length; + } + } + gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top, + width, height); } struct rgb { @@ -122,7 +145,8 @@ static void drawchar(struct fbc_priv *priv, int x, int y, int c) uint8_t t = inbuf[i]; int j; - adr = buf + line_length * (y * priv->font->height + i) + x * priv->font->width * bpp; + adr = buf + line_length * (priv->margin.top + y * priv->font->height + i) + + (priv->margin.left + x * priv->font->width) * bpp; for (j = 0; j < priv->font->width; j++) { if (t & 0x80) @@ -142,9 +166,11 @@ static void video_invertchar(struct fbc_priv *priv, int x, int y) buf = gui_screen_render_buffer(priv->sc); - gu_invert_area(priv->fb, buf, x * priv->font->width, y * priv->font->height, + gu_invert_area(priv->fb, buf, priv->margin.left + x * priv->font->width, + priv->margin.top + y * priv->font->height, priv->font->width, priv->font->height); - gu_screen_blit_area(priv->sc, x * priv->font->width, y * priv->font->height, + gu_screen_blit_area(priv->sc, priv->margin.left + x * priv->font->width, + priv->margin.top + y * priv->font->height, priv->font->width, priv->font->height); } @@ -185,8 +211,9 @@ static void printchar(struct fbc_priv *priv, int c) default: drawchar(priv, priv->x, priv->y, c); - gu_screen_blit_area(priv->sc, priv->x * priv->font->width, - priv->y * priv->font->height, + gu_screen_blit_area(priv->sc, + priv->margin.left + priv->x * priv->font->width, + priv->margin.top + priv->y * priv->font->height, priv->font->width, priv->font->height); priv->x++; @@ -198,15 +225,36 @@ static void printchar(struct fbc_priv *priv, int c) if (priv->y > priv->rows) { void *buf; + void *adr; u32 line_length = priv->fb->line_length; int line_height = line_length * priv->font->height; + int width = priv->fb->xres - priv->margin.left - priv->margin.right; + int height = (priv->rows + 1) * priv->font->height; buf = gui_screen_render_buffer(priv->sc); + adr = buf + priv->margin.top * line_length; + + if (!priv->margin.left && !priv->margin.right) { + memcpy(adr, adr + line_height, line_height * priv->rows); + memset(adr + line_height * priv->rows, 0, line_height); + } else { + int bpp = priv->fb->bits_per_pixel >> 3; + int y; + + adr += priv->margin.left * bpp; + + for (y = 0; y < height - priv->font->height; y++) { + memcpy(adr, adr + line_height, width * bpp); + adr += line_length; + } + for (y = height - priv->font->height; y < height; y++) { + memset(adr, 0, width * bpp); + adr += line_length; + } + } - memcpy(buf, buf + line_height, line_height * priv->rows); - memset(buf + line_height * priv->rows, 0, line_height); - - gu_screen_blit(priv->sc); + gu_screen_blit_area(priv->sc, priv->margin.left, priv->margin.top, + width, height); priv->y = priv->rows; } @@ -401,8 +449,9 @@ static void fbc_putc(struct console_device *cdev, char c) static int setup_font(struct fbc_priv *priv) { - struct fb_info *fb = priv->fb; const struct font_desc *font; + unsigned int height = priv->fb->yres - priv->margin.top - priv->margin.bottom; + unsigned int width = priv->fb->xres - priv->margin.left - priv->margin.right; font = find_font_enum(priv->par_font_val); if (!font) { @@ -411,8 +460,8 @@ static int setup_font(struct fbc_priv *priv) priv->font = font; - priv->rows = fb->yres / priv->font->height - 1; - priv->cols = fb->xres / priv->font->width - 1; + priv->rows = height / priv->font->height - 1; + priv->cols = width / priv->font->width - 1; return 0; } @@ -472,6 +521,35 @@ static int set_font(struct param_d *p, void *vpriv) return 0; } +static int set_margin(struct param_d *p, void *vpriv) +{ + struct fbc_priv *priv = vpriv; + struct console_device *cdev = &priv->cdev; + int ret; + + if (!priv->font) { + ret = setup_font(priv); + if (ret) + return ret; + } + + priv->margin.left = min(priv->margin.left, + priv->fb->xres - priv->margin.right - priv->font->width); + priv->margin.top = min(priv->margin.top, + priv->fb->yres - priv->margin.bottom - priv->font->height); + priv->margin.right = min(priv->margin.right, + priv->fb->xres - priv->margin.left - priv->font->width); + priv->margin.bottom = min(priv->margin.bottom, + priv->fb->yres - priv->margin.top - priv->font->height); + + if (cdev->f_active & (CONSOLE_STDOUT | CONSOLE_STDERR)) { + cls(priv); + setup_font(priv); + } + + return 0; +} + int register_fbconsole(struct fb_info *fb) { struct fbc_priv *priv; @@ -508,6 +586,15 @@ int register_fbconsole(struct fb_info *fb) set_font, NULL, &priv->par_font_val, priv); + dev_add_param_uint32(&cdev->class_dev, "margin.top", set_margin, + NULL, &priv->margin.top, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.left", set_margin, + NULL, &priv->margin.left, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.bottom", set_margin, + NULL, &priv->margin.bottom, "%u", priv); + dev_add_param_uint32(&cdev->class_dev, "margin.right", set_margin, + NULL, &priv->margin.right, "%u", priv); + pr_info("registered as %s%d\n", cdev->class_dev.name, cdev->class_dev.id); return 0; diff --git a/drivers/video/mipi_dbi.c b/drivers/video/mipi_dbi.c index 50d2fc4b29..a2333150b9 100644 --- a/drivers/video/mipi_dbi.c +++ b/drivers/video/mipi_dbi.c @@ -8,11 +8,13 @@ #define pr_fmt(fmt) "mipi-dbi: " fmt #include <common.h> +#include <dma.h> #include <linux/kernel.h> #include <linux/sizes.h> #include <gpiod.h> #include <regulator.h> #include <spi/spi.h> +#include <video/backlight.h> #include <video/mipi_dbi.h> #include <video/vpl.h> @@ -197,6 +199,221 @@ int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data, EXPORT_SYMBOL(mipi_dbi_command_stackbuf); /** + * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary + * @dst: The destination buffer + * @info: The source framebuffer info + * @clip: Clipping rectangle of the area to be copied + * @swap: When true, swap MSB/LSB of 16-bit values + */ +static void mipi_dbi_buf_copy(u16 *dst, struct fb_info *info, + struct fb_rect *clip, bool swap) +{ + u16 *src = (u16 *)info->screen_base; + unsigned int height = clip->y2 - clip->y1; + unsigned int width = clip->x2 - clip->x1; + int x, y; + + src += clip->y1 * info->xres + clip->x1; + if (swap) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) + *dst++ = src[x] << 8 | src[x] >> 8; + src += info->xres; + } + } else { + for (y = 0; y < height; y++) { + memcpy(dst, src, 2 * width); + dst += width; + src += info->xres; + } + } +} + +static void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev, + unsigned int xs, unsigned int xe, + unsigned int ys, unsigned int ye) +{ + struct mipi_dbi *dbi = &dbidev->dbi; + + xs += dbidev->mode.left_margin; + xe += dbidev->mode.left_margin; + ys += dbidev->mode.upper_margin; + ye += dbidev->mode.upper_margin; + + mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff, + xs & 0xff, (xe >> 8) & 0xff, xe & 0xff); + mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff, + ys & 0xff, (ye >> 8) & 0xff, ye & 0xff); +} + +static void mipi_dbi_fb_dirty(struct mipi_dbi_dev *dbidev, struct fb_info *info, + struct fb_rect *rect) +{ + unsigned int height = rect->y2 - rect->y1; + unsigned int width = rect->x2 - rect->x1; + struct mipi_dbi *dbi = &dbidev->dbi; + bool swap = dbi->swap_bytes; + int ret; + bool full; + void *tr; + + full = width == info->xres && height == info->yres; + + if (!full || swap) { + tr = dbidev->tx_buf; + mipi_dbi_buf_copy(tr, info, rect, swap); + } else { + tr = info->screen_base; + } + + mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1, + rect->y2 - 1); + + ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr, + width * height * 2); + if (ret) + pr_err_once("Failed to update display %d\n", ret); + + dbidev->damage.x1 = 0; + dbidev->damage.y1 = 0; + dbidev->damage.x2 = 0; + dbidev->damage.y2 = 0; +} + +/** + * mipi_dbi_enable_flush - MIPI DBI enable helper + * @dbidev: MIPI DBI device structure + * @info: Framebuffer info + * + * Flushes the whole framebuffer and enables the backlight. Drivers can use this + * in their &fb_ops->fb_enable callback. + */ +void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, + struct fb_info *info) +{ + struct fb_rect rect = { + .x1 = 0, + .y1 = 0, + .x2 = info->xres, + .y2 = info->yres + }; + + mipi_dbi_fb_dirty(dbidev, info, &rect); + + if (dbidev->backlight) + backlight_set_brightness_default(dbidev->backlight); +} +EXPORT_SYMBOL(mipi_dbi_enable_flush); + +static void mipi_dbi_blank(struct mipi_dbi_dev *dbidev) +{ + u16 height = dbidev->mode.xres; + u16 width = dbidev->mode.yres; + struct mipi_dbi *dbi = &dbidev->dbi; + size_t len = width * height * 2; + + memset(dbidev->tx_buf, 0, len); + + mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1); + mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, dbidev->tx_buf, len); +} + +/** + * mipi_dbi_fb_disable - MIPI DBI framebuffer disable helper + * @info: Framebuffer info + * + * This function disables backlight if present, if not the display memory is + * blanked. The regulator is disabled if in use. Drivers can use this as their + * &fb_ops->fb_disable callback. + */ +void mipi_dbi_fb_disable(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (dbidev->backlight) + backlight_set_brightness(dbidev->backlight, 0); + else + mipi_dbi_blank(dbidev); + + regulator_disable(dbidev->regulator); + regulator_disable(dbidev->io_regulator); +} +EXPORT_SYMBOL(mipi_dbi_fb_disable); + +void mipi_dbi_fb_damage(struct fb_info *info, const struct fb_rect *rect) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (dbidev->damage.x2 && dbidev->damage.y2) { + dbidev->damage.x1 = min(dbidev->damage.x1, rect->x1); + dbidev->damage.y1 = min(dbidev->damage.y1, rect->y1); + dbidev->damage.x2 = max(dbidev->damage.x2, rect->x2); + dbidev->damage.y2 = max(dbidev->damage.y2, rect->y2); + } else { + dbidev->damage = *rect; + } +} +EXPORT_SYMBOL(mipi_dbi_fb_damage); + +void mipi_dbi_fb_flush(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + + if (!dbidev->damage.x2 || !dbidev->damage.y2) { + dbidev->damage.x1 = 0; + dbidev->damage.y1 = 0; + dbidev->damage.x2 = info->xres; + dbidev->damage.y2 = info->yres; + } + + mipi_dbi_fb_dirty(dbidev, info, &dbidev->damage); +} +EXPORT_SYMBOL(mipi_dbi_fb_flush); + +/** + * mipi_dbi_dev_init - MIPI DBI device initialization + * @dbidev: MIPI DBI device structure to initialize + * @ops: Framebuffer operations + * @mode: Display mode + * + * This function sets up a &fb_info with one fixed &fb_videomode. + * Additionally &mipi_dbi.tx_buf is allocated. + * + * Supported format: RGB565. + * + * Returns: + * Zero on success, negative error code on failure. + */ +int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, struct fb_ops *ops, + struct fb_videomode *mode) +{ + struct fb_info *info = &dbidev->info; + + info->mode = mode; + info->fbops = ops; + info->dev.parent = dbidev->dev; + + info->xres = mode->xres; + info->yres = mode->yres; + info->bits_per_pixel = 16; + info->line_length = info->xres * 2; + info->screen_size = info->line_length * info->yres; + info->screen_base = dma_alloc(info->screen_size); + memset(info->screen_base, 0, info->screen_size); + + info->red.length = 5; + info->red.offset = 11; + info->green.length = 6; + info->green.offset = 5; + info->blue.length = 5; + info->blue.offset = 0; + + dbidev->tx_buf = dma_alloc(info->screen_size); + + return 0; +} + +/** * mipi_dbi_hw_reset - Hardware reset of controller * @dbi: MIPI DBI structure * @@ -246,6 +463,68 @@ bool mipi_dbi_display_is_on(struct mipi_dbi *dbi) } EXPORT_SYMBOL(mipi_dbi_display_is_on); +static int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond) +{ + struct device *dev = dbidev->dev; + struct mipi_dbi *dbi = &dbidev->dbi; + int ret; + + ret = regulator_enable(dbidev->regulator); + if (ret) { + dev_err(dev, "Failed to enable regulator (%d)\n", ret); + return ret; + } + + ret = regulator_enable(dbidev->io_regulator); + if (ret) { + dev_err(dev, "Failed to enable I/O regulator (%d)\n", ret); + regulator_disable(dbidev->regulator); + return ret; + } + + if (cond && mipi_dbi_display_is_on(dbi)) + return 1; + + mipi_dbi_hw_reset(dbi); + ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET); + if (ret) { + dev_err(dev, "Failed to send reset command (%d)\n", ret); + regulator_disable(dbidev->io_regulator); + regulator_disable(dbidev->regulator); + return ret; + } + + /* + * If we did a hw reset, we know the controller is in Sleep mode and + * per MIPI DSC spec should wait 5ms after soft reset. If we didn't, + * we assume worst case and wait 120ms. + */ + if (dbi->reset) + mdelay(5); + else + mdelay(120); + + return 0; +} + +/** + * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset + * @dbidev: MIPI DBI device structure + * + * This function enables the regulator if used and if the display is off, it + * does a hardware and software reset. If mipi_dbi_display_is_on() determines + * that the display is on, no reset is performed. + * + * Returns: + * Zero if the controller was reset, 1 if the display was already on, or a + * negative error code. + */ +int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev) +{ + return mipi_dbi_poweron_reset_conditional(dbidev, true); +} +EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset); + #if IS_ENABLED(CONFIG_SPI) /** @@ -406,8 +685,7 @@ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, dbi->command = mipi_dbi_typec3_command; dbi->dc = dc; - // TODO: can we just force 16 bit? - if (mipi_dbi_machine_little_endian() && spi->bits_per_word != 16) + if (mipi_dbi_machine_little_endian() && !spi_is_bpw_supported(spi, 16)) dbi->swap_bytes = true; dev_dbg(dev, "SPI speed: %uMHz\n", spi->max_speed_hz / 1000000); diff --git a/drivers/video/of_display_timing.c b/drivers/video/of_display_timing.c index 6fe1e1b08b..6082d45493 100644 --- a/drivers/video/of_display_timing.c +++ b/drivers/video/of_display_timing.c @@ -99,6 +99,28 @@ static int of_parse_display_timing(const struct device_node *np, } /** + * of_get_display_timing - parse a display_timing entry + * @np: device_node with the timing subnode + * @name: name of the timing node + * @mode: fb_videomode struct to fill + **/ +int of_get_display_timing(const struct device_node *np, const char *name, + struct fb_videomode *mode) +{ + struct device_node *timing_np; + + if (!np) + return -EINVAL; + + timing_np = of_get_child_by_name(np, name); + if (!timing_np) + return -ENOENT; + + return of_parse_display_timing(timing_np, mode); +} +EXPORT_SYMBOL_GPL(of_get_display_timing); + +/** * of_get_display_timings - parse all display_timing entries from a device_node * @np: device_node with the subnodes **/ diff --git a/drivers/video/panel-mipi-dbi.c b/drivers/video/panel-mipi-dbi.c new file mode 100644 index 0000000000..7fada69d6f --- /dev/null +++ b/drivers/video/panel-mipi-dbi.c @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * DRM driver for MIPI DBI compatible display panels + * + * Copyright 2022 Noralf Trønnes + */ + +#include <clock.h> +#include <common.h> +#include <fb.h> +#include <firmware.h> +#include <gpiod.h> +#include <linux/printk.h> +#include <of.h> +#include <regulator.h> +#include <spi/spi.h> + +#include <video/backlight.h> +#include <video/mipi_dbi.h> +#include <video/mipi_display.h> + +static const u8 panel_mipi_dbi_magic[15] = { 'M', 'I', 'P', 'I', ' ', 'D', 'B', 'I', + 0, 0, 0, 0, 0, 0, 0 }; + +/* + * The display controller configuration is stored in a firmware file. + * The Device Tree 'compatible' property value with a '.bin' suffix is passed + * to request_firmware() to fetch this file. + */ +struct panel_mipi_dbi_config { + /* Magic string: panel_mipi_dbi_magic */ + u8 magic[15]; + + /* Config file format version */ + u8 file_format_version; + + /* + * MIPI commands to execute when the display pipeline is enabled. + * This is used to configure the display controller. + * + * The commands are stored in a byte array with the format: + * command, num_parameters, [ parameter, ...], command, ... + * + * Some commands require a pause before the next command can be received. + * Inserting a delay in the command sequence is done by using the NOP command with one + * parameter: delay in miliseconds (the No Operation command is part of the MIPI Display + * Command Set where it has no parameters). + * + * Example: + * command 0x11 + * sleep 120ms + * command 0xb1 parameters 0x01, 0x2c, 0x2d + * command 0x29 + * + * Byte sequence: + * 0x11 0x00 + * 0x00 0x01 0x78 + * 0xb1 0x03 0x01 0x2c 0x2d + * 0x29 0x00 + */ + u8 commands[]; +}; + +struct panel_mipi_dbi_commands { + const u8 *buf; + size_t len; +}; + +static struct panel_mipi_dbi_commands * +panel_mipi_dbi_check_commands(struct device *dev, const struct firmware *fw) +{ + const struct panel_mipi_dbi_config *config = (struct panel_mipi_dbi_config *)fw->data; + struct panel_mipi_dbi_commands *commands; + size_t size = fw->size, commands_len; + unsigned int i = 0; + + if (size < sizeof(*config) + 2) { /* At least 1 command */ + dev_err(dev, "config: file size=%zu is too small\n", size); + return ERR_PTR(-EINVAL); + } + + if (memcmp(config->magic, panel_mipi_dbi_magic, sizeof(config->magic))) { + dev_err(dev, "config: Bad magic: %15ph\n", config->magic); + return ERR_PTR(-EINVAL); + } + + if (config->file_format_version != 1) { + dev_err(dev, "config: version=%u is not supported\n", config->file_format_version); + return ERR_PTR(-EINVAL); + } + + dev_dbg(dev, "size=%zu version=%u\n", size, config->file_format_version); + + commands_len = size - sizeof(*config); + + while ((i + 1) < commands_len) { + u8 command = config->commands[i++]; + u8 num_parameters = config->commands[i++]; + const u8 *parameters = &config->commands[i]; + + i += num_parameters; + if (i > commands_len) { + dev_err(dev, "config: command=0x%02x num_parameters=%u overflows\n", + command, num_parameters); + return ERR_PTR(-EINVAL); + } + + if (command == 0x00 && num_parameters == 1) + dev_dbg(dev, "sleep %ums\n", parameters[0]); + else + dev_dbg(dev, "command %02x %*ph\n", + command, num_parameters, parameters); + } + + if (i != commands_len) { + dev_err(dev, "config: malformed command array\n"); + return ERR_PTR(-EINVAL); + } + + commands = kzalloc(sizeof(*commands), GFP_KERNEL); + if (!commands) + return ERR_PTR(-ENOMEM); + + commands->len = commands_len; + commands->buf = kmemdup(config->commands, commands->len, GFP_KERNEL); + if (!commands->buf) + return ERR_PTR(-ENOMEM); + + return commands; +} + +static struct panel_mipi_dbi_commands *panel_mipi_dbi_commands_from_fw(struct device *dev) +{ + struct panel_mipi_dbi_commands *commands; + const struct firmware *fw; + const char *compatible; + char fw_name[40]; + int ret; + + ret = of_property_read_string_index(dev->of_node, "compatible", 0, &compatible); + if (ret) + return ERR_PTR(ret); + + snprintf(fw_name, sizeof(fw_name), "%s.bin", compatible); + ret = request_firmware(&fw, fw_name, dev); + if (ret) { + dev_err(dev, "No config file found for compatible '%s' (error=%d)\n", + compatible, ret); + + return ERR_PTR(ret); + } + + commands = panel_mipi_dbi_check_commands(dev, fw); + release_firmware(fw); + + return commands; +} + +static void panel_mipi_dbi_commands_execute(struct mipi_dbi *dbi, + struct panel_mipi_dbi_commands *commands) +{ + unsigned int i = 0; + + if (!commands) + return; + + while (i < commands->len) { + u8 command = commands->buf[i++]; + u8 num_parameters = commands->buf[i++]; + const u8 *parameters = &commands->buf[i]; + + if (command == 0x00 && num_parameters == 1) + mdelay(parameters[0]); + else if (num_parameters) + mipi_dbi_command_stackbuf(dbi, command, parameters, num_parameters); + else + mipi_dbi_command(dbi, command); + + i += num_parameters; + } +} + +static void panel_mipi_dbi_enable(struct fb_info *info) +{ + struct mipi_dbi_dev *dbidev = container_of(info, struct mipi_dbi_dev, info); + struct mipi_dbi *dbi = &dbidev->dbi; + int ret; + + if (!info->mode) { + dev_err(dbidev->dev, "No valid mode found\n"); + return; + } + + if (dbidev->backlight_node && !dbidev->backlight) { + dbidev->backlight = of_backlight_find(dbidev->backlight_node); + if (!dbidev->backlight) + dev_err(dbidev->dev, "No backlight found\n"); + } + + if (!dbidev->driver_private) { + dbidev->driver_private = panel_mipi_dbi_commands_from_fw(dbidev->dev); + if (IS_ERR(dbidev->driver_private)) { + dbidev->driver_private = NULL; + return; + } + } + + ret = mipi_dbi_poweron_conditional_reset(dbidev); + if (ret < 0) + return; + if (!ret) + panel_mipi_dbi_commands_execute(dbi, dbidev->driver_private); + + mipi_dbi_enable_flush(dbidev, info); +} + + +static struct fb_ops panel_mipi_dbi_ops = { + .fb_enable = panel_mipi_dbi_enable, + .fb_disable = mipi_dbi_fb_disable, + .fb_damage = mipi_dbi_fb_damage, + .fb_flush = mipi_dbi_fb_flush, +}; + + +static int panel_mipi_dbi_get_mode(struct mipi_dbi_dev *dbidev, struct fb_videomode *mode) +{ + struct device *dev = dbidev->dev; + int ret; + + ret = of_get_display_timing(dev->of_node, "panel-timing", mode); + if (ret) { + dev_err(dev, "%pOF: failed to get panel-timing (error=%d)\n", dev->of_node, ret); + return ret; + } + + /* + * Make sure width and height are set and that only back porch and + * pixelclock are set in the other timing values. Also check that + * width and height don't exceed the 16-bit value specified by MIPI DCS. + */ + if (!mode->xres || !mode->yres || mode->display_flags || + mode->right_margin || mode->hsync_len || (mode->left_margin + mode->xres) > 0xffff || + mode->lower_margin || mode->vsync_len || (mode->upper_margin + mode->yres) > 0xffff) { + dev_err(dev, "%pOF: panel-timing out of bounds\n", dev->of_node); + return -EINVAL; + } + + /* The driver doesn't use the pixel clock but it is mandatory so fake one if not set */ + if (!mode->pixclock) { + mode->pixclock = + (mode->left_margin + mode->xres + mode->right_margin + mode->hsync_len) * + (mode->upper_margin + mode->yres + mode->lower_margin + mode->vsync_len) * + 60 / 1000; + } + + return 0; +} + +static int panel_mipi_dbi_spi_probe(struct device *dev) +{ + struct mipi_dbi_dev *dbidev; + struct spi_device *spi = to_spi_device(dev); + struct mipi_dbi *dbi; + struct fb_info *info; + int dc; + int ret; + + dbidev = kzalloc(sizeof(*dbidev), GFP_KERNEL); + if (!dbidev) + return -ENOMEM; + + dbidev->dev = dev; + dbi = &dbidev->dbi; + info = &dbidev->info; + + ret = panel_mipi_dbi_get_mode(dbidev, &dbidev->mode); + if (ret) + return ret; + + dbidev->regulator = regulator_get(dev, "power"); + if (IS_ERR(dbidev->regulator)) + return dev_err_probe(dev, PTR_ERR(dbidev->regulator), + "Failed to get regulator 'power'\n"); + + dbidev->io_regulator = regulator_get(dev, "io"); + if (IS_ERR(dbidev->io_regulator)) + return dev_err_probe(dev, PTR_ERR(dbidev->io_regulator), + "Failed to get regulator 'io'\n"); + + dbidev->backlight_node = of_parse_phandle(dev->of_node, "backlight", 0); + + dbi->reset = gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (dbi->reset < 0 && dbi->reset != -ENOENT) + return dev_err_probe(dev, dbi->reset, "Failed to get GPIO 'reset'\n"); + + dc = gpiod_get(dev, "dc", GPIOD_OUT_LOW); + if (dc < 0 && dc != -ENOENT) + return dev_err_probe(dev, dc, "Failed to get GPIO 'dc'\n"); + + ret = mipi_dbi_spi_init(spi, dbi, dc); + if (ret) + return ret; + + ret = mipi_dbi_dev_init(dbidev, &panel_mipi_dbi_ops, &dbidev->mode); + if (ret) + return ret; + + ret = register_framebuffer(info); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to register framebuffer\n"); + + return 0; +} + +static const struct of_device_id panel_mipi_dbi_spi_of_match[] = { + { .compatible = "panel-mipi-dbi-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, panel_mipi_dbi_spi_of_match); + +static struct driver panel_mipi_dbi_spi_driver = { + .name = "panel-mipi-dbi-spi", + .probe = panel_mipi_dbi_spi_probe, + .of_compatible = DRV_OF_COMPAT(panel_mipi_dbi_spi_of_match), +}; +device_spi_driver(panel_mipi_dbi_spi_driver); + +MODULE_DESCRIPTION("MIPI DBI compatible display panel driver"); +MODULE_AUTHOR("Noralf Trønnes"); +MODULE_LICENSE("GPL"); |