https://source.android.com/docs/security/bulletin/2023-04-01 CVE-2022-4696 CVE-2023-20941 * tag 'ASB-2023-04-05_4.19-stable' of https://android.googlesource.com/kernel/common: UPSTREAM: ext4: fix kernel BUG in 'ext4_write_inline_data_end()' UPSTREAM: fsverity: don't drop pagecache at end of FS_IOC_ENABLE_VERITY UPSTREAM: fsverity: Remove WQ_UNBOUND from fsverity read workqueue BACKPORT: blk-mq: clear stale request in tags->rq[] before freeing one request pool Linux 4.19.279 HID: uhid: Over-ride the default maximum data buffer value with our own HID: core: Provide new max_buffer_size attribute to over-ride the default serial: 8250_em: Fix UART port type drm/i915: Don't use stolen memory for ring buffers with LLC x86/mm: Fix use of uninitialized buffer in sme_enable() fbdev: stifb: Provide valid pixelclock and add fb_check_var() checks ftrace: Fix invalid address access in lookup_rec() when index is 0 tracing: Make tracepoint lockdep check actually test something tracing: Check field value in hist_field_name() sh: intc: Avoid spurious sizeof-pointer-div warning drm/amdkfd: Fix an illegal memory access ext4: fix task hung in ext4_xattr_delete_inode ext4: fail ext4_iget if special inode unallocated jffs2: correct logic when creating a hole in jffs2_write_begin mmc: atmel-mci: fix race between stop command and start of next command media: m5mols: fix off-by-one loop termination error hwmon: (xgene) Fix use after free bug in xgene_hwmon_remove due to race condition hwmon: (adt7475) Fix masking of hysteresis registers hwmon: (adt7475) Display smoothing attributes in correct order ethernet: sun: add check for the mdesc_grab() net/iucv: Fix size of interrupt data net: usb: smsc75xx: Move packet length check to prevent kernel panic in skb_pull ipv4: Fix incorrect table ID in IOCTL path block: sunvdc: add check for mdesc_grab() returning NULL nvmet: avoid potential UAF in nvmet_req_complete() net: usb: smsc75xx: Limit packet length to skb->len nfc: st-nci: Fix use after free bug in ndlc_remove due to race condition net: phy: smsc: bail out in lan87xx_read_status if genphy_read_status fails net: tunnels: annotate lockless accesses to dev->needed_headroom qed/qed_dev: guard against a possible division by zero nfc: pn533: initialize struct pn533_out_arg properly tcp: tcp_make_synack() can be called from process context clk: HI655X: select REGMAP instead of depending on it fs: sysfs_emit_at: Remove PAGE_SIZE alignment check ext4: fix cgroup writeback accounting with fs-layer encryption UPSTREAM: ext4: fix another off-by-one fsmap error on 1k block filesystems Linux 4.19.278 ila: do not generate empty messages in ila_xlat_nl_cmd_get_mapping() nfc: fdp: add null check of devm_kmalloc_array in fdp_nci_i2c_read_device_properties net: caif: Fix use-after-free in cfusbl_device_notify() drm/i915: Don't use BAR mappings for ring buffers with LLC tipc: improve function tipc_wait_for_cond() media: ov5640: Fix analogue gain control PCI: Add SolidRun vendor ID macintosh: windfarm: Use unsigned type for 1-bit bitfields alpha: fix R_ALPHA_LITERAL reloc for large modules MIPS: Fix a compilation issue Revert "spi: mt7621: Fix an error message in mt7621_spi_probe()" scsi: core: Remove the /proc/scsi/${proc_name} directory earlier kbuild: generate modules.order only in directories visited by obj-y/m kbuild: fix false-positive need-builtin calculation udf: Detect system inodes linked into directory hierarchy udf: Preserve link count of system files udf: Remove pointless union in udf_inode_info udf: reduce leakage of blocks related to named streams udf: Explain handling of load_nls() failure nfc: change order inside nfc_se_io error path ext4: zero i_disksize when initializing the bootloader inode ext4: fix WARNING in ext4_update_inline_data ext4: move where set the MAY_INLINE_DATA flag is set ext4: fix another off-by-one fsmap error on 1k block filesystems ext4: fix RENAME_WHITEOUT handling for inline directories x86/CPU/AMD: Disable XSAVES on AMD family 0x17 fs: prevent out-of-bounds array speculation when closing a file descriptor Linux 4.19.277 staging: rtl8192e: Remove call_usermodehelper starting RadioPower.sh staging: rtl8192e: Remove function ..dm_check_ac_dc_power calling a script wifi: cfg80211: Partial revert "wifi: cfg80211: Fix use after free for wext" Linux 4.19.276 thermal: intel: powerclamp: Fix cur_state for multi package system f2fs: fix cgroup writeback accounting with fs-layer encryption media: uvcvideo: Fix race condition with usb_kill_urb media: uvcvideo: Provide sync and async uvc_ctrl_status_event tcp: Fix listen() regression in 4.19.270 s390/setup: init jump labels before command line parsing s390/maccess: add no DAT mode to kernel_write Bluetooth: hci_sock: purge socket queues in the destruct() callback phy: rockchip-typec: Fix unsigned comparison with less than zero usb: uvc: Enumerate valid values for color matching USB: ene_usb6250: Allocate enough memory for full object usb: host: xhci: mvebu: Iterate over array indexes instead of using pointer math iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_config_word() iio: accel: mma9551_core: Prevent uninitialized variable in mma9551_read_status_word() tools/iio/iio_utils:fix memory leak mei: bus-fixup:upon error print return values of send and receive tty: serial: fsl_lpuart: disable the CTS when send break signal tty: fix out-of-bounds access in tty_driver_lookup_tty() media: uvcvideo: Silence memcpy() run-time false positive warnings media: uvcvideo: Handle errors from calls to usb_string media: uvcvideo: Handle cameras with invalid descriptors firmware/efi sysfb_efi: Add quirk for Lenovo IdeaPad Duet 3 tracing: Add NULL checks for buffer in ring_buffer_free_read_page() thermal: intel: quark_dts: fix error pointer dereference scsi: ipr: Work around fortify-string warning vc_screen: modify vcs_size() handling in vcs_read() tcp: tcp_check_req() can be called from process context ARM: dts: spear320-hmi: correct STMPE GPIO compatible nfc: fix memory leak of se_io context in nfc_genl_se_io 9p/rdma: unmap receive dma buffer in rdma_request()/post_recv() 9p/xen: fix connection sequence 9p/xen: fix version parsing net: fix __dev_kfree_skb_any() vs drop monitor netfilter: ctnetlink: fix possible refcount leak in ctnetlink_create_conntrack() watchdog: pcwd_usb: Fix attempting to access uninitialized memory watchdog: Fix kmemleak in watchdog_cdev_register watchdog: at91sam9_wdt: use devm_request_irq to avoid missing free_irq() in error path x86: um: vdso: Add '%rcx' and '%r11' to the syscall clobber list ubi: ubi_wl_put_peb: Fix infinite loop when wear-leveling work failed ubi: Fix UAF wear-leveling entry in eraseblk_count_seq_show() ubifs: ubifs_writepage: Mark page dirty after writing inode failed ubifs: dirty_cow_znode: Fix memleak in error handling path ubifs: Re-statistic cleaned znode count if commit failed ubi: Fix possible null-ptr-deref in ubi_free_volume() ubi: Fix unreferenced object reported by kmemleak in ubi_resize_volume() ubi: Fix use-after-free when volume resizing failed ubifs: Reserve one leb for each journal head while doing budget ubifs: do_rename: Fix wrong space budget when target inode's nlink > 1 ubifs: Fix wrong dirty space budget for dirty inode ubifs: Rectify space budget for ubifs_xrename() ubifs: Rectify space budget for ubifs_symlink() if symlink is encrypted ubi: ensure that VID header offset + VID header size <= alloc, size um: vector: Fix memory leak in vector_config pwm: stm32-lp: fix the check on arr and cmp registers update fs/jfs: fix shift exponent db_agl2size negative net/sched: Retire tcindex classifier kbuild: Port silent mode detection to future gnu make. wifi: ath9k: use proper statements in conditionals drm/radeon: Fix eDP for single-display iMac11,2 PCI: Avoid FLR for AMD FCH AHCI adapters scsi: ses: Fix slab-out-of-bounds in ses_intf_remove() scsi: ses: Fix possible desc_ptr out-of-bounds accesses scsi: ses: Fix possible addl_desc_ptr out-of-bounds accesses scsi: ses: Fix slab-out-of-bounds in ses_enclosure_data_process() scsi: ses: Don't attach if enclosure has no components scsi: qla2xxx: Fix erroneous link down scsi: qla2xxx: Fix link failure in NPIV environment ktest.pl: Add RUN_TIMEOUT option with default unlimited ktest.pl: Fix missing "end_monitor" when machine check fails ktest.pl: Give back console on Ctrt^C on monitor media: ipu3-cio2: Fix PM runtime usage_count in driver unbind mips: fix syscall_get_nr alpha: fix FEN fault handling rbd: avoid use-after-free in do_rbd_add() when rbd_dev_create() fails ARM: dts: exynos: correct TMU phandle in Odroid XU ARM: dts: exynos: correct TMU phandle in Exynos4 dm flakey: don't corrupt the zero page dm flakey: fix logic when corrupting a bio wifi: cfg80211: Fix use after free for wext wifi: rtl8xxxu: Use a longer retry limit of 48 ext4: refuse to create ea block when umounted ext4: optimize ea_inode block expansion ALSA: ice1712: Do not left ice->gpio_mutex locked in aureon_add_controls() irqdomain: Drop bogus fwspec-mapping error handling irqdomain: Fix disassociation race irqdomain: Fix association race ima: Align ima_file_mmap() parameters with mmap_file LSM hook Documentation/hw-vuln: Document the interaction between IBRS and STIBP x86/speculation: Allow enabling STIBP with legacy IBRS x86/microcode/AMD: Fix mixed steppings support x86/microcode/AMD: Add a @cpu parameter to the reloading functions x86/microcode/amd: Remove load_microcode_amd()'s bsp parameter x86/kprobes: Fix arch_check_optimized_kprobe check within optimized_kprobe range x86/kprobes: Fix __recover_optprobed_insn check optimizing logic x86/reboot: Disable SVM, not just VMX, when stopping CPUs x86/reboot: Disable virtualization in an emergency if SVM is supported x86/crash: Disable virt in core NMI crash handler to avoid double shootdown x86/virt: Force GIF=1 prior to disabling SVM (for reboot flows) udf: Fix file corruption when appending just after end of preallocated extent udf: Do not update file length for failed writes to inline files udf: Do not bother merging very long extents udf: Truncate added extents on failed expansion ocfs2: fix non-auto defrag path not working issue ocfs2: fix defrag path triggering jbd2 ASSERT f2fs: fix information leak in f2fs_move_inline_dirents() fs: hfsplus: fix UAF issue in hfsplus_put_super hfs: fix missing hfs_bnode_get() in __hfs_bnode_create ARM: dts: exynos: correct HDMI phy compatible in Exynos4 s390/kprobes: fix current_kprobe never cleared after kprobes reenter s390/kprobes: fix irq mask clobbering on kprobe reenter from post_handler s390: discard .interp section rtc: pm8xxx: fix set-alarm race firmware: coreboot: framebuffer: Ignore reserved pixel color bits wifi: rtl8xxxu: fixing transmisison failure for rtl8192eu dm cache: add cond_resched() to various workqueue loops dm thin: add cond_resched() to various workqueue loops pinctrl: at91: use devm_kasprintf() to avoid potential leaks regulator: s5m8767: Bounds check id indexing into arrays regulator: max77802: Bounds check regulator id against opmode ASoC: kirkwood: Iterate over array indexes instead of using pointer math docs/scripts/gdb: add necessary make scripts_gdb step drm/msm/dsi: Add missing check for alloc_ordered_workqueue drm/radeon: free iio for atombios when driver shutdown drm/amd/display: Fix potential null-deref in dm_resume net/mlx5: fw_tracer: Fix debug print ACPI: video: Fix Lenovo Ideapad Z570 DMI match m68k: Check syscall_trace_enter() return code net: bcmgenet: Add a check for oversized packets ACPI: Don't build ACPICA with '-Os' inet: fix fast path in __inet_hash_connect() wifi: brcmfmac: ensure CLM version is null-terminated to prevent stack-out-of-bounds x86/bugs: Reset speculation control settings on init timers: Prevent union confusion from unexpected restart_syscall() thermal: intel: Fix unsigned comparison with less than zero rcu: Suppress smp_processor_id() complaint in synchronize_rcu_expedited_wait() wifi: brcmfmac: Fix potential stack-out-of-bounds in brcmf_c_preinit_dcmds() ARM: dts: exynos: Use Exynos5420 compatible for the MIPI video phy udf: Define EFSCORRUPTED error code rpmsg: glink: Avoid infinite loop on intent for missing channel media: usb: siano: Fix use after free bugs caused by do_submit_urb media: i2c: ov7670: 0 instead of -EINVAL was returned media: rc: Fix use-after-free bugs caused by ene_tx_irqsim() media: i2c: ov772x: Fix memleak in ov772x_probe() powerpc: Remove linker flag from KBUILD_AFLAGS media: platform: ti: Add missing check for devm_regulator_get MIPS: vpe-mt: drop physical_memsize powerpc/rtas: ensure 4KB alignment for rtas_data_buf powerpc/rtas: make all exports GPL powerpc/pseries/lparcfg: add missing RTAS retry status handling clk: Honor CLK_OPS_PARENT_ENABLE in clk_core_is_enabled() powerpc/powernv/ioda: Skip unallocated resources when mapping to PE Input: ads7846 - don't check penirq immediately for 7845 Input: ads7846 - don't report pressure for ads7845 mtd: rawnand: sunxi: Fix the size of the last OOB region mfd: pcf50633-adc: Fix potential memleak in pcf50633_adc_async_read() selftests/ftrace: Fix bash specific "==" operator sparc: allow PM configs for sparc32 COMPILE_TEST perf tools: Fix auto-complete on aarch64 perf llvm: Fix inadvertent file creation gfs2: jdata writepage fix cifs: Fix warning and UAF when destroy the MR list cifs: Fix lost destroy smbd connection when MR allocate failed nfsd: fix race to check ls_layouts dm: remove flush_scheduled_work() during local_exit() hwmon: (mlxreg-fan) Return zero speed for broken fan spi: bcm63xx-hsspi: Fix multi-bit mode setting spi: bcm63xx-hsspi: fix pm_runtime scsi: aic94xx: Add missing check for dma_map_single() hwmon: (ltc2945) Handle error case in ltc2945_value_store gpio: vf610: connect GPIO label to dev name ASoC: soc-compress.c: fixup private_data on snd_soc_new_compress() drm/mediatek: Clean dangling pointer on bind error path drm/mediatek: Drop unbalanced obj unref gpu: host1x: Don't skip assigning syncpoints to channels drm/msm/dpu: Add check for pstates drm/msm: use strscpy instead of strncpy drm/mipi-dsi: Fix byte order of 16-bit DCS set/get brightness ALSA: hda/ca0132: minor fix for allocation size pinctrl: rockchip: Fix refcount leak in rockchip_pinctrl_parse_groups pinctrl: pinctrl-rockchip: Fix a bunch of kerneldoc misdemeanours drm/msm/hdmi: Add missing check for alloc_ordered_workqueue gpu: ipu-v3: common: Add of_node_put() for reference returned by of_graph_get_port_by_id() drm/vc4: dpi: Fix format mapping for RGB565 drm/vc4: dpi: Add option for inverting pixel clock and output enable drm: Clarify definition of the DRM_BUS_FLAG_(PIXDATA|SYNC)_* macros drm/bridge: megachips: Fix error handling in i2c_register_driver() drm: mxsfb: DRM_MXSFB should depend on ARCH_MXS || ARCH_MXC selftest: fib_tests: Always cleanup before exit irqchip/irq-bcm7120-l2: Set IRQ_LEVEL for level triggered interrupts irqchip/irq-brcmstb-l2: Set IRQ_LEVEL for level triggered interrupts can: esd_usb: Move mislocated storage of SJA1000_ECC_SEG bits in case of a bus error wifi: mac80211: make rate u32 in sta_set_rate_info_rx() crypto: crypto4xx - Call dma_unmap_page when done wifi: mwifiex: fix loop iterator in mwifiex_update_ampdu_txwinsize() wifi: iwl4965: Add missing check for create_singlethread_workqueue() wifi: iwl3945: Add missing check for create_singlethread_workqueue RISC-V: time: initialize hrtimer based broadcast clock event device m68k: /proc/hardware should depend on PROC_FS crypto: rsa-pkcs1pad - Use akcipher_request_complete rds: rds_rm_zerocopy_callback() correct order for list_add_tail() libbpf: Fix alen calculation in libbpf_nla_dump_errormsg() Bluetooth: L2CAP: Fix potential user-after-free irqchip/irq-mvebu-gicp: Fix refcount leak in mvebu_gicp_probe irqchip/alpine-msi: Fix refcount leak in alpine_msix_init_domains net/mlx5: Enhance debug print in page allocation failure powercap: fix possible name leak in powercap_register_zone() crypto: seqiv - Handle EBUSY correctly ACPI: battery: Fix missing NUL-termination with large strings wifi: ath9k: Fix potential stack-out-of-bounds write in ath9k_wmi_rsp_callback() wifi: ath9k: hif_usb: clean up skbs if ath9k_hif_usb_rx_stream() fails ath9k: htc: clean up statistics macros ath9k: hif_usb: simplify if-if to if-else wifi: ath9k: htc_hst: free skb in ath9k_htc_rx_msg() if there is no callback function wifi: orinoco: check return value of hermes_write_wordrec() ACPICA: nsrepair: handle cases without a return value correctly lib/mpi: Fix buffer overrun when SG is too long genirq: Fix the return type of kstat_cpu_irqs_sum() ACPICA: Drop port I/O validation for some regions wifi: wl3501_cs: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: cmdresp: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: main: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas: if_usb: don't call kfree_skb() under spin_lock_irqsave() wifi: libertas_tf: don't call kfree_skb() under spin_lock_irqsave() wifi: brcmfmac: unmap dma buffer in brcmf_msgbuf_alloc_pktid() wifi: brcmfmac: fix potential memory leak in brcmf_netdev_start_xmit() wifi: ipw2200: fix memory leak in ipw_wdev_init() wifi: ipw2x00: don't call dev_kfree_skb() under spin_lock_irqsave() ipw2x00: switch from 'pci_' to 'dma_' API wifi: rtlwifi: Fix global-out-of-bounds bug in _rtl8812ae_phy_set_txpower_limit() rtlwifi: fix -Wpointer-sign warning wifi: rtl8xxxu: don't call dev_kfree_skb() under spin_lock_irqsave() wifi: libertas: fix memory leak in lbs_init_adapter() wifi: rsi: Fix memory leak in rsi_coex_attach() block: bio-integrity: Copy flags when bio_integrity_payload is cloned blk-mq: remove stale comment for blk_mq_sched_mark_restart_hctx arm64: dts: mediatek: mt7622: Add missing pwm-cells to pwm node arm64: dts: amlogic: meson-gxl: add missing unit address to eth-phy-mux node name arm64: dts: amlogic: meson-gx: add missing unit address to rng node name arm64: dts: amlogic: meson-gx: add missing SCPI sensors compatible arm64: dts: amlogic: meson-axg: fix SCPI clock dvfs node name arm64: dts: meson-axg: enable SCPI arm64: dts: amlogic: meson-gx: fix SCPI clock dvfs node name ARM: imx: Call ida_simple_remove() for ida_simple_get ARM: dts: exynos: correct wr-active property in Exynos3250 Rinato ARM: OMAP1: call platform_device_put() in error case in omap1_dm_timer_init() arm64: dts: meson-gx: Fix the SCPI DVFS node name and unit address arm64: dts: meson-gx: Fix Ethernet MAC address unit name ARM: zynq: Fix refcount leak in zynq_early_slcr_init ARM: OMAP2+: Fix memory leak in realtime_counter_init() HID: asus: use spinlock to safely schedule workers HID: asus: use spinlock to protect concurrent accesses HID: asus: Remove check for same LED brightness on set Change-Id: Ie09175b59aef5de140e476316d94097cac7a3031
1221 lines
30 KiB
C
1221 lines
30 KiB
C
/*
|
|
* MIPI DSI Bus
|
|
*
|
|
* Copyright (C) 2012-2013, Samsung Electronics, Co., Ltd.
|
|
* Andrzej Hajda <a.hajda@samsung.com>
|
|
*
|
|
* 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, sub license, 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 NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
|
|
*/
|
|
|
|
#include <drm/drm_mipi_dsi.h>
|
|
|
|
#include <linux/device.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include <video/mipi_display.h>
|
|
|
|
/**
|
|
* DOC: dsi helpers
|
|
*
|
|
* These functions contain some common logic and helpers to deal with MIPI DSI
|
|
* peripherals.
|
|
*
|
|
* Helpers are provided for a number of standard MIPI DSI command as well as a
|
|
* subset of the MIPI DCS command set.
|
|
*/
|
|
|
|
static int mipi_dsi_device_match(struct device *dev, struct device_driver *drv)
|
|
{
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
/* attempt OF style match */
|
|
if (of_driver_match_device(dev, drv))
|
|
return 1;
|
|
|
|
/* compare DSI device and driver names */
|
|
if (!strcmp(dsi->name, drv->name))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mipi_dsi_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|
{
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
int err;
|
|
|
|
err = of_device_uevent_modalias(dev, env);
|
|
if (err != -ENODEV)
|
|
return err;
|
|
|
|
add_uevent_var(env, "MODALIAS=%s%s", MIPI_DSI_MODULE_PREFIX,
|
|
dsi->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops mipi_dsi_device_pm_ops = {
|
|
.runtime_suspend = pm_generic_runtime_suspend,
|
|
.runtime_resume = pm_generic_runtime_resume,
|
|
.suspend = pm_generic_suspend,
|
|
.resume = pm_generic_resume,
|
|
.freeze = pm_generic_freeze,
|
|
.thaw = pm_generic_thaw,
|
|
.poweroff = pm_generic_poweroff,
|
|
.restore = pm_generic_restore,
|
|
};
|
|
|
|
static struct bus_type mipi_dsi_bus_type = {
|
|
.name = "mipi-dsi",
|
|
.match = mipi_dsi_device_match,
|
|
.uevent = mipi_dsi_uevent,
|
|
.pm = &mipi_dsi_device_pm_ops,
|
|
};
|
|
|
|
static int of_device_match(struct device *dev, void *data)
|
|
{
|
|
return dev->of_node == data;
|
|
}
|
|
|
|
/**
|
|
* of_find_mipi_dsi_device_by_node() - find the MIPI DSI device matching a
|
|
* device tree node
|
|
* @np: device tree node
|
|
*
|
|
* Return: A pointer to the MIPI DSI device corresponding to @np or NULL if no
|
|
* such device exists (or has not been registered yet).
|
|
*/
|
|
struct mipi_dsi_device *of_find_mipi_dsi_device_by_node(struct device_node *np)
|
|
{
|
|
struct device *dev;
|
|
|
|
dev = bus_find_device(&mipi_dsi_bus_type, NULL, np, of_device_match);
|
|
|
|
return dev ? to_mipi_dsi_device(dev) : NULL;
|
|
}
|
|
EXPORT_SYMBOL(of_find_mipi_dsi_device_by_node);
|
|
|
|
static void mipi_dsi_dev_release(struct device *dev)
|
|
{
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
of_node_put(dev->of_node);
|
|
kfree(dsi);
|
|
}
|
|
|
|
static const struct device_type mipi_dsi_device_type = {
|
|
.release = mipi_dsi_dev_release,
|
|
};
|
|
|
|
static struct mipi_dsi_device *mipi_dsi_device_alloc(struct mipi_dsi_host *host)
|
|
{
|
|
struct mipi_dsi_device *dsi;
|
|
|
|
dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
|
|
if (!dsi)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
dsi->host = host;
|
|
dsi->dev.bus = &mipi_dsi_bus_type;
|
|
dsi->dev.parent = host->dev;
|
|
dsi->dev.type = &mipi_dsi_device_type;
|
|
|
|
device_initialize(&dsi->dev);
|
|
|
|
return dsi;
|
|
}
|
|
|
|
static int mipi_dsi_device_add(struct mipi_dsi_device *dsi)
|
|
{
|
|
struct mipi_dsi_host *host = dsi->host;
|
|
|
|
dev_set_name(&dsi->dev, "%s.%d", dev_name(host->dev), dsi->channel);
|
|
|
|
return device_add(&dsi->dev);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_OF)
|
|
static struct mipi_dsi_device *
|
|
of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
|
|
{
|
|
struct device *dev = host->dev;
|
|
struct mipi_dsi_device_info info = { };
|
|
int ret;
|
|
u32 reg;
|
|
|
|
if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
|
|
dev_err(dev, "modalias failure on %pOF\n", node);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ret = of_property_read_u32(node, "reg", ®);
|
|
if (ret) {
|
|
dev_err(dev, "device node %pOF has no valid reg property: %d\n",
|
|
node, ret);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
info.channel = reg;
|
|
info.node = of_node_get(node);
|
|
|
|
return mipi_dsi_device_register_full(host, &info);
|
|
}
|
|
#else
|
|
static struct mipi_dsi_device *
|
|
of_mipi_dsi_device_add(struct mipi_dsi_host *host, struct device_node *node)
|
|
{
|
|
return ERR_PTR(-ENODEV);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* mipi_dsi_device_register_full - create a MIPI DSI device
|
|
* @host: DSI host to which this device is connected
|
|
* @info: pointer to template containing DSI device information
|
|
*
|
|
* Create a MIPI DSI device by using the device information provided by
|
|
* mipi_dsi_device_info template
|
|
*
|
|
* Returns:
|
|
* A pointer to the newly created MIPI DSI device, or, a pointer encoded
|
|
* with an error
|
|
*/
|
|
struct mipi_dsi_device *
|
|
mipi_dsi_device_register_full(struct mipi_dsi_host *host,
|
|
const struct mipi_dsi_device_info *info)
|
|
{
|
|
struct mipi_dsi_device *dsi;
|
|
struct device *dev = host->dev;
|
|
int ret;
|
|
|
|
if (!info) {
|
|
dev_err(dev, "invalid mipi_dsi_device_info pointer\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
if (info->channel > 3) {
|
|
dev_err(dev, "invalid virtual channel: %u\n", info->channel);
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
dsi = mipi_dsi_device_alloc(host);
|
|
if (IS_ERR(dsi)) {
|
|
dev_err(dev, "failed to allocate DSI device %ld\n",
|
|
PTR_ERR(dsi));
|
|
return dsi;
|
|
}
|
|
|
|
dsi->dev.of_node = info->node;
|
|
dsi->channel = info->channel;
|
|
strlcpy(dsi->name, info->type, sizeof(dsi->name));
|
|
|
|
ret = mipi_dsi_device_add(dsi);
|
|
if (ret) {
|
|
dev_err(dev, "failed to add DSI device %d\n", ret);
|
|
kfree(dsi);
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
return dsi;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_device_register_full);
|
|
|
|
/**
|
|
* mipi_dsi_device_unregister - unregister MIPI DSI device
|
|
* @dsi: DSI peripheral device
|
|
*/
|
|
void mipi_dsi_device_unregister(struct mipi_dsi_device *dsi)
|
|
{
|
|
device_unregister(&dsi->dev);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_device_unregister);
|
|
|
|
static DEFINE_MUTEX(host_lock);
|
|
static LIST_HEAD(host_list);
|
|
|
|
/**
|
|
* of_find_mipi_dsi_host_by_node() - find the MIPI DSI host matching a
|
|
* device tree node
|
|
* @node: device tree node
|
|
*
|
|
* Returns:
|
|
* A pointer to the MIPI DSI host corresponding to @node or NULL if no
|
|
* such device exists (or has not been registered yet).
|
|
*/
|
|
struct mipi_dsi_host *of_find_mipi_dsi_host_by_node(struct device_node *node)
|
|
{
|
|
struct mipi_dsi_host *host;
|
|
|
|
mutex_lock(&host_lock);
|
|
|
|
list_for_each_entry(host, &host_list, list) {
|
|
if (host->dev->of_node == node) {
|
|
mutex_unlock(&host_lock);
|
|
return host;
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&host_lock);
|
|
|
|
return NULL;
|
|
}
|
|
EXPORT_SYMBOL(of_find_mipi_dsi_host_by_node);
|
|
|
|
int mipi_dsi_host_register(struct mipi_dsi_host *host)
|
|
{
|
|
struct device_node *node;
|
|
|
|
for_each_available_child_of_node(host->dev->of_node, node) {
|
|
/* skip nodes without reg property */
|
|
if (!of_find_property(node, "reg", NULL))
|
|
continue;
|
|
of_mipi_dsi_device_add(host, node);
|
|
}
|
|
|
|
mutex_lock(&host_lock);
|
|
list_add_tail(&host->list, &host_list);
|
|
mutex_unlock(&host_lock);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_host_register);
|
|
|
|
static int mipi_dsi_remove_device_fn(struct device *dev, void *priv)
|
|
{
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
mipi_dsi_detach(dsi);
|
|
mipi_dsi_device_unregister(dsi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mipi_dsi_host_unregister(struct mipi_dsi_host *host)
|
|
{
|
|
device_for_each_child(host->dev, NULL, mipi_dsi_remove_device_fn);
|
|
|
|
mutex_lock(&host_lock);
|
|
list_del_init(&host->list);
|
|
mutex_unlock(&host_lock);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_host_unregister);
|
|
|
|
/**
|
|
* mipi_dsi_attach - attach a DSI device to its DSI host
|
|
* @dsi: DSI peripheral
|
|
*/
|
|
int mipi_dsi_attach(struct mipi_dsi_device *dsi)
|
|
{
|
|
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
|
|
|
|
if (!ops || !ops->attach)
|
|
return -ENOSYS;
|
|
|
|
return ops->attach(dsi->host, dsi);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_attach);
|
|
|
|
/**
|
|
* mipi_dsi_detach - detach a DSI device from its DSI host
|
|
* @dsi: DSI peripheral
|
|
*/
|
|
int mipi_dsi_detach(struct mipi_dsi_device *dsi)
|
|
{
|
|
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
|
|
|
|
if (!ops || !ops->detach)
|
|
return -ENOSYS;
|
|
|
|
return ops->detach(dsi->host, dsi);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_detach);
|
|
|
|
static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,
|
|
struct mipi_dsi_msg *msg)
|
|
{
|
|
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
|
|
|
|
if (!ops || !ops->transfer)
|
|
return -ENOSYS;
|
|
|
|
if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
|
|
msg->flags |= MIPI_DSI_MSG_USE_LPM;
|
|
msg->flags |= MIPI_DSI_MSG_LASTCOMMAND;
|
|
|
|
return ops->transfer(dsi->host, msg);
|
|
}
|
|
|
|
/**
|
|
* mipi_dsi_packet_format_is_short - check if a packet is of the short format
|
|
* @type: MIPI DSI data type of the packet
|
|
*
|
|
* Return: true if the packet for the given data type is a short packet, false
|
|
* otherwise.
|
|
*/
|
|
bool mipi_dsi_packet_format_is_short(u8 type)
|
|
{
|
|
switch (type) {
|
|
case MIPI_DSI_V_SYNC_START:
|
|
case MIPI_DSI_V_SYNC_END:
|
|
case MIPI_DSI_H_SYNC_START:
|
|
case MIPI_DSI_H_SYNC_END:
|
|
case MIPI_DSI_END_OF_TRANSMISSION:
|
|
case MIPI_DSI_COLOR_MODE_OFF:
|
|
case MIPI_DSI_COLOR_MODE_ON:
|
|
case MIPI_DSI_SHUTDOWN_PERIPHERAL:
|
|
case MIPI_DSI_TURN_ON_PERIPHERAL:
|
|
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:
|
|
case MIPI_DSI_DCS_COMPRESSION_MODE:
|
|
case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_packet_format_is_short);
|
|
|
|
/**
|
|
* mipi_dsi_packet_format_is_long - check if a packet is of the long format
|
|
* @type: MIPI DSI data type of the packet
|
|
*
|
|
* Return: true if the packet for the given data type is a long packet, false
|
|
* otherwise.
|
|
*/
|
|
bool mipi_dsi_packet_format_is_long(u8 type)
|
|
{
|
|
switch (type) {
|
|
case MIPI_DSI_PPS_LONG_WRITE:
|
|
case MIPI_DSI_NULL_PACKET:
|
|
case MIPI_DSI_BLANKING_PACKET:
|
|
case MIPI_DSI_GENERIC_LONG_WRITE:
|
|
case MIPI_DSI_DCS_LONG_WRITE:
|
|
case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_30:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_36:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_16:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_18:
|
|
case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
|
|
case MIPI_DSI_PACKED_PIXEL_STREAM_24:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_packet_format_is_long);
|
|
|
|
/**
|
|
* mipi_dsi_create_packet - create a packet from a message according to the
|
|
* DSI protocol
|
|
* @packet: pointer to a DSI packet structure
|
|
* @msg: message to translate into a packet
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
|
|
const struct mipi_dsi_msg *msg)
|
|
{
|
|
if (!packet || !msg)
|
|
return -EINVAL;
|
|
|
|
/* do some minimum sanity checking */
|
|
if (!mipi_dsi_packet_format_is_short(msg->type) &&
|
|
!mipi_dsi_packet_format_is_long(msg->type))
|
|
return -EINVAL;
|
|
|
|
if (msg->channel > 3)
|
|
return -EINVAL;
|
|
|
|
memset(packet, 0, sizeof(*packet));
|
|
packet->header[2] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
|
|
|
|
/* TODO: compute ECC if hardware support is not available */
|
|
|
|
/*
|
|
* Long write packets contain the word count in header bytes 1 and 2.
|
|
* The payload follows the header and is word count bytes long.
|
|
*
|
|
* Short write packets encode up to two parameters in header bytes 1
|
|
* and 2.
|
|
*/
|
|
if (mipi_dsi_packet_format_is_long(msg->type)) {
|
|
packet->header[0] = (msg->tx_len >> 0) & 0xff;
|
|
packet->header[1] = (msg->tx_len >> 8) & 0xff;
|
|
|
|
packet->payload_length = msg->tx_len;
|
|
packet->payload = msg->tx_buf;
|
|
} else {
|
|
const u8 *tx = msg->tx_buf;
|
|
|
|
packet->header[0] = (msg->tx_len > 0) ? tx[0] : 0;
|
|
packet->header[1] = (msg->tx_len > 1) ? tx[1] : 0;
|
|
}
|
|
|
|
packet->size = sizeof(packet->header) + packet->payload_length;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_create_packet);
|
|
|
|
/**
|
|
* mipi_dsi_shutdown_peripheral() - sends a Shutdown Peripheral command
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_shutdown_peripheral(struct mipi_dsi_device *dsi)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.type = MIPI_DSI_SHUTDOWN_PERIPHERAL,
|
|
.tx_buf = (u8 [2]) { 0, 0 },
|
|
.tx_len = 2,
|
|
};
|
|
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
|
|
|
return (ret < 0) ? ret : 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_shutdown_peripheral);
|
|
|
|
/**
|
|
* mipi_dsi_turn_on_peripheral() - sends a Turn On Peripheral command
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_turn_on_peripheral(struct mipi_dsi_device *dsi)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.type = MIPI_DSI_TURN_ON_PERIPHERAL,
|
|
.tx_buf = (u8 [2]) { 0, 0 },
|
|
.tx_len = 2,
|
|
};
|
|
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
|
|
|
return (ret < 0) ? ret : 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_turn_on_peripheral);
|
|
|
|
/*
|
|
* mipi_dsi_set_maximum_return_packet_size() - specify the maximum size of the
|
|
* the payload in a long packet transmitted from the peripheral back to the
|
|
* host processor
|
|
* @dsi: DSI peripheral device
|
|
* @value: the maximum size of the payload
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_set_maximum_return_packet_size(struct mipi_dsi_device *dsi,
|
|
u16 value)
|
|
{
|
|
u8 tx[2] = { value & 0xff, value >> 8 };
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE,
|
|
.tx_len = sizeof(tx),
|
|
.tx_buf = tx,
|
|
};
|
|
int ret = mipi_dsi_device_transfer(dsi, &msg);
|
|
|
|
return (ret < 0) ? ret : 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_set_maximum_return_packet_size);
|
|
|
|
/**
|
|
* mipi_dsi_generic_write() - transmit data using a generic write packet
|
|
* @dsi: DSI peripheral device
|
|
* @payload: buffer containing the payload
|
|
* @size: size of payload buffer
|
|
*
|
|
* This function will automatically choose the right data type depending on
|
|
* the payload length.
|
|
*
|
|
* Return: The number of bytes transmitted on success or a negative error code
|
|
* on failure.
|
|
*/
|
|
ssize_t mipi_dsi_generic_write(struct mipi_dsi_device *dsi, const void *payload,
|
|
size_t size)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.tx_buf = payload,
|
|
.tx_len = size
|
|
};
|
|
|
|
switch (size) {
|
|
case 0:
|
|
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
|
|
break;
|
|
|
|
case 1:
|
|
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
|
|
break;
|
|
|
|
case 2:
|
|
msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
|
|
break;
|
|
|
|
default:
|
|
msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
|
|
break;
|
|
}
|
|
|
|
return mipi_dsi_device_transfer(dsi, &msg);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_generic_write);
|
|
|
|
/**
|
|
* mipi_dsi_generic_read() - receive data using a generic read packet
|
|
* @dsi: DSI peripheral device
|
|
* @params: buffer containing the request parameters
|
|
* @num_params: number of request parameters
|
|
* @data: buffer in which to return the received data
|
|
* @size: size of receive buffer
|
|
*
|
|
* This function will automatically choose the right data type depending on
|
|
* the number of parameters passed in.
|
|
*
|
|
* Return: The number of bytes successfully read or a negative error code on
|
|
* failure.
|
|
*/
|
|
ssize_t mipi_dsi_generic_read(struct mipi_dsi_device *dsi, const void *params,
|
|
size_t num_params, void *data, size_t size)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.tx_len = num_params,
|
|
.tx_buf = params,
|
|
.rx_len = size,
|
|
.rx_buf = data
|
|
};
|
|
|
|
switch (num_params) {
|
|
case 0:
|
|
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM;
|
|
break;
|
|
|
|
case 1:
|
|
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM;
|
|
break;
|
|
|
|
case 2:
|
|
msg.type = MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return mipi_dsi_device_transfer(dsi, &msg);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_generic_read);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_write_buffer() - transmit a DCS command with payload
|
|
* @dsi: DSI peripheral device
|
|
* @data: buffer containing data to be transmitted
|
|
* @len: size of transmission buffer
|
|
*
|
|
* This function will automatically choose the right data type depending on
|
|
* the command payload length.
|
|
*
|
|
* Return: The number of bytes successfully transmitted or a negative error
|
|
* code on failure.
|
|
*/
|
|
ssize_t mipi_dsi_dcs_write_buffer(struct mipi_dsi_device *dsi,
|
|
const void *data, size_t len)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.tx_buf = data,
|
|
.tx_len = len
|
|
};
|
|
|
|
switch (len) {
|
|
case 0:
|
|
return -EINVAL;
|
|
|
|
case 1:
|
|
msg.type = MIPI_DSI_DCS_SHORT_WRITE;
|
|
break;
|
|
|
|
case 2:
|
|
msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
|
|
break;
|
|
|
|
default:
|
|
msg.type = MIPI_DSI_DCS_LONG_WRITE;
|
|
break;
|
|
}
|
|
|
|
return mipi_dsi_device_transfer(dsi, &msg);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_write_buffer);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_write() - send DCS write command
|
|
* @dsi: DSI peripheral device
|
|
* @cmd: DCS command
|
|
* @data: buffer containing the command payload
|
|
* @len: command payload length
|
|
*
|
|
* This function will automatically choose the right data type depending on
|
|
* the command payload length.
|
|
*
|
|
* Return: The number of bytes successfully transmitted or a negative error
|
|
* code on failure.
|
|
*/
|
|
ssize_t mipi_dsi_dcs_write(struct mipi_dsi_device *dsi, u8 cmd,
|
|
const void *data, size_t len)
|
|
{
|
|
ssize_t err;
|
|
size_t size;
|
|
u8 *tx;
|
|
|
|
if (len > 0) {
|
|
size = 1 + len;
|
|
|
|
tx = kmalloc(size, GFP_KERNEL);
|
|
if (!tx)
|
|
return -ENOMEM;
|
|
|
|
/* concatenate the DCS command byte and the payload */
|
|
tx[0] = cmd;
|
|
memcpy(&tx[1], data, len);
|
|
} else {
|
|
tx = &cmd;
|
|
size = 1;
|
|
}
|
|
|
|
err = mipi_dsi_dcs_write_buffer(dsi, tx, size);
|
|
|
|
if (len > 0)
|
|
kfree(tx);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_write);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_read() - send DCS read request command
|
|
* @dsi: DSI peripheral device
|
|
* @cmd: DCS command
|
|
* @data: buffer in which to receive data
|
|
* @len: size of receive buffer
|
|
*
|
|
* Return: The number of bytes read or a negative error code on failure.
|
|
*/
|
|
ssize_t mipi_dsi_dcs_read(struct mipi_dsi_device *dsi, u8 cmd, void *data,
|
|
size_t len)
|
|
{
|
|
struct mipi_dsi_msg msg = {
|
|
.channel = dsi->channel,
|
|
.type = MIPI_DSI_DCS_READ,
|
|
.tx_buf = &cmd,
|
|
.tx_len = 1,
|
|
.rx_buf = data,
|
|
.rx_len = len
|
|
};
|
|
|
|
return mipi_dsi_device_transfer(dsi, &msg);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_read);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_nop() - send DCS nop packet
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_nop(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_NOP, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_nop);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_soft_reset() - perform a software reset of the display module
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_soft_reset(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SOFT_RESET, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_soft_reset);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_get_power_mode() - query the display module's current power
|
|
* mode
|
|
* @dsi: DSI peripheral device
|
|
* @mode: return location for the current power mode
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_get_power_mode(struct mipi_dsi_device *dsi, u8 *mode)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_POWER_MODE, mode,
|
|
sizeof(*mode));
|
|
if (err <= 0) {
|
|
if (err == 0)
|
|
err = -ENODATA;
|
|
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_get_power_mode);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_get_pixel_format() - gets the pixel format for the RGB image
|
|
* data used by the interface
|
|
* @dsi: DSI peripheral device
|
|
* @format: return location for the pixel format
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_get_pixel_format(struct mipi_dsi_device *dsi, u8 *format)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_PIXEL_FORMAT, format,
|
|
sizeof(*format));
|
|
if (err <= 0) {
|
|
if (err == 0)
|
|
err = -ENODATA;
|
|
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_get_pixel_format);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_enter_sleep_mode() - disable all unnecessary blocks inside the
|
|
* display module except interface communication
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_enter_sleep_mode(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_ENTER_SLEEP_MODE, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_enter_sleep_mode);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_exit_sleep_mode() - enable all blocks inside the display
|
|
* module
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_exit_sleep_mode(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_EXIT_SLEEP_MODE, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_exit_sleep_mode);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_display_off() - stop displaying the image data on the
|
|
* display device
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_display_off(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_OFF, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_off);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_display_on() - start displaying the image data on the
|
|
* display device
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure
|
|
*/
|
|
int mipi_dsi_dcs_set_display_on(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_ON, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_on);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_column_address() - define the column extent of the frame
|
|
* memory accessed by the host processor
|
|
* @dsi: DSI peripheral device
|
|
* @start: first column of frame memory
|
|
* @end: last column of frame memory
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_column_address(struct mipi_dsi_device *dsi, u16 start,
|
|
u16 end)
|
|
{
|
|
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_COLUMN_ADDRESS, payload,
|
|
sizeof(payload));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_column_address);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_page_address() - define the page extent of the frame
|
|
* memory accessed by the host processor
|
|
* @dsi: DSI peripheral device
|
|
* @start: first page of frame memory
|
|
* @end: last page of frame memory
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_page_address(struct mipi_dsi_device *dsi, u16 start,
|
|
u16 end)
|
|
{
|
|
u8 payload[4] = { start >> 8, start & 0xff, end >> 8, end & 0xff };
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PAGE_ADDRESS, payload,
|
|
sizeof(payload));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_page_address);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_tear_off() - turn off the display module's Tearing Effect
|
|
* output signal on the TE signal line
|
|
* @dsi: DSI peripheral device
|
|
*
|
|
* Return: 0 on success or a negative error code on failure
|
|
*/
|
|
int mipi_dsi_dcs_set_tear_off(struct mipi_dsi_device *dsi)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_OFF, NULL, 0);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_off);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_tear_on() - turn on the display module's Tearing Effect
|
|
* output signal on the TE signal line.
|
|
* @dsi: DSI peripheral device
|
|
* @mode: the Tearing Effect Output Line mode
|
|
*
|
|
* Return: 0 on success or a negative error code on failure
|
|
*/
|
|
int mipi_dsi_dcs_set_tear_on(struct mipi_dsi_device *dsi,
|
|
enum mipi_dsi_dcs_tear_mode mode)
|
|
{
|
|
u8 value = mode;
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_ON, &value,
|
|
sizeof(value));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_on);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_pixel_format() - sets the pixel format for the RGB image
|
|
* data used by the interface
|
|
* @dsi: DSI peripheral device
|
|
* @format: pixel format
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_pixel_format(struct mipi_dsi_device *dsi, u8 format)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_PIXEL_FORMAT, &format,
|
|
sizeof(format));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_pixel_format);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_tear_scanline() - set the scanline to use as trigger for
|
|
* the Tearing Effect output signal of the display module
|
|
* @dsi: DSI peripheral device
|
|
* @scanline: scanline to use as trigger
|
|
*
|
|
* Return: 0 on success or a negative error code on failure
|
|
*/
|
|
int mipi_dsi_dcs_set_tear_scanline(struct mipi_dsi_device *dsi, u16 scanline)
|
|
{
|
|
u8 payload[2] = { scanline >> 8, scanline & 0xff };
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_TEAR_SCANLINE, payload,
|
|
sizeof(payload));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_tear_scanline);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_display_brightness() - sets the brightness value of the
|
|
* display
|
|
* @dsi: DSI peripheral device
|
|
* @brightness: brightness value
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_display_brightness(struct mipi_dsi_device *dsi,
|
|
u16 brightness)
|
|
{
|
|
u8 payload[2] = { brightness & 0xff, brightness >> 8 };
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
|
|
payload, sizeof(payload));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_get_display_brightness() - gets the current brightness value
|
|
* of the display
|
|
* @dsi: DSI peripheral device
|
|
* @brightness: brightness value
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_get_display_brightness(struct mipi_dsi_device *dsi,
|
|
u16 *brightness)
|
|
{
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
|
|
brightness, sizeof(*brightness));
|
|
if (err <= 0) {
|
|
if (err == 0)
|
|
err = -ENODATA;
|
|
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_set_display_brightness_large() - sets the 16-bit brightness value
|
|
* of the display
|
|
* @dsi: DSI peripheral device
|
|
* @brightness: brightness value
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_set_display_brightness_large(struct mipi_dsi_device *dsi,
|
|
u16 brightness)
|
|
{
|
|
u8 payload[2] = { brightness >> 8, brightness & 0xff };
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_write(dsi, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,
|
|
payload, sizeof(payload));
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_set_display_brightness_large);
|
|
|
|
/**
|
|
* mipi_dsi_dcs_get_display_brightness_large() - gets the current 16-bit
|
|
* brightness value of the display
|
|
* @dsi: DSI peripheral device
|
|
* @brightness: brightness value
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_dcs_get_display_brightness_large(struct mipi_dsi_device *dsi,
|
|
u16 *brightness)
|
|
{
|
|
u8 brightness_be[2];
|
|
ssize_t err;
|
|
|
|
err = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
|
|
brightness_be, sizeof(brightness_be));
|
|
if (err <= 0) {
|
|
if (err == 0)
|
|
err = -ENODATA;
|
|
|
|
return err;
|
|
}
|
|
|
|
*brightness = (brightness_be[0] << 8) | brightness_be[1];
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_dcs_get_display_brightness_large);
|
|
|
|
static int mipi_dsi_drv_probe(struct device *dev)
|
|
{
|
|
struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
return drv->probe(dsi);
|
|
}
|
|
|
|
static int mipi_dsi_drv_remove(struct device *dev)
|
|
{
|
|
struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
return drv->remove(dsi);
|
|
}
|
|
|
|
static void mipi_dsi_drv_shutdown(struct device *dev)
|
|
{
|
|
struct mipi_dsi_driver *drv = to_mipi_dsi_driver(dev->driver);
|
|
struct mipi_dsi_device *dsi = to_mipi_dsi_device(dev);
|
|
|
|
drv->shutdown(dsi);
|
|
}
|
|
|
|
/**
|
|
* mipi_dsi_driver_register_full() - register a driver for DSI devices
|
|
* @drv: DSI driver structure
|
|
* @owner: owner module
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
int mipi_dsi_driver_register_full(struct mipi_dsi_driver *drv,
|
|
struct module *owner)
|
|
{
|
|
drv->driver.bus = &mipi_dsi_bus_type;
|
|
drv->driver.owner = owner;
|
|
|
|
if (drv->probe)
|
|
drv->driver.probe = mipi_dsi_drv_probe;
|
|
if (drv->remove)
|
|
drv->driver.remove = mipi_dsi_drv_remove;
|
|
if (drv->shutdown)
|
|
drv->driver.shutdown = mipi_dsi_drv_shutdown;
|
|
|
|
return driver_register(&drv->driver);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_driver_register_full);
|
|
|
|
/**
|
|
* mipi_dsi_driver_unregister() - unregister a driver for DSI devices
|
|
* @drv: DSI driver structure
|
|
*
|
|
* Return: 0 on success or a negative error code on failure.
|
|
*/
|
|
void mipi_dsi_driver_unregister(struct mipi_dsi_driver *drv)
|
|
{
|
|
driver_unregister(&drv->driver);
|
|
}
|
|
EXPORT_SYMBOL(mipi_dsi_driver_unregister);
|
|
|
|
static int __init mipi_dsi_bus_init(void)
|
|
{
|
|
return bus_register(&mipi_dsi_bus_type);
|
|
}
|
|
postcore_initcall(mipi_dsi_bus_init);
|
|
|
|
MODULE_AUTHOR("Andrzej Hajda <a.hajda@samsung.com>");
|
|
MODULE_DESCRIPTION("MIPI DSI Bus");
|
|
MODULE_LICENSE("GPL and additional rights");
|