blob: 3bcd0a2aaa79fe0cd79abde9ef0c02b230652e0e [file] [log] [blame]
/*
* Core driver for the Paintbox programmable IPU
*
* Copyright (C) 2015 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*/
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/miscdevice.h>
#ifdef CONFIG_MNH_THERMAL
#include <linux/mnh_freq_cooling.h>
#endif
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <soc/mnh/mnh-trace.h>
#include <uapi/paintbox.h>
#include "paintbox-bif.h"
#include "paintbox-common.h"
#include "paintbox-debug.h"
#include "paintbox-dma.h"
#include "paintbox-fpga.h"
#include "paintbox-io.h"
#include "paintbox-irq.h"
#include "paintbox-lbp.h"
#include "paintbox-mipi.h"
#include "paintbox-mmu.h"
#include "paintbox-pmon.h"
#include "paintbox-power.h"
#include "paintbox-regs.h"
#include "paintbox-sim-regs.h"
#include "paintbox-stp.h"
#include "paintbox-stp-pc-histogram.h"
#include "paintbox-stp-sim.h"
#include "paintbox-stp-sram.h"
static int paintbox_open(struct inode *ip, struct file *fp)
{
struct paintbox_session *session;
struct paintbox_data *pb;
struct miscdevice *m = fp->private_data;
#ifdef CONFIG_PAINTBOX_DEBUG
ktime_t start_time;
#endif
pb = container_of(m, struct paintbox_data, misc_device);
#ifdef CONFIG_PAINTBOX_DEBUG
if (pb->stats.ioctl_time_enabled)
start_time = ktime_get_boottime();
#endif
session = kzalloc(sizeof(struct paintbox_session), GFP_KERNEL);
if (!session)
return -ENOMEM;
session->dev = pb;
INIT_LIST_HEAD(&session->irq_list);
INIT_LIST_HEAD(&session->dma_list);
INIT_LIST_HEAD(&session->stp_list);
INIT_LIST_HEAD(&session->lbp_list);
INIT_LIST_HEAD(&session->mipi_input_list);
INIT_LIST_HEAD(&session->mipi_output_list);
INIT_LIST_HEAD(&session->wait_list);
init_completion(&session->release_completion);
fp->private_data = session;
mutex_lock(&pb->lock);
pb->session_count++;
mutex_unlock(&pb->lock);
#ifdef CONFIG_PAINTBOX_DEBUG
if (pb->stats.ioctl_time_enabled)
paintbox_debug_log_non_ioctl_stats(pb, PB_STATS_OPEN, start_time,
ktime_get_boottime(), 0);
#endif
return 0;
}
static int paintbox_release(struct inode *ip, struct file *fp)
{
struct paintbox_session *session = fp->private_data;
struct paintbox_data *pb = session->dev;
struct paintbox_irq *irq, *irq_next;
#ifdef CONFIG_PAINTBOX_DEBUG
ktime_t start_time;
if (pb->stats.ioctl_time_enabled)
start_time = ktime_get_boottime();
#endif
mutex_lock(&pb->lock);
/* TODO: Cleanup release sequence. b/62372748 */
paintbox_mipi_release(pb, session);
paintbox_dma_release(pb, session);
paintbox_stp_release(pb, session);
/* Disable any interrupts associated with the session */
list_for_each_entry_safe(irq, irq_next, &session->irq_list,
session_entry)
release_interrupt(pb, session, irq);
paintbox_lbp_release(pb, session);
paintbox_irq_wait_for_release_complete(pb, session);
/* free any pmon allocations */
if (pb->bif.pmon_session == session)
pb->bif.pmon_session = NULL;
if (pb->mmu.pmon_session == session)
pb->mmu.pmon_session = NULL;
if (pb->dma.pmon_session == session)
pb->dma.pmon_session = NULL;
if (WARN_ON(--pb->session_count < 0))
pb->session_count = 0;
mutex_unlock(&pb->lock);
kfree(session);
#ifdef CONFIG_PAINTBOX_DEBUG
if (pb->stats.ioctl_time_enabled)
paintbox_debug_log_non_ioctl_stats(pb, PB_STATS_CLOSE, start_time,
ktime_get_boottime(), 0);
#endif
return 0;
}
static long paintbox_get_caps_ioctl(struct paintbox_data *pb,
struct paintbox_session *session, unsigned long arg)
{
struct ipu_capabilities caps;
uint32_t version;
memset(&caps, 0, sizeof(caps));
caps.num_lbps = pb->lbp.num_lbps;
caps.num_stps = pb->stp.num_stps;
caps.num_dma_channels = pb->dma.num_channels;
caps.num_interrupts = pb->io.num_interrupts;
version = readl(pb->reg_base + IPU_VERSION);
caps.version_major = (version & IPU_VERSION_MAJOR_MASK) >>
IPU_VERSION_MAJOR_SHIFT;
caps.version_minor = (version & IPU_VERSION_MINOR_MASK) >>
IPU_VERSION_MAJOR_SHIFT;
caps.version_build = version & IPU_VERSION_INCR_MASK;
caps.is_fpga = !!(version & IPU_VERSION_FPGA_BUILD_MASK);
#ifdef CONFIG_PAINTBOX_SIMULATOR_SUPPORT
caps.is_simulator = true;
#endif
#ifdef CONFIG_PAINTBOX_IOMMU
caps.iommu_enabled = pb->mmu.iommu_enabled;
#endif
caps.hardware_id = pb->hardware_id;
if (copy_to_user((void __user *)arg, &caps, sizeof(caps)))
return -EFAULT;
return 0;
}
#ifdef CONFIG_MNH_THERMAL
static long paintbox_ipu_reset_ioctl(struct paintbox_data *pb,
struct paintbox_session *session, unsigned long arg)
{
int ret;
mutex_lock(&pb->lock);
if (pb->session_count > 1) {
dev_warn(&pb->pdev->dev,
"%s: ignoring reset request: multiple active sessions\n",
__func__);
ret = -EBUSY;
goto unlock;
}
paintbox_io_disable_interrupt(pb, ~0ULL);
mnh_ipu_reset();
paintbox_io_apb_post_ipu_reset(pb);
/* TODO(showarth): bif post ipu reset */
paintbox_dma_post_ipu_reset(pb);
paintbox_lbp_post_ipu_reset(pb);
paintbox_mipi_post_ipu_reset(pb);
/* TODO(showarth): mmu post ipu reset */
paintbox_stp_post_ipu_reset(pb);
ret = 0;
unlock:
mutex_unlock(&pb->lock);
return ret;
}
#endif
static long paintbox_ioctl(struct file *fp, unsigned int cmd,
unsigned long arg)
{
struct paintbox_session *session = fp->private_data;
struct paintbox_data *pb = session->dev;
int ret;
#ifdef CONFIG_PAINTBOX_DEBUG
ktime_t start_time;
if (pb->stats.ioctl_time_enabled)
start_time = ktime_get_boottime();
#endif
switch (cmd) {
case PB_GET_IPU_CAPABILITIES:
ret = paintbox_get_caps_ioctl(pb, session, arg);
break;
case PB_ALLOCATE_INTERRUPT:
ret = allocate_interrupt_ioctl(pb, session, arg);
break;
case PB_WAIT_FOR_INTERRUPT:
ret = wait_for_interrupt_ioctl(pb, session, arg);
break;
case PB_RELEASE_INTERRUPT:
ret = release_interrupt_ioctl(pb, session, arg);
break;
case PB_FLUSH_INTERRUPTS:
ret = paintbox_flush_interrupt_ioctl(pb, session, arg);
break;
case PB_FLUSH_ALL_INTERRUPTS:
ret = paintbox_flush_all_interrupts_ioctl(pb, session, arg);
break;
case PB_ALLOCATE_DMA_CHANNEL:
ret = allocate_dma_channel_ioctl(pb, session, arg);
break;
case PB_BIND_DMA_INTERRUPT:
ret = bind_dma_interrupt_ioctl(pb, session, arg);
break;
case PB_UNBIND_DMA_INTERRUPT:
ret = unbind_dma_interrupt_ioctl(pb, session, arg);
break;
case PB_START_DMA_TRANSFER:
ret = start_dma_transfer_ioctl(pb, session, arg);
break;
case PB_STOP_DMA_TRANSFER:
ret = stop_dma_transfer_ioctl(pb, session, arg);
break;
case PB_RELEASE_DMA_CHANNEL:
ret = release_dma_channel_ioctl(pb, session, arg);
break;
case PB_GET_COMPLETED_UNREAD_COUNT:
ret = get_completed_transfer_count_ioctl(pb, session, arg);
break;
case PB_FLUSH_DMA_TRANSFERS:
ret = flush_dma_transfers_ioctl(pb, session, arg);
break;
case PB_ALLOCATE_LINE_BUFFER_POOL:
ret = allocate_lbp_ioctl(pb, session, arg);
break;
case PB_SETUP_LINE_BUFFER:
ret = setup_lb_ioctl(pb, session, arg);
break;
case PB_RELEASE_LINE_BUFFER_POOL:
ret = release_lbp_ioctl(pb, session, arg);
break;
case PB_RESET_LINE_BUFFER_POOL:
ret = reset_lbp_ioctl(pb, session, arg);
break;
case PB_RESET_LINE_BUFFER:
ret = reset_lb_ioctl(pb, session, arg);
break;
case PB_WRITE_LBP_MEMORY:
ret = write_lbp_memory_ioctl(pb, session, arg);
break;
case PB_READ_LBP_MEMORY:
ret = read_lbp_memory_ioctl(pb, session, arg);
break;
case PB_ALLOCATE_PROCESSOR:
ret = allocate_stp_ioctl(pb, session, arg);
break;
case PB_SETUP_PROCESSOR:
ret = setup_stp_ioctl(pb, session, arg);
break;
case PB_WRITE_STP_MEMORY:
ret = write_stp_scalar_sram_ioctl(pb, session, arg);
break;
case PB_READ_STP_MEMORY:
ret = read_stp_scalar_sram_ioctl(pb, session, arg);
break;
case PB_WRITE_VECTOR_SRAM_COORDINATES:
ret = write_stp_vector_sram_coordinates_ioctl(pb, session, arg);
break;
case PB_WRITE_VECTOR_SRAM_REPLICATE:
ret = write_stp_vector_sram_replicate_ioctl(pb, session, arg);
break;
case PB_READ_VECTOR_SRAM_COORDINATES:
ret = read_stp_vector_sram_coordinates_ioctl(pb, session, arg);
break;
case PB_START_PROCESSOR:
ret = start_stp_ioctl(pb, session, arg);
break;
case PB_STOP_PROCESSOR:
ret = stop_stp_ioctl(pb, session, arg);
break;
case PB_RESUME_PROCESSOR:
ret = resume_stp_ioctl(pb, session, arg);
break;
case PB_RESET_PROCESSOR:
ret = reset_stp_ioctl(pb, session, arg);
break;
case PB_RESET_ALL_PROCESSORS:
ret = paintbox_reset_all_stp_ioctl(pb, session);
break;
case PB_GET_PROGRAM_STATE:
ret = get_program_state_ioctl(pb, session, arg);
break;
case PB_GET_ALL_PROCESSOR_STATES:
ret = paintbox_get_all_processor_states(pb, session, arg);
break;
case PB_RELEASE_PROCESSOR:
ret = release_stp_ioctl(pb, session, arg);
break;
case PB_WAIT_FOR_ALL_PROCESSOR_IDLE:
#ifdef CONFIG_PAINTBOX_SIMULATOR_SUPPORT
ret = sim_wait_for_idle_ioctl(pb, session, arg);
#else
/* The simulator requires additional processing after the DMA
* interrupt before the processor goes idle. This processing
* is fast enough on the actual hardware that we do not need
* to poll for idle.
*/
ret = 0;
#endif
break;
case PB_GET_PROCESSOR_IDLE:
#ifdef CONFIG_PAINTBOX_SIMULATOR_SUPPORT
ret = sim_get_stp_idle_ioctl(pb, session, arg);
#else
ret = -EINVAL;
#endif
break;
case PB_BIND_STP_INTERRUPT:
ret = bind_stp_interrupt_ioctl(pb, session, arg);
break;
case PB_UNBIND_STP_INTERRUPT:
ret = unbind_stp_interrupt_ioctl(pb, session, arg);
break;
case PB_STP_PC_HISTOGRAM_CLEAR:
ret = stp_pc_histogram_clear_ioctl(pb, session, arg);
break;
case PB_STP_PC_HISTOGRAM_ENABLE:
ret = stp_pc_histogram_enable_ioctl(pb, session, arg);
break;
case PB_STP_PC_HISTOGRAM_READ:
ret = stp_pc_histogram_read_ioctl(pb, session, arg);
break;
case PB_SETUP_DMA_TRANSFER:
ret = setup_dma_transfer_ioctl(pb, session, arg);
break;
case PB_READ_DMA_TRANSFER:
ret = read_dma_transfer_ioctl(pb, session, arg);
break;
case PB_ALLOCATE_MIPI_IN_STREAM:
ret = allocate_mipi_input_stream_ioctl(pb, session, arg);
break;
case PB_RELEASE_MIPI_IN_STREAM:
ret = release_mipi_stream_ioctl(pb, session, arg, true);
break;
case PB_SETUP_MIPI_IN_STREAM:
ret = setup_mipi_stream_ioctl(pb, session, arg, true);
break;
case PB_ENABLE_MIPI_IN_STREAM:
ret = enable_mipi_stream_ioctl(pb, session, arg, true);
break;
case PB_DISABLE_MIPI_IN_STREAM:
ret = disable_mipi_stream_ioctl(pb, session, arg, true);
break;
case PB_GET_MIPI_IN_FRAME_NUMBER:
ret = get_mipi_frame_number_ioctl(pb, session, arg);
break;
case PB_CLEANUP_MIPI_IN_STREAM:
ret = cleanup_mipi_stream_ioctl(pb, session, arg, true);
break;
case PB_ALLOCATE_MIPI_OUT_STREAM:
ret = allocate_mipi_output_stream_ioctl(pb, session, arg);
break;
case PB_RELEASE_MIPI_OUT_STREAM:
ret = release_mipi_stream_ioctl(pb, session, arg, false);
break;
case PB_SETUP_MIPI_OUT_STREAM:
ret = setup_mipi_stream_ioctl(pb, session, arg, false);
break;
case PB_ENABLE_MIPI_OUT_STREAM:
ret = enable_mipi_stream_ioctl(pb, session, arg, false);
break;
case PB_DISABLE_MIPI_OUT_STREAM:
ret = disable_mipi_stream_ioctl(pb, session, arg, false);
break;
case PB_CLEANUP_MIPI_OUT_STREAM:
ret = cleanup_mipi_stream_ioctl(pb, session, arg, false);
break;
case PB_BIND_MIPI_IN_INTERRUPT:
ret = bind_mipi_interrupt_ioctl(pb, session, arg, true);
break;
case PB_UNBIND_MIPI_IN_INTERRUPT:
ret = unbind_mipi_interrupt_ioctl(pb, session, arg, true);
break;
case PB_BIND_MIPI_OUT_INTERRUPT:
ret = bind_mipi_interrupt_ioctl(pb, session, arg, false);
break;
case PB_UNBIND_MIPI_OUT_INTERRUPT:
ret = unbind_mipi_interrupt_ioctl(pb, session, arg, false);
break;
case PB_ENABLE_MIPI_IN_STREAMS:
ret = paintbox_mipi_enable_multiple_ioctl(pb, session, arg,
true /* is_input */);
break;
case PB_DISABLE_MIPI_IN_STREAMS:
ret = paintbox_mipi_disable_multiple_ioctl(pb, session, arg,
true /* is_input */);
break;
case PB_ENABLE_MIPI_OUT_STREAMS:
ret = paintbox_mipi_enable_multiple_ioctl(pb, session, arg,
false /* is_input */);
break;
case PB_DISABLE_MIPI_OUT_STREAMS:
ret = paintbox_mipi_disable_multiple_ioctl(pb, session, arg,
false /* is_input */);
break;
case PB_WAIT_FOR_MIPI_INPUT_QUIESCENCE:
ret = paintbox_mipi_input_wait_for_quiescence_ioctl(pb, session,
arg);
break;
#ifdef CONFIG_MNH_THERMAL
case PB_RESET_IPU:
ret = paintbox_ipu_reset_ioctl(pb, session, arg);
break;
#endif
case PB_PMON_ALLOCATE:
ret = pmon_allocate_ioctl(pb, session, arg);
break;
case PB_PMON_RELEASE:
ret = pmon_release_ioctl(pb, session, arg);
break;
case PB_PMON_CONFIG_WRITE:
ret = pmon_config_write_ioctl(pb, session, arg);
break;
case PB_PMON_DATA_READ:
ret = pmon_data_read_ioctl(pb, session, arg);
break;
case PB_PMON_DATA_WRITE:
ret = pmon_data_write_ioctl(pb, session, arg);
break;
case PB_PMON_ENABLE:
ret = pmon_enable_ioctl(pb, session, arg);
break;
#ifdef CONFIG_PAINTBOX_TEST_SUPPORT
case PB_TEST_DMA_RESET:
ret = dma_test_reset_ioctl(pb, session, arg);
break;
case PB_TEST_DMA_CHANNEL_RESET:
ret = dma_test_channel_reset_ioctl(pb, session, arg);
break;
case PB_TEST_MIPI_IN_RESET_STREAM:
ret = mipi_test_stream_reset_ioctl(pb, session, arg, true);
break;
case PB_TEST_MIPI_OUT_RESET_STREAM:
ret = mipi_test_stream_reset_ioctl(pb, session, arg, false);
break;
case PB_TEST_LBP_BROADCAST_WRITE_MEMORY:
ret = lbp_test_broadcast_write_memory_ioctl(pb, session, arg);
break;
#else
case PB_TEST_DMA_RESET:
case PB_TEST_DMA_CHANNEL_RESET:
case PB_TEST_MIPI_IN_RESET_STREAM:
case PB_TEST_MIPI_OUT_RESET_STREAM:
case PB_TEST_LBP_BROADCAST_WRITE_MEMORY:
ret = -EINVAL;
break;
#endif
default:
dev_err(&pb->pdev->dev, "%s: unknown ioctl 0x%0x\n", __func__,
cmd);
return -EINVAL;
}
#ifdef CONFIG_PAINTBOX_DEBUG
if (pb->stats.ioctl_time_enabled)
paintbox_debug_log_ioctl_stats(pb, cmd, start_time,
ktime_get_boottime());
#endif
return ret;
}
static const struct file_operations paintbox_fops = {
.owner = THIS_MODULE,
.open = paintbox_open,
.release = paintbox_release,
.unlocked_ioctl = paintbox_ioctl,
};
static int paintbox_get_capabilities(struct paintbox_data *pb)
{
uint64_t hardware_id;
uint32_t val;
uint8_t major, minor, build;
bool is_fpga;
int ret;
val = readl(pb->reg_base + IPU_VERSION);
major = (val & IPU_VERSION_MAJOR_MASK) >> IPU_VERSION_MAJOR_SHIFT;
minor = (val & IPU_VERSION_MINOR_MASK) >> IPU_VERSION_MAJOR_SHIFT;
build = val & IPU_VERSION_INCR_MASK;
is_fpga = !!(val & IPU_VERSION_FPGA_BUILD_MASK);
val = readl(pb->reg_base + IPU_CAP);
pb->stp.num_stps = val & IPU_CAP_NUM_STP_MASK;
pb->lbp.num_lbps = (val & IPU_CAP_NUM_LBP_MASK) >>
IPU_CAP_NUM_LBP_SHIFT;
pb->dma.num_channels = readl(pb->dma.dma_base + DMA_CAP0) &
DMA_CAP0_MAX_DMA_CHAN_MASK;
val = readl(pb->io_ipu.ipu_base + MPI_CAP);
pb->io_ipu.num_mipi_input_streams = (val & MPI_CAP_MAX_STRM_MASK) >>
MPI_CAP_MAX_STRM_SHIFT;
pb->io_ipu.num_mipi_input_interfaces = val & MPI_CAP_MAX_IFC_MASK;
val = readl(pb->io_ipu.ipu_base + MPO_CAP);
pb->io_ipu.num_mipi_output_streams = (val & MPO_CAP_MAX_STRM_MASK) >>
MPO_CAP_MAX_STRM_SHIFT;
pb->io_ipu.num_mipi_output_interfaces = val & MPO_CAP_MAX_IFC_MASK;
ret = of_property_read_u64(pb->pdev->dev.of_node, "hardware-id",
&hardware_id);
if (ret < 0) {
dev_err(&pb->pdev->dev,
"%s: hardware-id not set in device tree, err %d\n",
__func__, ret);
return ret;
}
pb->hardware_id = (uint32_t)hardware_id;
#if defined(CONFIG_PAINTBOX_SIMULATOR_SUPPORT)
dev_dbg(&pb->pdev->dev,
"Paintbox IPU Version %u.%u.%u Simulator Hardware ID %u\n",
major, minor, build, pb->hardware_id);
#else
dev_dbg(&pb->pdev->dev,
"Paintbox IPU Version %u.%u.%u %s Hardware ID %u\n",
major, minor, build, is_fpga ? "FPGA" : "",
pb->hardware_id);
#endif
dev_dbg(&pb->pdev->dev,
"STPs %u LBPs %u DMA Channels %u MIPI Input Streams %u MIPI Output Streams %u\n",
pb->stp.num_stps, pb->lbp.num_lbps,
pb->dma.num_channels, pb->io_ipu.num_mipi_input_streams,
pb->io_ipu.num_mipi_output_streams);
return 0;
}
static int paintbox_probe(struct platform_device *pdev)
{
int ret;
struct resource *r;
struct paintbox_data *pb;
#ifdef CONFIG_PAINTBOX_DEBUG
ktime_t start_time = ktime_get_boottime();
#endif
mnh_trace(MNH_TRACE_PAINTBOX_PROBE);
pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
if (pb == NULL)
return -ENOMEM;
pb->pdev = pdev;
mutex_init(&pb->lock);
spin_lock_init(&pb->irq_lock);
paintbox_debug_init(pb);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (r == NULL) {
dev_err(&pdev->dev, "platform_get_resource failed\n");
return -ENODEV;
}
pb->reg_base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
if (pb->reg_base == NULL) {
dev_err(&pdev->dev, "unable to remap MMIO\n");
return -ENOMEM;
}
ret = paintbox_fpga_init(pb);
if (ret < 0)
return ret;
pb->io.irq = platform_get_irq(pdev, 0);
if (pb->io.irq < 0) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
return -ENODEV;
}
platform_set_drvdata(pdev, pb);
#if CONFIG_PAINTBOX_VERSION_MAJOR >= 1
pb->io.aon_base = pb->reg_base + IPU_CSR_AON_OFFSET;
#else
/* Easel doesn't have an AON group. But instead of ifdef'ing accesses
* to all of the registers which have moved from the APB group to the
* AON group in Canvas, we use the AON base across the board and point
* it to the APB base for Easel.
*/
pb->io.aon_base = pb->reg_base + IPU_CSR_APB_OFFSET;
#endif
pb->io.apb_base = pb->reg_base + IPU_CSR_APB_OFFSET;
pb->io.axi_base = pb->reg_base + IPU_CSR_AXI_OFFSET;
pb->io_ipu.ipu_base = pb->reg_base + IPU_CSR_IO_OFFSET;
pb->dma.dma_base = pb->reg_base + IPU_CSR_DMA_OFFSET;
pb->stp.reg_base = pb->reg_base + IPU_CSR_STP_OFFSET;
pb->lbp.reg_base = pb->reg_base + IPU_CSR_LBP_OFFSET;
#ifdef CONFIG_PAINTBOX_SIMULATOR_SUPPORT
pb->sim_base = pb->reg_base + SIM_GROUP_OFFSET;
#endif
paintbox_pm_enable_io(pb);
ret = paintbox_get_capabilities(pb);
if (ret < 0)
return ret;
ret = paintbox_mipi_init(pb);
if (ret < 0)
return ret;
ret = paintbox_dma_init(pb);
if (ret < 0)
return ret;
ret = paintbox_lbp_init(pb);
if (ret < 0)
return ret;
ret = paintbox_stp_init(pb);
if (ret < 0)
return ret;
/* Initialize the IO APB block after the blocks that can generate
* interrupts. All interrupt sources need to be initialized first.
*/
ret = paintbox_io_apb_init(pb);
if (ret < 0)
return ret;
ret = paintbox_pm_init(pb);
if (ret < 0)
return ret;
paintbox_bif_init(pb);
ret = paintbox_mmu_init(pb);
if (ret < 0)
return ret;
/* Initialize the IRQ waiters after IO APB so the IRQ waiter code knows
* how many interrupts to allocate.
*/
ret = paintbox_irq_init(pb);
if (ret < 0)
return ret;
/* register the misc device */
pb->misc_device.minor = MISC_DYNAMIC_MINOR,
pb->misc_device.name = "paintbox",
pb->misc_device.fops = &paintbox_fops,
ret = misc_register(&pb->misc_device);
if (ret) {
pr_err("Failed to register misc device node (ret = %d)", ret);
return ret;
}
#ifdef CONFIG_PAINTBOX_DEBUG
pb->stats.probe_time = ktime_sub(ktime_get_boottime(), start_time);
#endif
mnh_trace(MNH_TRACE_PAINTBOX_PROBE_DONE);
return 0;
}
static int paintbox_remove(struct platform_device *pdev)
{
struct paintbox_data *pb = platform_get_drvdata(pdev);
misc_deregister(&pb->misc_device);
paintbox_irq_remove(pb);
paintbox_mmu_remove(pb);
paintbox_bif_remove(pb);
paintbox_mipi_remove(pb);
paintbox_dma_remove(pb);
paintbox_lbp_remove(pb);
paintbox_stp_remove(pb);
paintbox_pm_remove(pb);
paintbox_fpga_remove(pb);
paintbox_pm_disable_io(pb);
paintbox_io_apb_remove(pb);
devm_iounmap(&pdev->dev, pb->reg_base);
paintbox_debug_remove(pb);
mutex_destroy(&pb->lock);
kfree(pb);
return 0;
}
static const struct of_device_id paintbox_of_match[] = {
{ .compatible = "google,paintbox", },
{},
};
MODULE_DEVICE_TABLE(of, paintbox_of_match);
static struct platform_driver paintbox_driver = {
.probe = paintbox_probe,
.remove = paintbox_remove,
.driver = {
.name = "paintbox",
.of_match_table = paintbox_of_match,
}
};
module_platform_driver(paintbox_driver);
MODULE_AUTHOR("Google, Inc.");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Paintbox Driver");