malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
+ if (kfb->is_va)
+ ctrl |= L_TBU_EN;
malidp_write32_mask(reg, BLK_CONTROL, ctrl_mask, ctrl);
}
fb->pitches[i] & 0xFFFF);
}
+ if (kfb->is_va)
+ ctrl |= LW_TBU_EN;
+
malidp_write32(reg, LAYER_FMT, kfb->format_caps->hw_id);
malidp_write32(reg, BLK_IN_SIZE, HV_SIZE(st->hsize, st->vsize));
malidp_write32(reg, BLK_INPUT_ID0, to_d71_input_id(&state->inputs[0]));
table->n_formats = ARRAY_SIZE(d71_format_caps_table);
}
+static int d71_connect_iommu(struct komeda_dev *mdev)
+{
+ struct d71_dev *d71 = mdev->chip_data;
+ u32 __iomem *reg = d71->gcu_addr;
+ u32 check_bits = (d71->num_pipelines == 2) ?
+ GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0;
+ int i, ret;
+
+ if (!d71->integrates_tbu)
+ return -1;
+
+ malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_CONNECT_MODE);
+
+ ret = dp_wait_cond(has_bits(check_bits, malidp_read32(reg, BLK_STATUS)),
+ 100, 1000, 1000);
+ if (ret < 0) {
+ DRM_ERROR("timed out connecting to TCU!\n");
+ malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE);
+ return ret;
+ }
+
+ for (i = 0; i < d71->num_pipelines; i++)
+ malidp_write32_mask(d71->pipes[i]->lpu_addr, LPU_TBU_CONTROL,
+ LPU_TBU_CTRL_TLBPEN, LPU_TBU_CTRL_TLBPEN);
+ return 0;
+}
+
+static int d71_disconnect_iommu(struct komeda_dev *mdev)
+{
+ struct d71_dev *d71 = mdev->chip_data;
+ u32 __iomem *reg = d71->gcu_addr;
+ u32 check_bits = (d71->num_pipelines == 2) ?
+ GCU_STATUS_TCS0 | GCU_STATUS_TCS1 : GCU_STATUS_TCS0;
+ int ret;
+
+ malidp_write32_mask(reg, BLK_CONTROL, 0x7, TBU_DISCONNECT_MODE);
+
+ ret = dp_wait_cond(((malidp_read32(reg, BLK_STATUS) & check_bits) == 0),
+ 100, 1000, 1000);
+ if (ret < 0) {
+ DRM_ERROR("timed out disconnecting from TCU!\n");
+ malidp_write32_mask(reg, BLK_CONTROL, 0x7, INACTIVE_MODE);
+ }
+
+ return ret;
+}
+
static const struct komeda_dev_funcs d71_chip_funcs = {
.init_format_table = d71_init_fmt_tbl,
.enum_resources = d71_enum_resources,
.on_off_vblank = d71_on_off_vblank,
.change_opmode = d71_change_opmode,
.flush = d71_flush,
+ .connect_iommu = d71_connect_iommu,
+ .disconnect_iommu = d71_disconnect_iommu,
};
const struct komeda_dev_funcs *
*
*/
#include <linux/io.h>
+#include <linux/iommu.h>
#include <linux/of_device.h>
#include <linux/of_graph.h>
#include <linux/platform_device.h>
dev->dma_parms = &mdev->dma_parms;
dma_set_max_seg_size(dev, DMA_BIT_MASK(32));
+ mdev->iommu = iommu_get_domain_for_dev(mdev->dev);
+ if (!mdev->iommu)
+ DRM_INFO("continue without IOMMU support!\n");
+
+ if (mdev->iommu && mdev->funcs->connect_iommu) {
+ err = mdev->funcs->connect_iommu(mdev);
+ if (err) {
+ mdev->iommu = NULL;
+ goto err_cleanup;
+ }
+ }
+
err = sysfs_create_group(&dev->kobj, &komeda_sysfs_attr_group);
if (err) {
DRM_ERROR("create sysfs group failed.\n");
debugfs_remove_recursive(mdev->debugfs_root);
#endif
+ if (mdev->iommu && mdev->funcs->disconnect_iommu)
+ mdev->funcs->disconnect_iommu(mdev);
+ mdev->iommu = NULL;
+
for (i = 0; i < mdev->n_pipelines; i++) {
komeda_pipeline_destroy(mdev, mdev->pipelines[i]);
mdev->pipelines[i] = NULL;
int (*enum_resources)(struct komeda_dev *mdev);
/** @cleanup: call to chip to cleanup komeda_dev->chip data */
void (*cleanup)(struct komeda_dev *mdev);
+ /** @connect_iommu: Optional, connect to external iommu */
+ int (*connect_iommu)(struct komeda_dev *mdev);
+ /** @disconnect_iommu: Optional, disconnect to external iommu */
+ int (*disconnect_iommu)(struct komeda_dev *mdev);
/**
* @irq_handler:
*
*/
void *chip_data;
+ /** @iommu: iommu domain */
+ struct iommu_domain *iommu;
+
/** @debugfs_root: root directory of komeda debugfs */
struct dentry *debugfs_root;
};
goto err_cleanup;
}
+ kfb->is_va = mdev->iommu ? true : false;
+
return &kfb->base;
err_cleanup:
* extends drm_format_info for komeda specific information
*/
const struct komeda_format_caps *format_caps;
+ /** @is_va: if smmu is enabled, it will be true */
+ bool is_va;
/** @aligned_w: aligned frame buffer width */
u32 aligned_w;
/** @aligned_h: aligned frame buffer height */