| /* |
| * Copyright © 2010 Intel Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice (including the next |
| * paragraph) shall be included in all copies or substantial portions of the |
| * Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| * Authors: |
| * Jackie Li<yaodong.li@intel.com> |
| */ |
| |
| #include <linux/freezer.h> |
| #include <video/mipi_display.h> |
| |
| #include "mdfld_dsi_output.h" |
| #include "mdfld_dsi_pkg_sender.h" |
| #include "mdfld_dsi_dpi.h" |
| |
| #define MDFLD_DSI_READ_MAX_COUNT 5000 |
| |
| enum { |
| MDFLD_DSI_PANEL_MODE_SLEEP = 0x1, |
| }; |
| |
| enum { |
| MDFLD_DSI_PKG_SENDER_FREE = 0x0, |
| MDFLD_DSI_PKG_SENDER_BUSY = 0x1, |
| }; |
| |
| static const char *const dsi_errors[] = { |
| "RX SOT Error", |
| "RX SOT Sync Error", |
| "RX EOT Sync Error", |
| "RX Escape Mode Entry Error", |
| "RX LP TX Sync Error", |
| "RX HS Receive Timeout Error", |
| "RX False Control Error", |
| "RX ECC Single Bit Error", |
| "RX ECC Multibit Error", |
| "RX Checksum Error", |
| "RX DSI Data Type Not Recognised", |
| "RX DSI VC ID Invalid", |
| "TX False Control Error", |
| "TX ECC Single Bit Error", |
| "TX ECC Multibit Error", |
| "TX Checksum Error", |
| "TX DSI Data Type Not Recognised", |
| "TX DSI VC ID invalid", |
| "High Contention", |
| "Low contention", |
| "DPI FIFO Under run", |
| "HS TX Timeout", |
| "LP RX Timeout", |
| "Turn Around ACK Timeout", |
| "ACK With No Error", |
| "RX Invalid TX Length", |
| "RX Prot Violation", |
| "HS Generic Write FIFO Full", |
| "LP Generic Write FIFO Full", |
| "Generic Read Data Avail", |
| "Special Packet Sent", |
| "Tearing Effect", |
| }; |
| |
| static inline int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender, |
| u32 mask) |
| { |
| struct drm_device *dev = sender->dev; |
| u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg; |
| int retry = 0xffff; |
| |
| while (retry--) { |
| if ((mask & REG_READ(gen_fifo_stat_reg)) == mask) |
| return 0; |
| udelay(100); |
| } |
| DRM_ERROR("fifo is NOT empty 0x%08x\n", REG_READ(gen_fifo_stat_reg)); |
| return -EIO; |
| } |
| |
| static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender) |
| { |
| return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(10) | BIT(18) | |
| BIT(26) | BIT(27) | BIT(28))); |
| } |
| |
| static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender) |
| { |
| return wait_for_gen_fifo_empty(sender, (BIT(10) | BIT(26))); |
| } |
| |
| static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender) |
| { |
| return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(18))); |
| } |
| |
| static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask) |
| { |
| u32 intr_stat_reg = sender->mipi_intr_stat_reg; |
| struct drm_device *dev = sender->dev; |
| |
| dev_dbg(sender->dev->dev, "Handling error 0x%08x\n", mask); |
| |
| switch (mask) { |
| case BIT(0): |
| case BIT(1): |
| case BIT(2): |
| case BIT(3): |
| case BIT(4): |
| case BIT(5): |
| case BIT(6): |
| case BIT(7): |
| case BIT(8): |
| case BIT(9): |
| case BIT(10): |
| case BIT(11): |
| case BIT(12): |
| case BIT(13): |
| dev_dbg(sender->dev->dev, "No Action required\n"); |
| break; |
| case BIT(14): |
| /*wait for all fifo empty*/ |
| /*wait_for_all_fifos_empty(sender)*/ |
| break; |
| case BIT(15): |
| dev_dbg(sender->dev->dev, "No Action required\n"); |
| break; |
| case BIT(16): |
| break; |
| case BIT(17): |
| break; |
| case BIT(18): |
| case BIT(19): |
| dev_dbg(sender->dev->dev, "High/Low contention detected\n"); |
| /*wait for contention recovery time*/ |
| /*mdelay(10);*/ |
| /*wait for all fifo empty*/ |
| if (0) |
| wait_for_all_fifos_empty(sender); |
| break; |
| case BIT(20): |
| dev_dbg(sender->dev->dev, "No Action required\n"); |
| break; |
| case BIT(21): |
| /*wait for all fifo empty*/ |
| /*wait_for_all_fifos_empty(sender);*/ |
| break; |
| case BIT(22): |
| break; |
| case BIT(23): |
| case BIT(24): |
| case BIT(25): |
| case BIT(26): |
| case BIT(27): |
| dev_dbg(sender->dev->dev, "HS Gen fifo full\n"); |
| REG_WRITE(intr_stat_reg, mask); |
| wait_for_hs_fifos_empty(sender); |
| break; |
| case BIT(28): |
| dev_dbg(sender->dev->dev, "LP Gen fifo full\n"); |
| REG_WRITE(intr_stat_reg, mask); |
| wait_for_lp_fifos_empty(sender); |
| break; |
| case BIT(29): |
| case BIT(30): |
| case BIT(31): |
| dev_dbg(sender->dev->dev, "No Action required\n"); |
| break; |
| } |
| |
| if (mask & REG_READ(intr_stat_reg)) |
| dev_dbg(sender->dev->dev, |
| "Cannot clean interrupt 0x%08x\n", mask); |
| return 0; |
| } |
| |
| static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender) |
| { |
| struct drm_device *dev = sender->dev; |
| u32 intr_stat_reg = sender->mipi_intr_stat_reg; |
| u32 mask; |
| u32 intr_stat; |
| int i; |
| int err = 0; |
| |
| intr_stat = REG_READ(intr_stat_reg); |
| |
| for (i = 0; i < 32; i++) { |
| mask = (0x00000001UL) << i; |
| if (intr_stat & mask) { |
| dev_dbg(sender->dev->dev, "[DSI]: %s\n", dsi_errors[i]); |
| err = handle_dsi_error(sender, mask); |
| if (err) |
| DRM_ERROR("Cannot handle error\n"); |
| } |
| } |
| return err; |
| } |
| |
| static int send_short_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 cmd, u8 param, bool hs) |
| { |
| struct drm_device *dev = sender->dev; |
| u32 ctrl_reg; |
| u32 val; |
| u8 virtual_channel = 0; |
| |
| if (hs) { |
| ctrl_reg = sender->mipi_hs_gen_ctrl_reg; |
| |
| /* FIXME: wait_for_hs_fifos_empty(sender); */ |
| } else { |
| ctrl_reg = sender->mipi_lp_gen_ctrl_reg; |
| |
| /* FIXME: wait_for_lp_fifos_empty(sender); */ |
| } |
| |
| val = FLD_VAL(param, 23, 16) | FLD_VAL(cmd, 15, 8) | |
| FLD_VAL(virtual_channel, 7, 6) | FLD_VAL(data_type, 5, 0); |
| |
| REG_WRITE(ctrl_reg, val); |
| |
| return 0; |
| } |
| |
| static int send_long_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 *data, int len, bool hs) |
| { |
| struct drm_device *dev = sender->dev; |
| u32 ctrl_reg; |
| u32 data_reg; |
| u32 val; |
| u8 *p; |
| u8 b1, b2, b3, b4; |
| u8 virtual_channel = 0; |
| int i; |
| |
| if (hs) { |
| ctrl_reg = sender->mipi_hs_gen_ctrl_reg; |
| data_reg = sender->mipi_hs_gen_data_reg; |
| |
| /* FIXME: wait_for_hs_fifos_empty(sender); */ |
| } else { |
| ctrl_reg = sender->mipi_lp_gen_ctrl_reg; |
| data_reg = sender->mipi_lp_gen_data_reg; |
| |
| /* FIXME: wait_for_lp_fifos_empty(sender); */ |
| } |
| |
| p = data; |
| for (i = 0; i < len / 4; i++) { |
| b1 = *p++; |
| b2 = *p++; |
| b3 = *p++; |
| b4 = *p++; |
| |
| REG_WRITE(data_reg, b4 << 24 | b3 << 16 | b2 << 8 | b1); |
| } |
| |
| i = len % 4; |
| if (i) { |
| b1 = 0; b2 = 0; b3 = 0; |
| |
| switch (i) { |
| case 3: |
| b1 = *p++; |
| b2 = *p++; |
| b3 = *p++; |
| break; |
| case 2: |
| b1 = *p++; |
| b2 = *p++; |
| break; |
| case 1: |
| b1 = *p++; |
| break; |
| } |
| |
| REG_WRITE(data_reg, b3 << 16 | b2 << 8 | b1); |
| } |
| |
| val = FLD_VAL(len, 23, 8) | FLD_VAL(virtual_channel, 7, 6) | |
| FLD_VAL(data_type, 5, 0); |
| |
| REG_WRITE(ctrl_reg, val); |
| |
| return 0; |
| } |
| |
| static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 *data, u16 len) |
| { |
| u8 cmd; |
| |
| switch (data_type) { |
| case MIPI_DSI_DCS_SHORT_WRITE: |
| case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
| case MIPI_DSI_DCS_LONG_WRITE: |
| cmd = *data; |
| break; |
| default: |
| return 0; |
| } |
| |
| /*this prevents other package sending while doing msleep*/ |
| sender->status = MDFLD_DSI_PKG_SENDER_BUSY; |
| |
| /*wait for 120 milliseconds in case exit_sleep_mode just be sent*/ |
| if (unlikely(cmd == MIPI_DCS_ENTER_SLEEP_MODE)) { |
| /*TODO: replace it with msleep later*/ |
| mdelay(120); |
| } |
| |
| if (unlikely(cmd == MIPI_DCS_EXIT_SLEEP_MODE)) { |
| /*TODO: replace it with msleep later*/ |
| mdelay(120); |
| } |
| return 0; |
| } |
| |
| static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 *data, u16 len) |
| { |
| u8 cmd; |
| |
| switch (data_type) { |
| case MIPI_DSI_DCS_SHORT_WRITE: |
| case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
| case MIPI_DSI_DCS_LONG_WRITE: |
| cmd = *data; |
| break; |
| default: |
| return 0; |
| } |
| |
| /*update panel status*/ |
| if (unlikely(cmd == MIPI_DCS_ENTER_SLEEP_MODE)) { |
| sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP; |
| /*TODO: replace it with msleep later*/ |
| mdelay(120); |
| } else if (unlikely(cmd == MIPI_DCS_EXIT_SLEEP_MODE)) { |
| sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP; |
| /*TODO: replace it with msleep later*/ |
| mdelay(120); |
| } else if (unlikely(cmd == MIPI_DCS_SOFT_RESET)) { |
| /*TODO: replace it with msleep later*/ |
| mdelay(5); |
| } |
| |
| sender->status = MDFLD_DSI_PKG_SENDER_FREE; |
| |
| return 0; |
| } |
| |
| static int send_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 *data, u16 len, bool hs) |
| { |
| int ret; |
| |
| /*handle DSI error*/ |
| ret = dsi_error_handler(sender); |
| if (ret) { |
| DRM_ERROR("Error handling failed\n"); |
| return -EAGAIN; |
| } |
| |
| /* send pkg */ |
| if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) { |
| DRM_ERROR("sender is busy\n"); |
| return -EAGAIN; |
| } |
| |
| ret = send_pkg_prepare(sender, data_type, data, len); |
| if (ret) { |
| DRM_ERROR("send_pkg_prepare error\n"); |
| return ret; |
| } |
| |
| switch (data_type) { |
| case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM: |
| case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM: |
| case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM: |
| case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM: |
| case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM: |
| case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM: |
| case MIPI_DSI_DCS_SHORT_WRITE: |
| case MIPI_DSI_DCS_SHORT_WRITE_PARAM: |
| case MIPI_DSI_DCS_READ: |
| ret = send_short_pkg(sender, data_type, data[0], data[1], hs); |
| break; |
| case MIPI_DSI_GENERIC_LONG_WRITE: |
| case MIPI_DSI_DCS_LONG_WRITE: |
| ret = send_long_pkg(sender, data_type, data, len, hs); |
| break; |
| } |
| |
| send_pkg_done(sender, data_type, data, len); |
| |
| /*FIXME: should I query complete and fifo empty here?*/ |
| |
| return ret; |
| } |
| |
| int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, |
| u32 len, bool hs) |
| { |
| unsigned long flags; |
| |
| if (!sender || !data || !len) { |
| DRM_ERROR("Invalid parameters\n"); |
| return -EINVAL; |
| } |
| |
| spin_lock_irqsave(&sender->lock, flags); |
| send_pkg(sender, MIPI_DSI_DCS_LONG_WRITE, data, len, hs); |
| spin_unlock_irqrestore(&sender->lock, flags); |
| |
| return 0; |
| } |
| |
| int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd, |
| u8 param, u8 param_num, bool hs) |
| { |
| u8 data[2]; |
| unsigned long flags; |
| u8 data_type; |
| |
| if (!sender) { |
| DRM_ERROR("Invalid parameter\n"); |
| return -EINVAL; |
| } |
| |
| data[0] = cmd; |
| |
| if (param_num) { |
| data_type = MIPI_DSI_DCS_SHORT_WRITE_PARAM; |
| data[1] = param; |
| } else { |
| data_type = MIPI_DSI_DCS_SHORT_WRITE; |
| data[1] = 0; |
| } |
| |
| spin_lock_irqsave(&sender->lock, flags); |
| send_pkg(sender, data_type, data, sizeof(data), hs); |
| spin_unlock_irqrestore(&sender->lock, flags); |
| |
| return 0; |
| } |
| |
| int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0, |
| u8 param1, u8 param_num, bool hs) |
| { |
| u8 data[2]; |
| unsigned long flags; |
| u8 data_type; |
| |
| if (!sender || param_num > 2) { |
| DRM_ERROR("Invalid parameter\n"); |
| return -EINVAL; |
| } |
| |
| switch (param_num) { |
| case 0: |
| data_type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM; |
| data[0] = 0; |
| data[1] = 0; |
| break; |
| case 1: |
| data_type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM; |
| data[0] = param0; |
| data[1] = 0; |
| break; |
| case 2: |
| data_type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM; |
| data[0] = param0; |
| data[1] = param1; |
| break; |
| } |
| |
| spin_lock_irqsave(&sender->lock, flags); |
| send_pkg(sender, data_type, data, sizeof(data), hs); |
| spin_unlock_irqrestore(&sender->lock, flags); |
| |
| return 0; |
| } |
| |
| int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data, |
| u32 len, bool hs) |
| { |
| unsigned long flags; |
| |
| if (!sender || !data || !len) { |
| DRM_ERROR("Invalid parameters\n"); |
| return -EINVAL; |
| } |
| |
| spin_lock_irqsave(&sender->lock, flags); |
| send_pkg(sender, MIPI_DSI_GENERIC_LONG_WRITE, data, len, hs); |
| spin_unlock_irqrestore(&sender->lock, flags); |
| |
| return 0; |
| } |
| |
| static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, u8 data_type, |
| u8 *data, u16 len, u32 *data_out, u16 len_out, bool hs) |
| { |
| unsigned long flags; |
| struct drm_device *dev = sender->dev; |
| int i; |
| u32 gen_data_reg; |
| int retry = MDFLD_DSI_READ_MAX_COUNT; |
| |
| if (!sender || !data_out || !len_out) { |
| DRM_ERROR("Invalid parameters\n"); |
| return -EINVAL; |
| } |
| |
| /** |
| * do reading. |
| * 0) send out generic read request |
| * 1) polling read data avail interrupt |
| * 2) read data |
| */ |
| spin_lock_irqsave(&sender->lock, flags); |
| |
| REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); |
| |
| if ((REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) |
| DRM_ERROR("Can NOT clean read data valid interrupt\n"); |
| |
| /*send out read request*/ |
| send_pkg(sender, data_type, data, len, hs); |
| |
| /*polling read data avail interrupt*/ |
| while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) { |
| udelay(100); |
| retry--; |
| } |
| |
| if (!retry) { |
| spin_unlock_irqrestore(&sender->lock, flags); |
| return -ETIMEDOUT; |
| } |
| |
| REG_WRITE(sender->mipi_intr_stat_reg, BIT(29)); |
| |
| /*read data*/ |
| if (hs) |
| gen_data_reg = sender->mipi_hs_gen_data_reg; |
| else |
| gen_data_reg = sender->mipi_lp_gen_data_reg; |
| |
| for (i = 0; i < len_out; i++) |
| *(data_out + i) = REG_READ(gen_data_reg); |
| |
| spin_unlock_irqrestore(&sender->lock, flags); |
| |
| return 0; |
| } |
| |
| int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd, |
| u32 *data, u16 len, bool hs) |
| { |
| if (!sender || !data || !len) { |
| DRM_ERROR("Invalid parameters\n"); |
| return -EINVAL; |
| } |
| |
| return __read_panel_data(sender, MIPI_DSI_DCS_READ, &cmd, 1, |
| data, len, hs); |
| } |
| |
| int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector, |
| int pipe) |
| { |
| struct mdfld_dsi_pkg_sender *pkg_sender; |
| struct mdfld_dsi_config *dsi_config = |
| mdfld_dsi_get_config(dsi_connector); |
| struct drm_device *dev = dsi_config->dev; |
| struct drm_psb_private *dev_priv = dev->dev_private; |
| const struct psb_offset *map = &dev_priv->regmap[pipe]; |
| u32 mipi_val = 0; |
| |
| if (!dsi_connector) { |
| DRM_ERROR("Invalid parameter\n"); |
| return -EINVAL; |
| } |
| |
| pkg_sender = dsi_connector->pkg_sender; |
| |
| if (!pkg_sender || IS_ERR(pkg_sender)) { |
| pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender), |
| GFP_KERNEL); |
| if (!pkg_sender) { |
| DRM_ERROR("Create DSI pkg sender failed\n"); |
| return -ENOMEM; |
| } |
| dsi_connector->pkg_sender = (void *)pkg_sender; |
| } |
| |
| pkg_sender->dev = dev; |
| pkg_sender->dsi_connector = dsi_connector; |
| pkg_sender->pipe = pipe; |
| pkg_sender->pkg_num = 0; |
| pkg_sender->panel_mode = 0; |
| pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE; |
| |
| /*init regs*/ |
| /* FIXME: should just copy the regmap ptr ? */ |
| pkg_sender->dpll_reg = map->dpll; |
| pkg_sender->dspcntr_reg = map->cntr; |
| pkg_sender->pipeconf_reg = map->conf; |
| pkg_sender->dsplinoff_reg = map->linoff; |
| pkg_sender->dspsurf_reg = map->surf; |
| pkg_sender->pipestat_reg = map->status; |
| |
| pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe); |
| pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe); |
| pkg_sender->mipi_hs_gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe); |
| pkg_sender->mipi_lp_gen_ctrl_reg = MIPI_LP_GEN_CTRL_REG(pipe); |
| pkg_sender->mipi_hs_gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe); |
| pkg_sender->mipi_gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe); |
| pkg_sender->mipi_data_addr_reg = MIPI_DATA_ADD_REG(pipe); |
| pkg_sender->mipi_data_len_reg = MIPI_DATA_LEN_REG(pipe); |
| pkg_sender->mipi_cmd_addr_reg = MIPI_CMD_ADD_REG(pipe); |
| pkg_sender->mipi_cmd_len_reg = MIPI_CMD_LEN_REG(pipe); |
| |
| /*init lock*/ |
| spin_lock_init(&pkg_sender->lock); |
| |
| if (mdfld_get_panel_type(dev, pipe) != TC35876X) { |
| /** |
| * For video mode, don't enable DPI timing output here, |
| * will init the DPI timing output during mode setting. |
| */ |
| mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX; |
| |
| if (pipe == 0) |
| mipi_val |= 0x2; |
| |
| REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi_val); |
| REG_READ(MIPI_PORT_CONTROL(pipe)); |
| |
| /* do dsi controller init */ |
| mdfld_dsi_controller_init(dsi_config, pipe); |
| } |
| |
| return 0; |
| } |
| |
| void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender) |
| { |
| if (!sender || IS_ERR(sender)) |
| return; |
| |
| /*free*/ |
| kfree(sender); |
| } |
| |
| |