summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig4
-rw-r--r--drivers/Makefile5
-rw-r--r--drivers/ata/Kconfig11
-rw-r--r--drivers/ata/Makefile1
-rw-r--r--drivers/ata/ahci_msm.c528
-rw-r--r--drivers/ata/ahci_platform.c1
-rw-r--r--drivers/base/power/opp.c87
-rw-r--r--drivers/char/Kconfig4
-rw-r--r--drivers/char/diag/diag_dci.c115
-rw-r--r--drivers/char/diag/diag_debugfs.c19
-rw-r--r--drivers/char/diag/diagchar.h6
-rw-r--r--drivers/char/diag/diagchar_core.c21
-rw-r--r--drivers/char/diag/diagchar_hdlc.c4
-rw-r--r--drivers/char/diag/diagfwd.c5
-rw-r--r--drivers/char/diag/diagfwd_bridge.c3
-rw-r--r--drivers/char/diag/diagfwd_cntl.c4
-rw-r--r--drivers/char/diag/diagfwd_sdio.c7
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clkdev.c2
-rw-r--r--drivers/clk/qcom/Makefile5
-rw-r--r--drivers/clk/qcom/clock-debug.c643
-rw-r--r--drivers/clk/qcom/clock-dummy.c100
-rw-r--r--drivers/clk/qcom/clock-generic.c729
-rw-r--r--drivers/clk/qcom/clock.c905
-rw-r--r--drivers/clk/qcom/clock.h (renamed from drivers/clk/msm/clock.h)0
-rw-r--r--drivers/clocksource/arm_arch_timer.c1
-rw-r--r--drivers/coresight/coresight-tmc.c18
-rw-r--r--drivers/cpufreq/Kconfig11
-rw-r--r--drivers/cpufreq/Kconfig.arm3
-rw-r--r--drivers/cpufreq/Kconfig.powerpc1
-rw-r--r--drivers/cpufreq/Kconfig.x8612
-rw-r--r--drivers/cpufreq/Makefile5
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c38
-rw-r--r--drivers/cpufreq/arm_big_little.c453
-rw-r--r--drivers/cpufreq/arm_big_little.h5
-rw-r--r--drivers/cpufreq/arm_big_little_dt.c42
-rw-r--r--drivers/cpufreq/at32ap-cpufreq.c1
-rw-r--r--drivers/cpufreq/blackfin-cpufreq.c11
-rw-r--r--drivers/cpufreq/cpufreq-cpu0.c20
-rw-r--r--drivers/cpufreq/cpufreq-nforce2.c5
-rw-r--r--drivers/cpufreq/cpufreq.c1210
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c42
-rw-r--r--drivers/cpufreq/cpufreq_governor.c80
-rw-r--r--drivers/cpufreq/cpufreq_governor.h34
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c52
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c87
-rw-r--r--drivers/cpufreq/cpufreq_performance.c7
-rw-r--r--drivers/cpufreq/cpufreq_powersave.c9
-rw-r--r--drivers/cpufreq/cpufreq_stats.c39
-rw-r--r--drivers/cpufreq/cpufreq_userspace.c123
-rw-r--r--drivers/cpufreq/cris-artpec3-cpufreq.c1
-rw-r--r--drivers/cpufreq/cris-etraxfs-cpufreq.c1
-rw-r--r--drivers/cpufreq/davinci-cpufreq.c20
-rw-r--r--drivers/cpufreq/dbx500-cpufreq.c2
-rw-r--r--drivers/cpufreq/e_powersaver.c13
-rw-r--r--drivers/cpufreq/elanfreq.c1
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c2
-rw-r--r--drivers/cpufreq/exynos5440-cpufreq.c17
-rw-r--r--drivers/cpufreq/freq_table.c89
-rw-r--r--drivers/cpufreq/gx-suspmod.c3
-rw-r--r--drivers/cpufreq/ia64-acpi-cpufreq.c26
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c22
-rw-r--r--drivers/cpufreq/integrator-cpufreq.c9
-rw-r--r--drivers/cpufreq/intel_pstate.c9
-rw-r--r--drivers/cpufreq/kirkwood-cpufreq.c3
-rw-r--r--drivers/cpufreq/longhaul.c23
-rw-r--r--drivers/cpufreq/longhaul.h26
-rw-r--r--drivers/cpufreq/longrun.c11
-rw-r--r--drivers/cpufreq/loongson2_cpufreq.c3
-rw-r--r--drivers/cpufreq/maple-cpufreq.c1
-rw-r--r--drivers/cpufreq/omap-cpufreq.c12
-rw-r--r--drivers/cpufreq/p4-clockmod.c5
-rw-r--r--drivers/cpufreq/pcc-cpufreq.c4
-rw-r--r--drivers/cpufreq/powernow-k6.c9
-rw-r--r--drivers/cpufreq/powernow-k7.c38
-rw-r--r--drivers/cpufreq/powernow-k8.c32
-rw-r--r--drivers/cpufreq/ppc_cbe_cpufreq.c5
-rw-r--r--drivers/cpufreq/pxa2xx-cpufreq.c8
-rw-r--r--drivers/cpufreq/pxa3xx-cpufreq.c4
-rw-r--r--drivers/cpufreq/s3c2416-cpufreq.c24
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c3
-rw-r--r--drivers/cpufreq/sc520_freq.c3
-rw-r--r--drivers/cpufreq/sh-cpufreq.c8
-rw-r--r--drivers/cpufreq/sparc-us2e-cpufreq.c25
-rw-r--r--drivers/cpufreq/sparc-us3-cpufreq.c21
-rw-r--r--drivers/cpufreq/spear-cpufreq.c4
-rw-r--r--drivers/cpufreq/speedstep-centrino.c9
-rw-r--r--drivers/cpufreq/speedstep-ich.c1
-rw-r--r--drivers/cpufreq/speedstep-smi.c1
-rw-r--r--drivers/cpufreq/unicore2-cpufreq.c4
-rw-r--r--drivers/crypto/msm/qce50.c3
-rw-r--r--drivers/crypto/msm/qcrypto.c19
-rw-r--r--drivers/devfreq/Kconfig8
-rw-r--r--drivers/devfreq/Makefile1
-rw-r--r--drivers/devfreq/devfreq.c16
-rw-r--r--drivers/devfreq/governor_msm_cpufreq.c85
-rw-r--r--drivers/esoc/Kconfig16
-rw-r--r--drivers/esoc/Makefile3
-rw-r--r--drivers/esoc/esoc-mdm-4x.c857
-rw-r--r--drivers/esoc/esoc-mdm-drv.c250
-rw-r--r--drivers/gpio/Kconfig16
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-msm-common.c2
-rw-r--r--drivers/gpio/gpio-msm-v1.c840
-rw-r--r--drivers/gpio/gpio-msm-v2.c225
-rw-r--r--drivers/gpio/gpio-sx150x.c141
-rw-r--r--drivers/gpu/ion/msm/msm_ion.c20
-rw-r--r--drivers/gpu/msm/adreno.c31
-rw-r--r--drivers/gpu/msm/adreno.h3
-rw-r--r--drivers/gpu/msm/adreno_a3xx.c12
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c4
-rw-r--r--drivers/gpu/msm/adreno_ringbuffer.c3
-rw-r--r--drivers/gpu/msm/adreno_trace.h1
-rw-r--r--drivers/gpu/msm/kgsl.c38
-rw-r--r--drivers/gpu/msm/kgsl.h8
-rw-r--r--drivers/gpu/msm/kgsl_events.c20
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c38
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h4
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.h2
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c1
-rw-r--r--drivers/gpu/msm/kgsl_trace.c7
-rw-r--r--drivers/hwmon/epm_adc.c38
-rw-r--r--drivers/hwmon/qpnp-adc-common.c76
-rw-r--r--drivers/hwmon/qpnp-adc-current.c5
-rw-r--r--drivers/hwmon/qpnp-adc-voltage.c18
-rw-r--r--drivers/hwspinlock/msm_remote_spinlock.c2
-rw-r--r--drivers/input/misc/bmp18x-core.c92
-rw-r--r--drivers/input/misc/cm36283.c222
-rw-r--r--drivers/input/misc/kxtj9.c123
-rw-r--r--drivers/input/misc/mpu3050.c230
-rw-r--r--drivers/input/misc/stk3x1x.c145
-rwxr-xr-xdrivers/input/touchscreen/atmel_maxtouch_ts.c3286
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c16
-rw-r--r--drivers/input/touchscreen/ft5x06_ts.c5
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.c73
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx.h5
-rw-r--r--drivers/input/touchscreen/gt9xx/gt9xx_update.c26
-rw-r--r--drivers/input/touchscreen/synaptics_i2c_rmi4.c2
-rw-r--r--drivers/iommu/msm_iommu-v0.c3
-rw-r--r--drivers/iommu/msm_iommu-v1.c1
-rw-r--r--drivers/iommu/msm_iommu_dev-v0.c32
-rw-r--r--drivers/iommu/msm_iommu_dev-v1.c50
-rw-r--r--drivers/iommu/msm_iommu_domains.c16
-rw-r--r--drivers/iommu/msm_iommu_sec.c4
-rw-r--r--drivers/leds/leds-qpnp.c18
-rw-r--r--drivers/media/dvb-core/dmxdev.c38
-rw-r--r--drivers/media/platform/msm/broadcast/ensigma_uccp330.c2
-rw-r--r--drivers/media/platform/msm/broadcast/tsc.c282
-rw-r--r--drivers/media/platform/msm/broadcast/tspp.c2
-rw-r--r--drivers/media/platform/msm/broadcast/tspp2.c546
-rw-r--r--drivers/media/platform/msm/camera_v2/camera/camera.c52
-rw-r--r--drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c54
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c101
-rw-r--r--drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c18
-rw-r--r--drivers/media/platform/msm/camera_v2/msm.c49
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c92
-rw-r--r--drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h1
-rw-r--r--drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c37
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c6
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c58
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c2
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c1
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c26
-rw-r--r--drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c2
-rw-r--r--drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c35
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c171
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h3
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c725
-rw-r--r--drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h23
-rw-r--r--drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h22
-rw-r--r--drivers/media/platform/msm/vidc/hfi_packetization.c32
-rw-r--r--drivers/media/platform/msm/vidc/hfi_response_handler.c179
-rw-r--r--drivers/media/platform/msm/vidc/msm_smem.c19
-rw-r--r--drivers/media/platform/msm/vidc/msm_v4l2_vidc.c2
-rw-r--r--drivers/media/platform/msm/vidc/msm_vdec.c81
-rw-r--r--drivers/media/platform/msm/vidc/msm_venc.c94
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc.c46
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_common.c152
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.c6
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_debug.h49
-rw-r--r--drivers/media/platform/msm/vidc/msm_vidc_res_parse.c6
-rw-r--r--drivers/media/platform/msm/vidc/q6_hfi.c187
-rw-r--r--drivers/media/platform/msm/vidc/q6_hfi.h2
-rw-r--r--drivers/media/platform/msm/vidc/venus_hfi.c373
-rw-r--r--drivers/media/platform/msm/vidc/vidc_hfi_api.h1
-rw-r--r--drivers/media/platform/msm/vpu/vpu_hfi.c63
-rw-r--r--drivers/media/platform/msm/vpu/vpu_hfi_intf.h48
-rw-r--r--drivers/media/platform/msm/vpu/vpu_resources.c12
-rw-r--r--drivers/media/platform/msm/vpu/vpu_v4l2.c2
-rw-r--r--drivers/media/platform/msm/wfd/wfd-ioctl.c1
-rw-r--r--drivers/mfd/db8500-prcmu.c10
-rw-r--r--drivers/misc/Kconfig1
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/apq8084_dock.c10
-rw-r--r--drivers/misc/qcom/Kconfig8
-rw-r--r--drivers/misc/qcom/Makefile1
-rw-r--r--drivers/misc/qcom/qdsp6v2/Makefile4
-rw-r--r--drivers/misc/qcom/qdsp6v2/aac_in.c428
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrnb_in.c285
-rw-r--r--drivers/misc/qcom/qdsp6v2/amrwb_in.c282
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_aac.c309
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrnb.c165
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwb.c168
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c238
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_evrc.c174
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_mp3.c174
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_multi_aac.c328
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_qcelp.c179
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.c656
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils.h104
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.c1483
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_utils_aio.h221
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wma.c214
-rw-r--r--drivers/misc/qcom/qdsp6v2/audio_wmapro.c273
-rw-r--r--drivers/misc/qcom/qdsp6v2/evrc_in.c293
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_common.h44
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2.c98
-rw-r--r--drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c219
-rw-r--r--drivers/misc/qcom/qdsp6v2/qcelp_in.c291
-rw-r--r--drivers/misc/qfp_fuse.c6
-rw-r--r--drivers/misc/qpnp-misc.c4
-rw-r--r--drivers/misc/qseecom.c360
-rw-r--r--drivers/mmc/card/block.c2
-rw-r--r--drivers/mmc/core/core.c5
-rw-r--r--drivers/mmc/core/mmc.c3
-rw-r--r--drivers/mmc/host/msm_sdcc.c2
-rw-r--r--drivers/mmc/host/sdhci-msm.c2
-rw-r--r--drivers/mmc/host/sdhci.c19
-rw-r--r--drivers/mtd/devices/msm_qpic_nand.c2
-rw-r--r--drivers/net/ethernet/msm/ecm_ipa.c2
-rw-r--r--drivers/net/ethernet/msm/emac/emac_ethtool.c2
-rw-r--r--drivers/net/ethernet/msm/emac/emac_hw.c67
-rw-r--r--drivers/net/ethernet/msm/emac/emac_hw.h6
-rw-r--r--drivers/net/ethernet/msm/emac/emac_main.c56
-rw-r--r--drivers/net/ethernet/msm/rndis_ipa.c56
-rw-r--r--drivers/net/wireless/cnss/Makefile5
-rw-r--r--drivers/net/wireless/cnss/cnss.c60
-rw-r--r--drivers/net/wireless/wcnss/qcomwlan_secif.c14
-rw-r--r--drivers/net/wireless/wcnss/wcnss_vreg.c2
-rw-r--r--drivers/net/wireless/wcnss/wcnss_wlan.c315
-rw-r--r--drivers/of/of_batterydata.c16
-rw-r--r--drivers/phy/Kconfig24
-rw-r--r--drivers/phy/Makefile6
-rw-r--r--drivers/phy/phy-core.c698
-rw-r--r--drivers/phy/phy-msm-sata.c663
-rw-r--r--drivers/platform/msm/Kconfig2
-rw-r--r--drivers/platform/msm/avtimer.c2
-rw-r--r--drivers/platform/msm/ipa/ipa.c32
-rw-r--r--drivers/platform/msm/ipa/ipa_bridge.c2
-rw-r--r--drivers/platform/msm/ipa/ipa_dp.c42
-rw-r--r--drivers/platform/msm/ipa/ipa_nat.c27
-rw-r--r--drivers/platform/msm/ipa/rmnet_ipa.c9
-rw-r--r--drivers/platform/msm/qca1530.c203
-rw-r--r--drivers/platform/msm/qpnp-power-on.c31
-rw-r--r--drivers/platform/msm/usb_bam.c582
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/power/qpnp-bms.c23
-rw-r--r--drivers/power/smb135x-charger.c196
-rw-r--r--drivers/regulator/Kconfig11
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/qpnp-regulator.c2
-rw-r--r--drivers/regulator/rpm-smd-regulator.c1722
-rw-r--r--drivers/rtc/qpnp-rtc.c10
-rw-r--r--drivers/scsi/ufs/debugfs.c4
-rw-r--r--drivers/scsi/ufs/ufs-msm.c250
-rw-r--r--drivers/scsi/ufs/ufs-msm.h2
-rw-r--r--drivers/scsi/ufs/ufs.h24
-rw-r--r--drivers/scsi/ufs/ufs_test.c3
-rw-r--r--drivers/scsi/ufs/ufshcd-pci.c2
-rw-r--r--drivers/scsi/ufs/ufshcd-pltfrm.c6
-rw-r--r--drivers/scsi/ufs/ufshcd.c686
-rw-r--r--drivers/scsi/ufs/ufshcd.h62
-rw-r--r--drivers/scsi/ufs/unipro.h18
-rw-r--r--drivers/sensors/sensors_class.c74
-rw-r--r--drivers/sh/clk/core.c4
-rw-r--r--drivers/slimbus/slim-msm-ngd.c2
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/qcom/Kconfig43
-rw-r--r--drivers/soc/qcom/Makefile6
-rw-r--r--drivers/soc/qcom/memshare/Kconfig9
-rw-r--r--drivers/soc/qcom/memshare/Makefile1
-rw-r--r--drivers/soc/qcom/memshare/heap_mem_ext_v01.c138
-rw-r--r--drivers/soc/qcom/memshare/heap_mem_ext_v01.h143
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.c312
-rw-r--r--drivers/soc/qcom/memshare/msm_memshare.h32
-rw-r--r--drivers/soc/qcom/qdsp6v2/Makefile4
-rw-r--r--drivers/soc/qcom/qdsp6v2/adsp-loader.c245
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr.c834
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_tal.c283
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_v2.c60
-rw-r--r--drivers/soc/qcom/qdsp6v2/apr_v3.c44
-rw-r--r--drivers/soc/qcom/qdsp6v2/dsp_debug.c261
-rw-r--r--drivers/soc/qcom/qdsp6v2/msm_audio_ion.c577
-rw-r--r--drivers/soc/qcom/smem.c1375
-rw-r--r--drivers/soc/qcom/smem_debug.c139
-rw-r--r--drivers/soc/qcom/smem_private.h104
-rw-r--r--drivers/spi/spi_qsd.c615
-rw-r--r--drivers/spi/spi_qsd.h6
-rw-r--r--drivers/spmi/qpnp-int.c3
-rw-r--r--drivers/spmi/spmi-dbgfs.h12
-rw-r--r--drivers/spmi/spmi-pmic-arb.c22
-rw-r--r--drivers/staging/android/alarm-dev.c19
-rw-r--r--drivers/staging/android/android_alarm.h3
-rw-r--r--drivers/thermal/Kconfig1
-rw-r--r--drivers/thermal/msm8974-tsens.c22
-rw-r--r--drivers/thermal/msm_thermal.c11
-rw-r--r--drivers/tty/serial/msm_serial_hs.c4
-rw-r--r--drivers/usb/class/Kconfig12
-rw-r--r--drivers/usb/class/Makefile1
-rw-r--r--drivers/usb/class/ccid_bridge.c885
-rw-r--r--drivers/usb/dwc3/core.h1
-rw-r--r--drivers/usb/dwc3/dwc3-msm.c78
-rw-r--r--drivers/usb/dwc3/gadget.c57
-rw-r--r--drivers/usb/gadget/android.c21
-rw-r--r--drivers/usb/gadget/f_diag.c22
-rw-r--r--drivers/usb/gadget/f_mbim.c9
-rw-r--r--drivers/usb/gadget/f_mtp.c5
-rw-r--r--drivers/usb/gadget/f_qc_ecm.c10
-rw-r--r--drivers/usb/gadget/f_qc_rndis.c10
-rw-r--r--drivers/usb/gadget/f_rmnet.c25
-rw-r--r--drivers/usb/gadget/u_bam.c11
-rw-r--r--drivers/usb/gadget/u_bam_data.c6
-rw-r--r--drivers/usb/gadget/u_ether.c5
-rw-r--r--drivers/usb/gadget/u_qdss.c2
-rw-r--r--drivers/usb/gadget/u_rmnet.h4
-rw-r--r--drivers/usb/gadget/u_rmnet_ctrl_qti.c54
-rw-r--r--drivers/usb/gadget/u_serial.c24
-rw-r--r--drivers/usb/host/ehci-hub.c11
-rw-r--r--drivers/usb/host/ehci-msm-hsic.c57
-rw-r--r--drivers/usb/host/ehci-msm.c2
-rw-r--r--drivers/usb/host/ehci-msm2.c20
-rw-r--r--drivers/usb/host/ehci-q.c2
-rw-r--r--drivers/usb/host/xhci-dbg.c39
-rw-r--r--drivers/usb/host/xhci-msm-hsic.c14
-rw-r--r--drivers/usb/phy/phy-msm-hsusb.c16
-rw-r--r--drivers/usb/phy/phy-msm-ssusb-qmp.c8
-rw-r--r--drivers/usb/phy/phy-msm-ssusb.c76
-rw-r--r--drivers/usb/phy/phy-msm-usb.c62
-rw-r--r--drivers/video/msm/mdss/dsi_host_v2.c34
-rw-r--r--drivers/video/msm/mdss/mdp3.c28
-rw-r--r--drivers/video/msm/mdss/mdp3.h1
-rw-r--r--drivers/video/msm/mdss/mdp3_ctrl.c109
-rw-r--r--drivers/video/msm/mdss/mdp3_ctrl.h2
-rw-r--r--drivers/video/msm/mdss/mdp3_dma.c65
-rw-r--r--drivers/video/msm/mdss/mdp3_dma.h12
-rw-r--r--drivers/video/msm/mdss/mdss_dsi.c29
-rw-r--r--drivers/video/msm/mdss/mdss_dsi_cmd.h1
-rw-r--r--drivers/video/msm/mdss/mdss_dsi_panel.c13
-rw-r--r--drivers/video/msm/mdss/mdss_edp.c12
-rw-r--r--drivers/video/msm/mdss/mdss_fb.c74
-rw-r--r--drivers/video/msm/mdss/mdss_fb.h13
-rw-r--r--drivers/video/msm/mdss/mdss_mdp.c20
-rw-r--r--drivers/video/msm/mdss/mdss_mdp.h8
-rw-r--r--drivers/video/msm/mdss/mdss_mdp_ctl.c12
-rw-r--r--drivers/video/msm/mdss/mdss_mdp_intf_video.c6
-rw-r--r--drivers/video/msm/mdss/mdss_mdp_overlay.c266
-rw-r--r--drivers/video/msm/mdss/mdss_mdp_pipe.c20
-rw-r--r--drivers/video/msm/mdss/mdss_mdp_pp.c456
-rw-r--r--drivers/video/msm/mdss/mdss_panel.h1
-rw-r--r--drivers/video/msm/mdss/splash.h5279
364 files changed, 37373 insertions, 5954 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 1c26f33f99c2..5504162c55da 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -182,4 +182,8 @@ source "drivers/bif/Kconfig"
source "drivers/sensors/Kconfig"
+source "drivers/phy/Kconfig"
+
+source "drivers/soc/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index d01aa057b563..a0e9647fba37 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -8,6 +8,8 @@
obj-y += irqchip/
obj-y += bus/
+obj-$(CONFIG_GENERIC_PHY) += phy/
+
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
obj-y += gpio/
@@ -17,6 +19,7 @@ obj-$(CONFIG_PARISC) += parisc/
obj-$(CONFIG_RAPIDIO) += rapidio/
obj-y += video/
obj-y += idle/
+obj-y += soc/
# IPMI must come before ACPI in order to provide IPMI opregion support
obj-$(CONFIG_IPMI_HANDLER) += char/ipmi/
@@ -55,7 +58,7 @@ obj-$(CONFIG_FB_I810) += video/i810/
obj-$(CONFIG_FB_INTEL) += video/intelfb/
obj-$(CONFIG_PARPORT) += parport/
-obj-y += base/ block/ misc/ mfd/ nfc/
+obj-y += base/ block/ misc/ mfd/ nfc/ soc/
obj-$(CONFIG_NUBUS) += nubus/
obj-y += macintosh/
obj-$(CONFIG_IDE) += ide/
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index a5a3ebcbdd2c..da5290224954 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -97,6 +97,17 @@ config SATA_AHCI_PLATFORM
If unsure, say N.
+config SATA_AHCI_MSM
+ tristate "MSM AHCI SATA support"
+ depends on OF && ARCH_MSM
+ select SATA_AHCI_PLATFORM
+ help
+ This option enables support for AHCI SATA controller
+ integrated into MSM chipsets. For more information
+ please refer to http://www.qualcomm.com/chipsets.
+
+ If unsure, say N.
+
config SATA_FSL
tristate "Freescale 3.0Gbps SATA support"
depends on FSL_SOC
diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile
index c04d0fd038a3..34f1b73ed3e3 100644
--- a/drivers/ata/Makefile
+++ b/drivers/ata/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_SATA_INIC162X) += sata_inic162x.o
obj-$(CONFIG_SATA_SIL24) += sata_sil24.o
obj-$(CONFIG_SATA_DWC) += sata_dwc_460ex.o
obj-$(CONFIG_SATA_HIGHBANK) += sata_highbank.o libahci.o
+obj-$(CONFIG_SATA_AHCI_MSM) += ahci_msm.o
# SFF w/ custom DMA
obj-$(CONFIG_PDC_ADMA) += pdc_adma.o
diff --git a/drivers/ata/ahci_msm.c b/drivers/ata/ahci_msm.c
new file mode 100644
index 000000000000..db990353227a
--- /dev/null
+++ b/drivers/ata/ahci_msm.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/ahci_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct msm_ahci_clk_info {
+ struct list_head list;
+ struct clk *clk;
+ const char *name;
+ u32 max_freq;
+ bool enabled;
+};
+
+struct msm_ahci_host {
+ struct platform_device *ahci_pdev;
+ struct list_head clk_list_head;
+ void __iomem *ahci_base;
+ struct phy *phy;
+ bool phy_powered_on;
+};
+
+static int msm_ahci_enable_clk(struct device *dev,
+ struct msm_ahci_clk_info *clki)
+{
+ int ret = 0;
+
+ if (clki->enabled)
+ goto out;
+
+ ret = clk_prepare_enable(clki->clk);
+ if (ret) {
+ dev_err(dev, "%s: %s prepare enable failed, %d\n",
+ __func__, clki->name, ret);
+ } else {
+ clki->enabled = true;
+ dev_dbg(dev, "%s: clk: %s enabled\n",
+ __func__, clki->name);
+ }
+out:
+ return ret;
+}
+
+static void msm_ahci_disable_clk(struct device *dev,
+ struct msm_ahci_clk_info *clki)
+{
+ if (!clki->enabled)
+ return;
+
+ clk_disable_unprepare(clki->clk);
+ clki->enabled = false;
+ dev_dbg(dev, "%s: clk: %s disabled\n",
+ __func__, clki->name);
+}
+
+static int msm_ahci_setup_asic_rbc_clks(struct msm_ahci_host *host, bool on)
+{
+ int ret = 0;
+ struct device *dev = host->ahci_pdev->dev.parent;
+ struct msm_ahci_clk_info *clki;
+ struct list_head *head = &host->clk_list_head;
+
+ if (!head || list_empty(head))
+ goto out;
+
+ /*
+ * asic0_clk and rbc0_clk should be enabled/disabled
+ * only when PHY is powered on.
+ */
+ if (!host->phy_powered_on)
+ goto out;
+
+gate_ungate:
+ list_for_each_entry(clki, head, list) {
+ if (IS_ERR_OR_NULL(clki->clk))
+ continue;
+
+ if (!strcmp(clki->name, "asic0_clk") ||
+ !strcmp(clki->name, "rbc0_clk")) {
+ if (on)
+ ret = msm_ahci_enable_clk(dev, clki);
+ else
+ msm_ahci_disable_clk(dev, clki);
+ }
+ }
+out:
+ if (ret) {
+ on = false;
+ goto gate_ungate;
+ }
+
+ return ret;
+}
+
+static int msm_ahci_setup_clocks(struct msm_ahci_host *host, bool on)
+{
+ int ret = 0;
+ struct device *dev = host->ahci_pdev->dev.parent;
+ struct msm_ahci_clk_info *clki;
+ struct list_head *head = &host->clk_list_head;
+
+ if (!head || list_empty(head))
+ goto out;
+
+gate_ungate:
+ list_for_each_entry(clki, head, list) {
+ if (IS_ERR_OR_NULL(clki->clk))
+ continue;
+
+ /*
+ * asic0_clk and rbc0_clk are handled separately
+ * see msm_ahci_setup_asic_rbc_clks().
+ */
+ if (!strcmp(clki->name, "asic0_clk") ||
+ !strcmp(clki->name, "rbc0_clk"))
+ continue;
+ if (on)
+ ret = msm_ahci_enable_clk(dev, clki);
+ else
+ msm_ahci_disable_clk(dev, clki);
+ }
+out:
+ if (ret) {
+ on = false;
+ goto gate_ungate;
+ }
+
+ return ret;
+}
+
+static int msm_ahci_get_clocks(struct msm_ahci_host *host)
+{
+ int ret = 0;
+ struct msm_ahci_clk_info *clki;
+ struct device *dev = host->ahci_pdev->dev.parent;
+ struct list_head *head = &host->clk_list_head;
+
+ if (!head || list_empty(head))
+ goto out;
+
+ list_for_each_entry(clki, head, list) {
+ if (!clki->name)
+ continue;
+
+ clki->clk = devm_clk_get(dev, clki->name);
+ if (IS_ERR(clki->clk)) {
+ ret = PTR_ERR(clki->clk);
+ dev_err(dev, "%s: %s clk get failed, %d\n",
+ __func__, clki->name, ret);
+ goto out;
+ }
+
+ if (clki->max_freq) {
+ ret = clk_set_rate(clki->clk, clki->max_freq);
+ if (ret) {
+ dev_err(dev, "%s: %s clk set rate(%dHz) failed, %d\n",
+ __func__, clki->name,
+ clki->max_freq, ret);
+ goto out;
+ }
+ }
+ dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__,
+ clki->name, clk_get_rate(clki->clk));
+ }
+out:
+ return ret;
+}
+
+static int msm_ahci_parse_clock_info(struct msm_ahci_host *host)
+{
+ int ret = 0;
+ int cnt;
+ int i;
+ struct device *dev = host->ahci_pdev->dev.parent;
+ struct device_node *np = dev->of_node;
+ char *name;
+ u32 *clkfreq = NULL;
+ struct msm_ahci_clk_info *clki;
+
+ if (!np)
+ goto out;
+
+ INIT_LIST_HEAD(&host->clk_list_head);
+
+ cnt = of_property_count_strings(np, "clock-names");
+ if (!cnt || (cnt == -EINVAL)) {
+ dev_info(dev, "%s: Unable to find clocks, assuming enabled\n",
+ __func__);
+ } else if (cnt < 0) {
+ dev_err(dev, "%s: count clock strings failed, err %d\n",
+ __func__, cnt);
+ ret = cnt;
+ }
+
+ if (cnt <= 0)
+ goto out;
+
+ clkfreq = kzalloc(cnt * sizeof(*clkfreq), GFP_KERNEL);
+ if (!clkfreq) {
+ ret = -ENOMEM;
+ dev_err(dev, "%s: memory alloc failed\n", __func__);
+ goto out;
+ }
+
+ ret = of_property_read_u32_array(np,
+ "max-clock-frequency-hz", clkfreq, cnt);
+ if (ret && (ret != -EINVAL)) {
+ dev_err(dev, "%s: invalid max-clock-frequency-hz property, %d\n",
+ __func__, ret);
+ goto out;
+ }
+
+ for (i = 0; i < cnt; i++) {
+ ret = of_property_read_string_index(np,
+ "clock-names", i, (const char **)&name);
+ if (ret)
+ goto out;
+
+ clki = devm_kzalloc(dev, sizeof(*clki), GFP_KERNEL);
+ if (!clki) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ clki->max_freq = clkfreq[i];
+ clki->name = kstrdup(name, GFP_KERNEL);
+ list_add_tail(&clki->list, &host->clk_list_head);
+ }
+out:
+ kfree(clkfreq);
+ return ret;
+}
+
+static int msm_ahci_init_clocks(struct msm_ahci_host *host)
+{
+ int ret = 0;
+
+ ret = msm_ahci_parse_clock_info(host);
+ if (ret)
+ goto out;
+
+ ret = msm_ahci_get_clocks(host);
+ if (ret)
+ goto out;
+
+ ret = msm_ahci_setup_clocks(host, true);
+ if (ret)
+ goto out;
+out:
+ return ret;
+}
+
+static void msm_ahci_exit_clocks(struct msm_ahci_host *host)
+{
+ msm_ahci_setup_clocks(host, false);
+}
+
+static int msm_ahci_init_phy(struct msm_ahci_host *host)
+{
+ int ret = 0;
+ struct device *dev = host->ahci_pdev->dev.parent;
+
+ host->phy = devm_phy_get(dev, "sata-6g");
+ if (IS_ERR(host->phy)) {
+ ret = PTR_ERR(host->phy);
+ dev_err(dev, "PHY get failed %d\n", ret);
+ goto out;
+ }
+
+ ret = phy_init(host->phy);
+ if (ret) {
+ dev_err(dev, "PHY initialization failed %d\n", ret);
+ goto out;
+ }
+
+ ret = phy_power_on(host->phy);
+ if (ret) {
+ dev_err(dev, "PHY power on failed %d\n", ret);
+ goto out;
+ }
+
+ host->phy_powered_on = true;
+
+ /* asic0 and rbc0 clks needs to be ungated only after phy power on */
+ ret = msm_ahci_setup_asic_rbc_clks(host, true);
+ if (ret) {
+ dev_err(dev, "failed to enable asic0/rbc0 clks %d", ret);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static void msm_ahci_exit_phy(struct msm_ahci_host *host)
+{
+ msm_ahci_setup_asic_rbc_clks(host, false);
+ phy_power_off(host->phy);
+ host->phy_powered_on = false;
+}
+
+static int msm_ahci_init(struct device *ahci_dev, void __iomem *addr)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_ahci_host *host = dev_get_drvdata(dev);
+
+ /* Save ahci mmio to access vendor specific registers */
+ host->ahci_base = addr;
+
+ ret = msm_ahci_init_clocks(host);
+ if (ret) {
+ dev_err(dev, "AHCI clk init failed with err=%d\n", ret);
+ goto out;
+ }
+
+ ret = msm_ahci_init_phy(host);
+ if (ret) {
+ dev_err(dev, "SATA PHY init failed with err=%d\n", ret);
+ goto out_exit_clks;
+ }
+
+ return 0;
+
+out_exit_clks:
+ msm_ahci_exit_clocks(host);
+out:
+ return ret;
+}
+
+static void msm_ahci_exit(struct device *ahci_dev)
+{
+ struct device *dev = ahci_dev->parent;
+ struct msm_ahci_host *host = dev_get_drvdata(dev);
+
+
+ msm_ahci_exit_phy(host);
+ msm_ahci_exit_clocks(host);
+}
+
+static int msm_ahci_suspend(struct device *ahci_dev)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_ahci_host *host = dev_get_drvdata(dev);
+
+ msm_ahci_setup_asic_rbc_clks(host, false);
+ ret = phy_power_off(host->phy);
+ if (ret) {
+ dev_err(dev, "%s: PHY power off failed %d\n", __func__, ret);
+ goto out;
+ } else {
+ host->phy_powered_on = false;
+ }
+
+ msm_ahci_setup_clocks(host, false);
+out:
+ return ret;
+}
+
+static int msm_ahci_resume(struct device *ahci_dev)
+{
+ int ret;
+ struct device *dev = ahci_dev->parent;
+ struct msm_ahci_host *host = dev_get_drvdata(dev);
+
+ ret = msm_ahci_setup_clocks(host, true);
+ if (ret)
+ goto out;
+
+ ret = phy_power_on(host->phy);
+ if (ret) {
+ dev_err(dev, "%s: PHY power on failed %d\n", __func__, ret);
+ goto out;
+ } else {
+ host->phy_powered_on = true;
+ }
+
+ /* asic0 and rbc0 clks needs to be ungated only after phy power on */
+ ret = msm_ahci_setup_asic_rbc_clks(host, true);
+out:
+ return ret;
+}
+
+static struct ahci_platform_data msm_ahci_pdata = {
+ .init = msm_ahci_init,
+ .exit = msm_ahci_exit,
+ .suspend = msm_ahci_suspend,
+ .resume = msm_ahci_resume,
+};
+
+static const struct of_device_id msm_ahci_of_match[] = {
+ { .compatible = "qcom,msm-ahci", .data = &msm_ahci_pdata },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, msm_ahci_of_match);
+
+static int msm_ahci_probe(struct platform_device *pdev)
+{
+ int err;
+ struct msm_ahci_host *host;
+ struct device *dev = &pdev->dev;
+ const struct of_device_id *of_id;
+ const struct ahci_platform_data *pdata = NULL;
+ struct platform_device *ahci_pdev;
+ struct device *ahci_dev;
+ int ret = 0;
+
+ host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
+ if (!host) {
+ dev_err(dev, "failed to allocate memory for msm host\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ahci_pdev = platform_device_alloc("ahci", -1);
+ if (!ahci_pdev) {
+ dev_err(dev, "failed to allocate ahci platform device\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ahci_dev = &ahci_pdev->dev;
+ ahci_dev->parent = dev;
+
+ if (!dev->dma_mask)
+ dev->dma_mask = &dev->coherent_dma_mask;
+
+ if (!dma_set_mask(dev, DMA_BIT_MASK(64))) {
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(64));
+ } else if (!dma_set_mask(dev, DMA_BIT_MASK(32))) {
+ dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
+ } else {
+ err = -EIO;
+ dev_err(dev, "unable to set dma mask\n");
+ goto err_put_device;
+ }
+
+ ahci_dev->dma_mask = dev->dma_mask;
+ ahci_dev->dma_parms = dev->dma_parms;
+ dma_set_coherent_mask(ahci_dev, dev->coherent_dma_mask);
+
+ host->ahci_pdev = ahci_pdev;
+
+ platform_set_drvdata(pdev, host);
+
+ of_id = of_match_device(msm_ahci_of_match, dev);
+ if (of_id) {
+ pdata = of_id->data;
+ } else {
+ ret = -EINVAL;
+ dev_err(dev, "pdata is required to initialze ahci %d\n", ret);
+ goto err;
+ }
+
+ ahci_dev->of_node = dev->of_node;
+
+ ret = platform_device_add_resources(ahci_pdev,
+ pdev->resource, pdev->num_resources);
+ if (ret) {
+ dev_err(dev, "failed to add resources %d\n", ret);
+ goto err_put_device;
+ }
+
+ ret = platform_device_add_data(ahci_pdev, pdata, sizeof(*pdata));
+ if (ret) {
+ dev_err(dev, "failed to add pdata %d\n", ret);
+ goto err_put_device;
+ }
+
+ ret = platform_device_add(ahci_pdev);
+ if (ret) {
+ dev_err(dev, "failed to register ahci device %d\n", ret);
+ goto err_put_device;
+ }
+
+ return 0;
+
+err_put_device:
+ platform_device_put(ahci_pdev);
+err:
+ return ret;
+}
+
+static int msm_ahci_remove(struct platform_device *pdev)
+{
+ struct msm_ahci_host *host = platform_get_drvdata(pdev);
+
+ platform_device_unregister(host->ahci_pdev);
+
+ return 0;
+}
+
+static struct platform_driver msm_ahci_driver = {
+ .probe = msm_ahci_probe,
+ .remove = msm_ahci_remove,
+ .driver = {
+ .name = "ahci-msm",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_ahci_of_match,
+ },
+};
+module_platform_driver(msm_ahci_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("AHCI platform MSM Glue Layer");
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 4221d1132aff..f451f43c4f21 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -356,7 +356,6 @@ static const struct dev_pm_ops ahci_pm_ops = {
static const struct of_device_id ahci_of_match[] = {
{ .compatible = "snps,spear-ahci", },
- { .compatible = "qcom,msm-ahci", },
{},
};
MODULE_DEVICE_TABLE(of, ahci_of_match);
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index f0077cb8e249..9ee8975e2a7f 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -21,7 +21,7 @@
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/of.h>
#include <linux/export.h>
@@ -136,7 +136,7 @@ static struct device_opp *find_device_opp(struct device *dev)
}
/**
- * opp_get_voltage() - Gets the voltage corresponding to an available opp
+ * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an available opp
* @opp: opp for which voltage has to be returned for
*
* Return voltage in micro volt corresponding to the opp, else
@@ -150,7 +150,7 @@ static struct device_opp *find_device_opp(struct device *dev)
* prior to unlocking with rcu_read_unlock() to maintain the integrity of the
* pointer.
*/
-unsigned long opp_get_voltage(struct opp *opp)
+unsigned long dev_pm_opp_get_voltage(struct opp *opp)
{
struct opp *tmp_opp;
unsigned long v = 0;
@@ -163,10 +163,10 @@ unsigned long opp_get_voltage(struct opp *opp)
return v;
}
-EXPORT_SYMBOL_GPL(opp_get_voltage);
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_voltage);
/**
- * opp_get_freq() - Gets the frequency corresponding to an available opp
+ * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp
* @opp: opp for which frequency has to be returned for
*
* Return frequency in hertz corresponding to the opp, else
@@ -180,7 +180,7 @@ EXPORT_SYMBOL_GPL(opp_get_voltage);
* prior to unlocking with rcu_read_unlock() to maintain the integrity of the
* pointer.
*/
-unsigned long opp_get_freq(struct opp *opp)
+unsigned long dev_pm_opp_get_freq(struct opp *opp)
{
struct opp *tmp_opp;
unsigned long f = 0;
@@ -193,10 +193,10 @@ unsigned long opp_get_freq(struct opp *opp)
return f;
}
-EXPORT_SYMBOL_GPL(opp_get_freq);
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq);
/**
- * opp_get_opp_count() - Get number of opps available in the opp list
+ * dev_pm_opp_get_opp_count() - Get number of opps available in the opp list
* @dev: device for which we do this operation
*
* This function returns the number of available opps if there are any,
@@ -206,7 +206,7 @@ EXPORT_SYMBOL_GPL(opp_get_freq);
* internally references two RCU protected structures: device_opp and opp which
* are safe as long as we are under a common RCU locked section.
*/
-int opp_get_opp_count(struct device *dev)
+int dev_pm_opp_get_opp_count(struct device *dev)
{
struct device_opp *dev_opp;
struct opp *temp_opp;
@@ -226,10 +226,10 @@ int opp_get_opp_count(struct device *dev)
return count;
}
-EXPORT_SYMBOL_GPL(opp_get_opp_count);
+EXPORT_SYMBOL_GPL(dev_pm_opp_get_opp_count);
/**
- * opp_find_freq_exact() - search for an exact frequency
+ * dev_pm_opp_find_freq_exact() - search for an exact frequency
* @dev: device for which we do this operation
* @freq: frequency to search for
* @available: true/false - match for available opp
@@ -254,7 +254,7 @@ EXPORT_SYMBOL_GPL(opp_get_opp_count);
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
*/
-struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
+struct opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq,
bool available)
{
struct device_opp *dev_opp;
@@ -277,10 +277,10 @@ struct opp *opp_find_freq_exact(struct device *dev, unsigned long freq,
return opp;
}
-EXPORT_SYMBOL_GPL(opp_find_freq_exact);
+EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
/**
- * opp_find_freq_ceil() - Search for an rounded ceil freq
+ * dev_pm_opp_find_freq_ceil() - Search for an rounded ceil freq
* @dev: device for which we do this operation
* @freq: Start frequency
*
@@ -300,7 +300,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_exact);
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
*/
-struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
+struct opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq)
{
struct device_opp *dev_opp;
struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
@@ -324,10 +324,10 @@ struct opp *opp_find_freq_ceil(struct device *dev, unsigned long *freq)
return opp;
}
-EXPORT_SYMBOL_GPL(opp_find_freq_ceil);
+EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil);
/**
- * opp_find_freq_floor() - Search for a rounded floor freq
+ * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq
* @dev: device for which we do this operation
* @freq: Start frequency
*
@@ -347,7 +347,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_ceil);
* under the locked area. The pointer returned must be used prior to unlocking
* with rcu_read_unlock() to maintain the integrity of the pointer.
*/
-struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
+struct opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq)
{
struct device_opp *dev_opp;
struct opp *temp_opp, *opp = ERR_PTR(-ERANGE);
@@ -375,17 +375,17 @@ struct opp *opp_find_freq_floor(struct device *dev, unsigned long *freq)
return opp;
}
-EXPORT_SYMBOL_GPL(opp_find_freq_floor);
+EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor);
/**
- * opp_add() - Add an OPP table from a table definitions
+ * dev_pm_opp_add() - Add an OPP table from a table definitions
* @dev: device for which we do this operation
* @freq: Frequency in Hz for this OPP
* @u_volt: Voltage in uVolts for this OPP
*
* This function adds an opp definition to the opp list and returns status.
* The opp is made available by default and it can be controlled using
- * opp_enable/disable functions.
+ * dev_pm_opp_enable/disable functions.
*
* Locking: The internal device_opp and opp structures are RCU protected.
* Hence this function internally uses RCU updater strategy with mutex locks
@@ -393,7 +393,7 @@ EXPORT_SYMBOL_GPL(opp_find_freq_floor);
* that this function is *NOT* called under RCU protection or in contexts where
* mutex cannot be locked.
*/
-int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
+int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
{
struct device_opp *dev_opp = NULL;
struct opp *opp, *new_opp;
@@ -460,6 +460,7 @@ int opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
srcu_notifier_call_chain(&dev_opp->head, OPP_EVENT_ADD, new_opp);
return 0;
}
+EXPORT_SYMBOL_GPL(dev_pm_opp_add);
/**
* opp_set_availability() - helper to set the availability of an opp
@@ -551,13 +552,13 @@ unlock:
}
/**
- * opp_enable() - Enable a specific OPP
+ * dev_pm_opp_enable() - Enable a specific OPP
* @dev: device for which we do this operation
* @freq: OPP frequency to enable
*
* Enables a provided opp. If the operation is valid, this returns 0, else the
* corresponding error value. It is meant to be used for users an OPP available
- * after being temporarily made unavailable with opp_disable.
+ * after being temporarily made unavailable with dev_pm_opp_disable.
*
* Locking: The internal device_opp and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the
@@ -565,21 +566,21 @@ unlock:
* this function is *NOT* called under RCU protection or in contexts where
* mutex locking or synchronize_rcu() blocking calls cannot be used.
*/
-int opp_enable(struct device *dev, unsigned long freq)
+int dev_pm_opp_enable(struct device *dev, unsigned long freq)
{
return opp_set_availability(dev, freq, true);
}
-EXPORT_SYMBOL_GPL(opp_enable);
+EXPORT_SYMBOL_GPL(dev_pm_opp_enable);
/**
- * opp_disable() - Disable a specific OPP
+ * dev_pm_opp_disable() - Disable a specific OPP
* @dev: device for which we do this operation
* @freq: OPP frequency to disable
*
* Disables a provided opp. If the operation is valid, this returns
* 0, else the corresponding error value. It is meant to be a temporary
* control by users to make this OPP not available until the circumstances are
- * right to make it available again (with a call to opp_enable).
+ * right to make it available again (with a call to dev_pm_opp_enable).
*
* Locking: The internal device_opp and opp structures are RCU protected.
* Hence this function indirectly uses RCU and mutex locks to keep the
@@ -587,15 +588,15 @@ EXPORT_SYMBOL_GPL(opp_enable);
* this function is *NOT* called under RCU protection or in contexts where
* mutex locking or synchronize_rcu() blocking calls cannot be used.
*/
-int opp_disable(struct device *dev, unsigned long freq)
+int dev_pm_opp_disable(struct device *dev, unsigned long freq)
{
return opp_set_availability(dev, freq, false);
}
-EXPORT_SYMBOL_GPL(opp_disable);
+EXPORT_SYMBOL_GPL(dev_pm_opp_disable);
#ifdef CONFIG_CPU_FREQ
/**
- * opp_init_cpufreq_table() - create a cpufreq table for a device
+ * dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device
* @dev: device for which we do this operation
* @table: Cpufreq table returned back to caller
*
@@ -618,7 +619,7 @@ EXPORT_SYMBOL_GPL(opp_disable);
* Callers should ensure that this function is *NOT* called under RCU protection
* or in contexts where mutex locking cannot be used.
*/
-int opp_init_cpufreq_table(struct device *dev,
+int dev_pm_opp_init_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
{
struct device_opp *dev_opp;
@@ -638,7 +639,7 @@ int opp_init_cpufreq_table(struct device *dev,
}
freq_table = kzalloc(sizeof(struct cpufreq_frequency_table) *
- (opp_get_opp_count(dev) + 1), GFP_KERNEL);
+ (dev_pm_opp_get_opp_count(dev) + 1), GFP_KERNEL);
if (!freq_table) {
mutex_unlock(&dev_opp_list_lock);
dev_warn(dev, "%s: Unable to allocate frequency table\n",
@@ -648,30 +649,30 @@ int opp_init_cpufreq_table(struct device *dev,
list_for_each_entry(opp, &dev_opp->opp_list, node) {
if (opp->available) {
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = opp->rate / 1000;
i++;
}
}
mutex_unlock(&dev_opp_list_lock);
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
*table = &freq_table[0];
return 0;
}
-EXPORT_SYMBOL_GPL(opp_init_cpufreq_table);
+EXPORT_SYMBOL_GPL(dev_pm_opp_init_cpufreq_table);
/**
- * opp_free_cpufreq_table() - free the cpufreq table
+ * dev_pm_opp_free_cpufreq_table() - free the cpufreq table
* @dev: device for which we do this operation
* @table: table to free
*
- * Free up the table allocated by opp_init_cpufreq_table
+ * Free up the table allocated by dev_pm_opp_init_cpufreq_table
*/
-void opp_free_cpufreq_table(struct device *dev,
+void dev_pm_opp_free_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
{
if (!table)
@@ -680,14 +681,14 @@ void opp_free_cpufreq_table(struct device *dev,
kfree(*table);
*table = NULL;
}
-EXPORT_SYMBOL_GPL(opp_free_cpufreq_table);
+EXPORT_SYMBOL_GPL(dev_pm_opp_free_cpufreq_table);
#endif /* CONFIG_CPU_FREQ */
/**
- * opp_get_notifier() - find notifier_head of the device with opp
+ * dev_pm_opp_get_notifier() - find notifier_head of the device with opp
* @dev: device pointer used to lookup device OPPs.
*/
-struct srcu_notifier_head *opp_get_notifier(struct device *dev)
+struct srcu_notifier_head *dev_pm_opp_get_notifier(struct device *dev)
{
struct device_opp *dev_opp = find_device_opp(dev);
@@ -731,7 +732,7 @@ int of_init_opp_table(struct device *dev)
unsigned long freq = be32_to_cpup(val++) * 1000;
unsigned long volt = be32_to_cpup(val++);
- if (opp_add(dev, freq, volt)) {
+ if (dev_pm_opp_add(dev, freq, volt)) {
dev_warn(dev, "%s: Failed to add OPP %ld\n",
__func__, freq);
continue;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 2b870dfacb8a..5f165941f5f7 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -632,7 +632,7 @@ config MSM_ROTATOR_USE_IMEM
config MSM_ADSPRPC
tristate "Qualcomm ADSP RPC driver"
- depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2
+ depends on MSM_AUDIO_QDSP6
help
Provides a communication mechanism that allows for clients to
make remote method invocations across processor boundary to
@@ -657,7 +657,7 @@ config CSDIO_DEVICE_ID
config MSM_RDBG
tristate "Qualcomm Remote debug driver"
- depends on MSM_AUDIO_QDSP6 || MSM_AUDIO_QDSP6V2
+ depends on MSM_AUDIO_QDSP6
help
Implements a shared memory based transport mechanism that allows
for a debugger running on a host PC to communicate with a remote
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 77c3d5d8bcf7..eb76ea843412 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -433,6 +433,34 @@ static inline int __diag_dci_query_event_mask(struct diag_dci_client_tbl *entry,
return ((*event_mask_ptr & byte_mask) == byte_mask) ? 1 : 0;
}
+static int diag_dci_filter_commands(struct diag_pkt_header_t *header)
+{
+ if (!header)
+ return -ENOMEM;
+
+ switch (header->cmd_code) {
+ case 0x7d: /* Msg Mask Configuration */
+ case 0x73: /* Log Mask Configuration */
+ case 0x81: /* Event Mask Configuration */
+ case 0x82: /* Event Mask Change */
+ case 0x60: /* Event Mask Toggle */
+ return 1;
+ }
+
+ if (header->cmd_code == 0x4b && header->subsys_id == 0x12) {
+ switch (header->subsys_cmd_code) {
+ case 0x60: /* Extended Event Mask Config */
+ case 0x61: /* Extended Msg Mask Config */
+ case 0x62: /* Extended Log Mask Config */
+ case 0x20C: /* Set current Preset ID */
+ case 0x20D: /* Get current Preset ID */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static struct dci_pkt_req_entry_t *diag_register_dci_transaction(int uid)
{
struct dci_pkt_req_entry_t *entry = NULL;
@@ -986,8 +1014,8 @@ static int diag_send_dci_pkt(struct diag_master_table entry,
int diag_process_dci_transaction(unsigned char *buf, int len)
{
unsigned char *temp = buf;
- uint16_t subsys_cmd_code, log_code, item_num;
- int subsys_id, cmd_code, ret = -1, found = 0;
+ uint16_t log_code, item_num;
+ int ret = -1, found = 0, req_uid;
struct diag_master_table entry;
int count, set_mask, num_codes, bit_index, event_id, offset = 0, i;
unsigned int byte_index, read_len = 0;
@@ -995,6 +1023,7 @@ int diag_process_dci_transaction(unsigned char *buf, int len)
uint8_t *event_mask_ptr;
struct diag_dci_client_tbl *dci_entry = NULL;
struct dci_pkt_req_entry_t *req_entry = NULL;
+ struct diag_pkt_header_t *header = NULL;
if (!temp) {
pr_err("diag: Invalid buffer in %s\n", __func__);
@@ -1008,59 +1037,55 @@ int diag_process_dci_transaction(unsigned char *buf, int len)
__func__);
return -EIO;
}
- /* enter this UID into kernel table and return index */
- req_entry = diag_register_dci_transaction(*(int *)temp);
- if (!req_entry) {
- pr_alert("diag: registering new DCI transaction failed\n");
- return DIAG_DCI_NO_REG;
- }
+ req_uid = *(int *)temp;
temp += sizeof(int);
- /*
- * Check for registered peripheral and fwd pkt to
- * appropriate proc
- */
- cmd_code = (int)(*(char *)temp);
- temp++;
- subsys_id = (int)(*(char *)temp);
- temp++;
- subsys_cmd_code = *(uint16_t *)temp;
- temp += sizeof(uint16_t);
- read_len += sizeof(int) + 2 + sizeof(uint16_t);
+ header = (struct diag_pkt_header_t *)temp;
+ temp += sizeof(struct diag_pkt_header_t);
+ read_len = sizeof(int) + sizeof(struct diag_pkt_header_t);
if (read_len >= USER_SPACE_DATA) {
pr_err("diag: dci: Invalid length in %s\n", __func__);
return -EIO;
}
- pr_debug("diag: %d %d %d", cmd_code, subsys_id,
- subsys_cmd_code);
+ /* check if the command is allowed on DCI */
+ if (diag_dci_filter_commands(header)) {
+ pr_debug("diag: command not supported %d %d %d",
+ header->cmd_code,
+ header->subsys_id,
+ header->subsys_cmd_code);
+ return DIAG_DCI_SEND_DATA_FAIL;
+ }
+ /* enter this UID into kernel table */
+ req_entry = diag_register_dci_transaction(req_uid);
+ if (!req_entry) {
+ pr_alert("diag: registering new DCI transaction failed\n");
+ return DIAG_DCI_NO_REG;
+ }
for (i = 0; i < diag_max_reg; i++) {
entry = driver->table[i];
- if (entry.process_id != NO_PROCESS) {
- if (entry.cmd_code == cmd_code &&
- entry.subsys_id == subsys_id &&
- entry.cmd_code_lo <= subsys_cmd_code &&
- entry.cmd_code_hi >= subsys_cmd_code) {
+ if (entry.process_id == NO_PROCESS)
+ continue;
+ if (entry.cmd_code == header->cmd_code &&
+ entry.subsys_id == header->subsys_id &&
+ entry.cmd_code_lo <= header->subsys_cmd_code &&
+ entry.cmd_code_hi >= header->subsys_cmd_code) {
+ ret = diag_send_dci_pkt(entry, buf, len,
+ req_entry->tag);
+ } else if (entry.cmd_code == 255 &&
+ header->cmd_code == 75) {
+ if (entry.subsys_id == header->subsys_id &&
+ entry.cmd_code_lo <=
+ header->subsys_cmd_code &&
+ entry.cmd_code_hi >=
+ header->subsys_cmd_code) {
ret = diag_send_dci_pkt(entry, buf, len,
req_entry->tag);
- } else if (entry.cmd_code == 255
- && cmd_code == 75) {
- if (entry.subsys_id == subsys_id &&
- entry.cmd_code_lo <=
- subsys_cmd_code &&
- entry.cmd_code_hi >=
- subsys_cmd_code) {
- ret = diag_send_dci_pkt(entry,
- buf, len,
- req_entry->tag);
- }
- } else if (entry.cmd_code == 255 &&
- entry.subsys_id == 255) {
- if (entry.cmd_code_lo <= cmd_code &&
- entry.cmd_code_hi >=
- cmd_code) {
- ret = diag_send_dci_pkt(entry,
- buf, len,
+ }
+ } else if (entry.cmd_code == 255 &&
+ entry.subsys_id == 255) {
+ if (entry.cmd_code_lo <= header->cmd_code &&
+ entry.cmd_code_hi >= header->cmd_code) {
+ ret = diag_send_dci_pkt(entry, buf, len,
req_entry->tag);
- }
}
}
}
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 37bf60ec2c7a..4bbe94837c1b 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -183,7 +183,7 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
driver->real_time_mode);
#ifdef CONFIG_DIAG_OVER_USB
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
"usb_connected: %d\n",
driver->usb_connected);
#endif
@@ -197,8 +197,7 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos)
{
char *buf = NULL;
- unsigned int bytes_remaining, bytes_written = 0;
- unsigned int bytes_in_buf = 0, i = 0;
+ int bytes_remaining, bytes_written = 0, bytes_in_buf = 0, i = 0;
struct diag_dci_data_info *temp_data = dci_data_smd;
int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
@@ -336,7 +335,7 @@ static ssize_t diag_dbgfs_read_workpending(struct file *file,
diag_notify_update_smd_work)));
#ifdef CONFIG_DIAG_OVER_USB
- ret += scnprintf(buf+ret, DEBUG_BUF_SIZE-ret,
+ ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
"diag_proc_hdlc_work: %d\n"
"diag_read_work: %d\n",
work_pending(&(driver->diag_proc_hdlc_work)),
@@ -354,9 +353,9 @@ static ssize_t diag_dbgfs_read_table(struct file *file, char __user *ubuf,
char *buf;
int ret = 0;
int i;
- unsigned int bytes_remaining;
- unsigned int bytes_in_buffer = 0;
- unsigned int bytes_written;
+ int bytes_remaining;
+ int bytes_in_buffer = 0;
+ int bytes_written;
int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
if (diag_dbgfs_table_index >= diag_max_reg) {
@@ -527,9 +526,9 @@ static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf,
char *buf;
int ret;
int i;
- unsigned int bytes_remaining;
- unsigned int bytes_in_buffer = 0;
- unsigned int bytes_written;
+ int bytes_remaining;
+ int bytes_in_buffer = 0;
+ int bytes_written;
int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
int bytes_hsic_inited = 45;
int bytes_hsic_not_inited = 410;
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 502173e3bf5d..2a4221d4d366 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -172,6 +172,12 @@ enum remote_procs {
QSC = 5,
};
+struct diag_pkt_header_t {
+ uint8_t cmd_code;
+ uint8_t subsys_id;
+ uint16_t subsys_cmd_code;
+} __packed;
+
struct diag_master_table {
uint16_t cmd_code;
uint16_t subsys_id;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 485af7a67a98..2bbfb7a4fc4b 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -51,7 +51,6 @@ MODULE_DESCRIPTION("Diag Char Driver");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("1.0");
-#define MIN_SIZ_ALLOW 4
#define INIT 1
#define EXIT -1
struct diagchar_dev *driver;
@@ -1451,10 +1450,6 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf,
index = 0;
/* Get the packet type F3/log/event/Pkt response */
err = copy_from_user((&pkt_type), buf, 4);
- if (err) {
- pr_alert("diag: copy failed for pkt_type\n");
- return -EAGAIN;
- }
/* First 4 bytes indicate the type of payload - ignore these */
if (count < 4) {
pr_err("diag: Client sending short data\n");
@@ -1498,8 +1493,8 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf,
return err;
}
if (pkt_type == CALLBACK_DATA_TYPE) {
- if (payload_size > itemsize || payload_size <= MIN_SIZ_ALLOW) {
- pr_err("diag: Dropping packet, invalid packet size. Current payload size %d\n",
+ if (payload_size > driver->itemsize) {
+ pr_err("diag: Dropping packet, packet payload size crosses 4KB limit. Current payload size %d\n",
payload_size);
driver->dropped_count++;
return -EBADMSG;
@@ -1629,16 +1624,10 @@ static ssize_t diagchar_write(struct file *file, const char __user *buf,
return -EIO;
}
/* Check for proc_type */
- remote_proc = diag_get_remote(*(int *)user_space_data);
+ remote_proc =
+ diag_get_remote(*(int *)driver->user_space_data_buf);
+
if (remote_proc) {
- if (payload_size <= MIN_SIZ_ALLOW) {
- pr_err("diag: Integer underflow in %s, payload size: %d",
- __func__, payload_size);
- diagmem_free(driver, user_space_data,
- POOL_TYPE_USER);
- user_space_data = NULL;
- return -EBADMSG;
- }
token_offset = 4;
payload_size -= 4;
buf += 4;
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index 39f1f4487467..d5ba452877d3 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -177,8 +177,8 @@ int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc)
int msg_start;
if (hdlc && hdlc->src_ptr && hdlc->dest_ptr &&
- (hdlc->src_size > hdlc->src_idx) &&
- (hdlc->dest_size > hdlc->dest_idx)) {
+ (hdlc->src_size - hdlc->src_idx > 0) &&
+ (hdlc->dest_size - hdlc->dest_idx > 0)) {
msg_start = (hdlc->src_idx == 0) ? 1 : 0;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 424d74d6ae3c..aac2281a2073 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1942,7 +1942,6 @@ int diagfwd_disconnect(void)
printk(KERN_DEBUG "diag: USB disconnected\n");
driver->usb_connected = 0;
driver->debug_flag = 1;
- usb_diag_free_req(driver->legacy_ch);
if (driver->logging_mode == USB_MODE) {
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
smd_info = &driver->smd_data[i];
@@ -2197,12 +2196,10 @@ static int diag_smd_probe(struct platform_device *pdev)
index = MODEM_DATA;
channel_name = "DIAG";
}
-#if defined(CONFIG_MSM_N_WAY_SMD)
else if (pdev->id == SMD_APPS_QDSP) {
index = LPASS_DATA;
channel_name = "DIAG";
}
-#endif
else if (pdev->id == SMD_APPS_WCNSS) {
index = WCNSS_DATA;
channel_name = "APPS_RIVA_DATA";
@@ -2711,8 +2708,6 @@ void diagfwd_exit(void)
diag_smd_destructor(&driver->smd_data[i]);
#ifdef CONFIG_DIAG_OVER_USB
- if (driver->usb_connected)
- usb_diag_free_req(driver->legacy_ch);
usb_diag_close(driver->legacy_ch);
#endif
platform_driver_unregister(&msm_smd_ch1_driver);
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
index 143959b39a4b..142d15a6f217 100644
--- a/drivers/char/diag/diagfwd_bridge.c
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -133,7 +133,6 @@ int diagfwd_disconnect_bridge(int process_cable)
/* If the usb cable is being disconnected */
if (process_cable) {
diag_bridge[i].usb_connected = 0;
- usb_diag_free_req(diag_bridge[i].ch);
}
if (i == SMUX) {
@@ -383,8 +382,6 @@ void diagfwd_bridge_exit(void)
for (i = 0; i < MAX_BRIDGES; i++) {
if (diag_bridge[i].enabled) {
#ifdef CONFIG_DIAG_OVER_USB
- if (diag_bridge[i].usb_connected)
- usb_diag_free_req(diag_bridge[i].ch);
usb_diag_close(diag_bridge[i].ch);
#endif
kfree(diag_bridge[i].usb_buf_out);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index e383dafcc57d..64399318b834 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -349,7 +349,7 @@ void diag_send_diag_mode_update_by_smd(struct diag_smd_info *smd_info,
int wr_size = -ENOMEM, retry_count = 0, timer;
struct diag_smd_info *data = NULL;
- if (!smd_info && smd_info->type != SMD_CNTL_TYPE) {
+ if (!smd_info || smd_info->type != SMD_CNTL_TYPE) {
pr_err("diag: In %s, invalid channel info, smd_info: %p type: %d\n",
__func__, smd_info,
((smd_info) ? smd_info->type : -1));
@@ -483,12 +483,10 @@ static int diag_smd_cntl_probe(struct platform_device *pdev)
index = MODEM_DATA;
channel_name = "DIAG_CNTL";
}
-#if defined(CONFIG_MSM_N_WAY_SMD)
else if (pdev->id == SMD_APPS_QDSP) {
index = LPASS_DATA;
channel_name = "DIAG_CNTL";
}
-#endif
else if (pdev->id == SMD_APPS_WCNSS) {
index = WCNSS_DATA;
channel_name = "APPS_RIVA_CTRL";
diff --git a/drivers/char/diag/diagfwd_sdio.c b/drivers/char/diag/diagfwd_sdio.c
index 7d4e0d54f8a0..ef56a1b22b7f 100644
--- a/drivers/char/diag/diagfwd_sdio.c
+++ b/drivers/char/diag/diagfwd_sdio.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -131,7 +131,6 @@ int diagfwd_connect_sdio(void)
int diagfwd_disconnect_sdio(void)
{
- usb_diag_free_req(driver->mdm_ch);
if (driver->sdio_ch && (driver->logging_mode == USB_MODE)) {
driver->in_busy_sdio = 1;
diag_sdio_close();
@@ -280,10 +279,6 @@ err:
void diagfwd_sdio_exit(void)
{
-#ifdef CONFIG_DIAG_OVER_USB
- if (driver->usb_connected)
- usb_diag_free_req(driver->mdm_ch);
-#endif
platform_driver_unregister(&msm_sdio_ch_driver);
#ifdef CONFIG_DIAG_OVER_USB
usb_diag_close(driver->mdm_ch);
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f79ea4d0b30c..07f8776ed507 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
+obj-$(CONFIG_ARCH_MSM) += qcom/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index 9b570816ca4d..757c2ccb5acd 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -143,7 +143,7 @@ struct clk *clk_get_sys(const char *dev_id, const char *con_id)
cl = NULL;
mutex_unlock(&clocks_mutex);
- return cl ? cl->clk : ERR_PTR(-ENOENT);
+ return cl ? cl->clk : ERR_PTR(-EPROBE_DEFER);
}
EXPORT_SYMBOL(clk_get_sys);
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
new file mode 100644
index 000000000000..88de4708ace1
--- /dev/null
+++ b/drivers/clk/qcom/Makefile
@@ -0,0 +1,5 @@
+obj-y += clock.o
+obj-y += clock-dummy.o
+obj-y += clock-generic.o
+
+obj-$(CONFIG_DEBUG_FS) += clock-debug.o
diff --git a/drivers/clk/qcom/clock-debug.c b/drivers/clk/qcom/clock-debug.c
new file mode 100644
index 000000000000..9cdd2f0c7b2f
--- /dev/null
+++ b/drivers/clk/qcom/clock-debug.c
@@ -0,0 +1,643 @@
+/*
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <linux/clk/msm-clk-provider.h>
+
+
+#include "clock.h"
+
+static LIST_HEAD(clk_list);
+static DEFINE_MUTEX(clk_list_lock);
+
+static struct dentry *debugfs_base;
+static u32 debug_suspend;
+
+struct clk_table {
+ struct list_head node;
+ struct clk_lookup *clocks;
+ size_t num_clocks;
+};
+
+static int clock_debug_rate_set(void *data, u64 val)
+{
+ struct clk *clock = data;
+ int ret;
+
+ /* Only increases to max rate will succeed, but that's actually good
+ * for debugging purposes so we don't check for error. */
+ if (clock->flags & CLKFLAG_MAX)
+ clk_set_max_rate(clock, val);
+ ret = clk_set_rate(clock, val);
+ if (ret)
+ pr_err("clk_set_rate(%s, %lu) failed (%d)\n", clock->dbg_name,
+ (unsigned long)val, ret);
+
+ return ret;
+}
+
+static int clock_debug_rate_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ *val = clk_get_rate(clock);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_rate_fops, clock_debug_rate_get,
+ clock_debug_rate_set, "%llu\n");
+
+static struct clk *measure;
+
+static int clock_debug_measure_get(void *data, u64 *val)
+{
+ struct clk *clock = data, *par;
+ int ret, is_hw_gated;
+ unsigned long meas_rate, sw_rate;
+
+ /* Check to see if the clock is in hardware gating mode */
+ if (clock->ops->in_hwcg_mode)
+ is_hw_gated = clock->ops->in_hwcg_mode(clock);
+ else
+ is_hw_gated = 0;
+
+ ret = clk_set_parent(measure, clock);
+ if (!ret) {
+ /*
+ * Disable hw gating to get accurate rate measurements. Only do
+ * this if the clock is explictly enabled by software. This
+ * allows us to detect errors where clocks are on even though
+ * software is not requesting them to be on due to broken
+ * hardware gating signals.
+ */
+ if (is_hw_gated && clock->count)
+ clock->ops->disable_hwcg(clock);
+ par = measure;
+ while (par && par != clock) {
+ if (par->ops->enable)
+ par->ops->enable(par);
+ par = par->parent;
+ }
+ *val = clk_get_rate(measure);
+ /* Reenable hwgating if it was disabled */
+ if (is_hw_gated && clock->count)
+ clock->ops->enable_hwcg(clock);
+ }
+
+ /*
+ * If there's a divider on the path from the clock output to the
+ * measurement circuitry, account for it by dividing the original clock
+ * rate with the rate set on the parent of the measure clock.
+ */
+ meas_rate = clk_get_rate(clock);
+ sw_rate = clk_get_rate(measure->parent);
+ if (sw_rate && meas_rate >= (sw_rate * 2))
+ *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate);
+
+ return ret;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_measure_fops, clock_debug_measure_get,
+ NULL, "%lld\n");
+
+static int clock_debug_enable_set(void *data, u64 val)
+{
+ struct clk *clock = data;
+ int rc = 0;
+
+ if (val)
+ rc = clk_prepare_enable(clock);
+ else
+ clk_disable_unprepare(clock);
+
+ return rc;
+}
+
+static int clock_debug_enable_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ int enabled;
+
+ if (clock->ops->is_enabled)
+ enabled = clock->ops->is_enabled(clock);
+ else
+ enabled = !!(clock->count);
+
+ *val = enabled;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_enable_fops, clock_debug_enable_get,
+ clock_debug_enable_set, "%lld\n");
+
+static int clock_debug_local_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+
+ if (!clock->ops->is_local)
+ *val = true;
+ else
+ *val = clock->ops->is_local(clock);
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
+ NULL, "%llu\n");
+
+static int clock_debug_hwcg_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ if (clock->ops->in_hwcg_mode)
+ *val = !!clock->ops->in_hwcg_mode(clock);
+ else
+ *val = 0;
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get,
+ NULL, "%llu\n");
+
+static void clock_print_fmax_by_level(struct seq_file *m, int level)
+{
+ struct clk *clock = m->private;
+ struct clk_vdd_class *vdd_class = clock->vdd_class;
+ int off, i, vdd_level, nregs = vdd_class->num_regulators;
+
+ vdd_level = find_vdd_level(clock, clock->rate);
+
+ seq_printf(m, "%2s%10lu", vdd_level == level ? "[" : "",
+ clock->fmax[level]);
+ for (i = 0; i < nregs; i++) {
+ off = nregs*level + i;
+ if (vdd_class->vdd_uv)
+ seq_printf(m, "%10u", vdd_class->vdd_uv[off]);
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10u", vdd_class->vdd_ua[off]);
+ }
+
+ if (vdd_level == level)
+ seq_puts(m, "]");
+ seq_puts(m, "\n");
+}
+
+static int fmax_rates_show(struct seq_file *m, void *unused)
+{
+ struct clk *clock = m->private;
+ struct clk_vdd_class *vdd_class = clock->vdd_class;
+ int level = 0, i, nregs = vdd_class->num_regulators;
+ char reg_name[10];
+
+ int vdd_level = find_vdd_level(clock, clock->rate);
+ if (vdd_level < 0) {
+ seq_printf(m, "could not find_vdd_level for %s, %ld\n",
+ clock->dbg_name, clock->rate);
+ return 0;
+ }
+
+ seq_printf(m, "%12s", "");
+ for (i = 0; i < nregs; i++) {
+ snprintf(reg_name, ARRAY_SIZE(reg_name), "reg %d", i);
+ seq_printf(m, "%10s", reg_name);
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10s", "");
+ }
+
+ seq_printf(m, "\n%12s", "freq");
+ for (i = 0; i < nregs; i++) {
+ seq_printf(m, "%10s", "uV");
+ if (vdd_class->vdd_ua)
+ seq_printf(m, "%10s", "uA");
+ }
+ seq_printf(m, "\n");
+
+ for (level = 0; level < clock->num_fmax; level++)
+ clock_print_fmax_by_level(m, level);
+
+ return 0;
+}
+
+static int fmax_rates_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fmax_rates_show, inode->i_private);
+}
+
+static const struct file_operations fmax_rates_fops = {
+ .open = fmax_rates_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+#define clock_debug_output(m, c, fmt, ...) \
+do { \
+ if (m) \
+ seq_printf(m, fmt, ##__VA_ARGS__); \
+ else if (c) \
+ pr_cont(fmt, ##__VA_ARGS__); \
+ else \
+ pr_info(fmt, ##__VA_ARGS__); \
+} while (0)
+
+static int clock_debug_print_clock(struct clk *c, struct seq_file *m)
+{
+ char *start = "";
+
+ if (!c || !c->prepare_count)
+ return 0;
+
+ clock_debug_output(m, 0, "\t");
+ do {
+ if (c->vdd_class)
+ clock_debug_output(m, 1, "%s%s:%u:%u [%ld, %lu]", start,
+ c->dbg_name, c->prepare_count, c->count,
+ c->rate, c->vdd_class->cur_level);
+ else
+ clock_debug_output(m, 1, "%s%s:%u:%u [%ld]", start,
+ c->dbg_name, c->prepare_count, c->count,
+ c->rate);
+ start = " -> ";
+ } while ((c = clk_get_parent(c)));
+
+ clock_debug_output(m, 1, "\n");
+
+ return 1;
+}
+
+/**
+ * clock_debug_print_enabled_clocks() - Print names of enabled clocks
+ *
+ */
+static void clock_debug_print_enabled_clocks(struct seq_file *m)
+{
+ struct clk_table *table;
+ int i, cnt = 0;
+
+ if (!mutex_trylock(&clk_list_lock)) {
+ pr_err("clock-debug: Clocks are being registered. Cannot print clock state now.\n");
+ return;
+ }
+ clock_debug_output(m, 0, "Enabled clocks:\n");
+ list_for_each_entry(table, &clk_list, node) {
+ for (i = 0; i < table->num_clocks; i++)
+ cnt += clock_debug_print_clock(table->clocks[i].clk, m);
+ }
+ mutex_unlock(&clk_list_lock);
+
+ if (cnt)
+ clock_debug_output(m, 0, "Enabled clock count: %d\n", cnt);
+ else
+ clock_debug_output(m, 0, "No clocks enabled.\n");
+}
+
+static int enabled_clocks_show(struct seq_file *m, void *unused)
+{
+ clock_debug_print_enabled_clocks(m);
+ return 0;
+}
+
+static int enabled_clocks_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, enabled_clocks_show, inode->i_private);
+}
+
+static const struct file_operations enabled_clocks_fops = {
+ .open = enabled_clocks_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static int list_rates_show(struct seq_file *m, void *unused)
+{
+ struct clk *clock = m->private;
+ int level, i = 0;
+ unsigned long rate, fmax = 0;
+
+ /* Find max frequency supported within voltage constraints. */
+ if (!clock->vdd_class) {
+ fmax = ULONG_MAX;
+ } else {
+ for (level = 0; level < clock->num_fmax; level++)
+ if (clock->fmax[level])
+ fmax = clock->fmax[level];
+ }
+
+ /*
+ * List supported frequencies <= fmax. Higher frequencies may appear in
+ * the frequency table, but are not valid and should not be listed.
+ */
+ while (!IS_ERR_VALUE(rate = clock->ops->list_rate(clock, i++))) {
+ if (rate <= fmax)
+ seq_printf(m, "%lu\n", rate);
+ }
+
+ return 0;
+}
+
+static int list_rates_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, list_rates_show, inode->i_private);
+}
+
+static const struct file_operations list_rates_fops = {
+ .open = list_rates_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t clock_parent_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct clk *clock = filp->private_data;
+ struct clk *p = clock->parent;
+ char name[256] = {0};
+
+ snprintf(name, sizeof(name), "%s\n", p ? p->dbg_name : "None\n");
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, name, strlen(name));
+}
+
+
+static ssize_t clock_parent_write(struct file *filp,
+ const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ struct clk *clock = filp->private_data;
+ char buf[256];
+ char *cmp;
+ struct clk_table *table;
+ int i, ret;
+ struct clk *parent = NULL;
+
+ cnt = min(cnt, sizeof(buf) - 1);
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+ buf[cnt] = '\0';
+ cmp = strstrip(buf);
+
+ mutex_lock(&clk_list_lock);
+ list_for_each_entry(table, &clk_list, node) {
+ for (i = 0; i < table->num_clocks; i++)
+ if (!strcmp(cmp, table->clocks[i].clk->dbg_name)) {
+ parent = table->clocks[i].clk;
+ break;
+ }
+ if (parent)
+ break;
+ }
+
+ if (!parent) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_unlock(&clk_list_lock);
+ ret = clk_set_parent(clock, table->clocks[i].clk);
+ if (ret)
+ return ret;
+
+ return cnt;
+err:
+ mutex_unlock(&clk_list_lock);
+ return ret;
+}
+
+
+static const struct file_operations clock_parent_fops = {
+ .open = simple_open,
+ .read = clock_parent_read,
+ .write = clock_parent_write,
+};
+
+void clk_debug_print_hw(struct clk *clk, struct seq_file *f)
+{
+ void __iomem *base;
+ struct clk_register_data *regs;
+ u32 i, j, size;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ clk_debug_print_hw(clk->parent, f);
+
+ clock_debug_output(f, false, "%s\n", clk->dbg_name);
+
+ if (!clk->ops->list_registers)
+ return;
+
+ j = 0;
+ base = clk->ops->list_registers(clk, j, &regs, &size);
+ while (!IS_ERR(base)) {
+ for (i = 0; i < size; i++) {
+ u32 val = readl_relaxed(base + regs[i].offset);
+ clock_debug_output(f, false, "%20s: 0x%.8x\n",
+ regs[i].name, val);
+ }
+ j++;
+ base = clk->ops->list_registers(clk, j, &regs, &size);
+ }
+}
+
+static int print_hw_show(struct seq_file *m, void *unused)
+{
+ struct clk *c = m->private;
+ clk_debug_print_hw(c, m);
+
+ return 0;
+}
+
+static int print_hw_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, print_hw_show, inode->i_private);
+}
+
+static const struct file_operations clock_print_hw_fops = {
+ .open = print_hw_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+
+static void clock_measure_add(struct clk *clock)
+{
+ if (IS_ERR_OR_NULL(measure))
+ return;
+
+ if (clk_set_parent(measure, clock))
+ return;
+
+ debugfs_create_file("measure", S_IRUGO, clock->clk_dir, clock,
+ &clock_measure_fops);
+}
+
+static int clock_debug_add(struct clk *clock)
+{
+ char temp[50], *ptr;
+ struct dentry *clk_dir;
+
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ strlcpy(temp, clock->dbg_name, ARRAY_SIZE(temp));
+ for (ptr = temp; *ptr; ptr++)
+ *ptr = tolower(*ptr);
+
+ clk_dir = debugfs_create_dir(temp, debugfs_base);
+ if (!clk_dir)
+ return -ENOMEM;
+
+ clock->clk_dir = clk_dir;
+
+ if (!debugfs_create_file("rate", S_IRUGO | S_IWUSR, clk_dir,
+ clock, &clock_rate_fops))
+ goto error;
+
+ if (!debugfs_create_file("enable", S_IRUGO | S_IWUSR, clk_dir,
+ clock, &clock_enable_fops))
+ goto error;
+
+ if (!debugfs_create_file("is_local", S_IRUGO, clk_dir, clock,
+ &clock_local_fops))
+ goto error;
+
+ if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock,
+ &clock_hwcg_fops))
+ goto error;
+
+ if (clock->ops->list_rate)
+ if (!debugfs_create_file("list_rates",
+ S_IRUGO, clk_dir, clock, &list_rates_fops))
+ goto error;
+
+ if (clock->vdd_class && !debugfs_create_file("fmax_rates",
+ S_IRUGO, clk_dir, clock, &fmax_rates_fops))
+ goto error;
+
+ if (!debugfs_create_file("parent", S_IRUGO, clk_dir, clock,
+ &clock_parent_fops))
+ goto error;
+
+ if (!debugfs_create_file("print", S_IRUGO, clk_dir, clock,
+ &clock_print_hw_fops))
+ goto error;
+
+ clock_measure_add(clock);
+
+ return 0;
+error:
+ debugfs_remove_recursive(clk_dir);
+ return -ENOMEM;
+}
+static DEFINE_MUTEX(clk_debug_lock);
+static int clk_debug_init_once;
+
+/**
+ * clock_debug_init() - Initialize clock debugfs
+ * Lock clk_debug_lock before invoking this function.
+ */
+static int clock_debug_init(void)
+{
+ if (clk_debug_init_once)
+ return 0;
+
+ clk_debug_init_once = 1;
+
+ debugfs_base = debugfs_create_dir("clk", NULL);
+ if (!debugfs_base)
+ return -ENOMEM;
+
+ if (!debugfs_create_u32("debug_suspend", S_IRUGO | S_IWUSR,
+ debugfs_base, &debug_suspend)) {
+ debugfs_remove_recursive(debugfs_base);
+ return -ENOMEM;
+ }
+
+ if (!debugfs_create_file("enabled_clocks", S_IRUGO, debugfs_base, NULL,
+ &enabled_clocks_fops))
+ return -ENOMEM;
+
+ return 0;
+}
+
+/**
+ * clock_debug_register() - Add additional clocks to clock debugfs hierarchy
+ * @table: Table of clocks to create debugfs nodes for
+ * @size: Size of @table
+ *
+ */
+int clock_debug_register(struct clk_lookup *table, size_t size)
+{
+ struct clk_table *clk_table, *clk_table_tmp;
+ int i, ret;
+
+ mutex_lock(&clk_debug_lock);
+
+ ret = clock_debug_init();
+ if (ret)
+ goto out;
+
+ clk_table = kmalloc(sizeof(*clk_table), GFP_KERNEL);
+ if (!clk_table) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ clk_table->clocks = table;
+ clk_table->num_clocks = size;
+
+ if (IS_ERR_OR_NULL(measure)) {
+ measure = clk_get_sys("debug", "measure");
+ if (!IS_ERR(measure)) {
+ mutex_lock(&clk_list_lock);
+ list_for_each_entry(clk_table_tmp, &clk_list, node) {
+ for (i = 0; i < clk_table_tmp->num_clocks; i++)
+ clock_measure_add(clk_table_tmp->clocks[i].clk);
+ }
+ mutex_unlock(&clk_list_lock);
+ }
+ }
+
+ mutex_lock(&clk_list_lock);
+ list_add_tail(&clk_table->node, &clk_list);
+ mutex_unlock(&clk_list_lock);
+
+ for (i = 0; i < size; i++)
+ clock_debug_add(table[i].clk);
+out:
+ mutex_unlock(&clk_debug_lock);
+ return ret;
+}
+
+/*
+ * Print the names of enabled clocks and their parents if debug_suspend is set
+ */
+void clock_debug_print_enabled(void)
+{
+ if (likely(!debug_suspend))
+ return;
+
+ clock_debug_print_enabled_clocks(NULL);
+}
diff --git a/drivers/clk/qcom/clock-dummy.c b/drivers/clk/qcom/clock-dummy.c
new file mode 100644
index 000000000000..e86aaae2f0de
--- /dev/null
+++ b/drivers/clk/qcom/clock-dummy.c
@@ -0,0 +1,100 @@
+/* Copyright (c) 2011,2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/clk/msm-clk-provider.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+static int dummy_clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+ return 0;
+}
+
+static int dummy_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ clk->rate = rate;
+ return 0;
+}
+
+static int dummy_clk_set_max_rate(struct clk *clk, unsigned long rate)
+{
+ return 0;
+}
+
+static int dummy_clk_set_flags(struct clk *clk, unsigned flags)
+{
+ return 0;
+}
+
+static unsigned long dummy_clk_get_rate(struct clk *clk)
+{
+ return clk->rate;
+}
+
+static long dummy_clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ return rate;
+}
+
+struct clk_ops clk_ops_dummy = {
+ .reset = dummy_clk_reset,
+ .set_rate = dummy_clk_set_rate,
+ .set_max_rate = dummy_clk_set_max_rate,
+ .set_flags = dummy_clk_set_flags,
+ .get_rate = dummy_clk_get_rate,
+ .round_rate = dummy_clk_round_rate,
+};
+
+struct clk dummy_clk = {
+ .dbg_name = "dummy_clk",
+ .ops = &clk_ops_dummy,
+ CLK_INIT(dummy_clk),
+};
+
+static struct clk *of_dummy_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ return &dummy_clk;
+}
+
+static struct of_device_id msm_clock_dummy_match_table[] = {
+ { .compatible = "qcom,dummycc" },
+ {}
+};
+
+static int msm_clock_dummy_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = of_clk_add_provider(pdev->dev.of_node, of_dummy_get, NULL);
+ if (ret)
+ return -ENOMEM;
+
+ dev_info(&pdev->dev, "Registered DUMMY provider.\n");
+ return ret;
+}
+
+static struct platform_driver msm_clock_dummy_driver = {
+ .probe = msm_clock_dummy_probe,
+ .driver = {
+ .name = "clock-dummy",
+ .of_match_table = msm_clock_dummy_match_table,
+ .owner = THIS_MODULE,
+ },
+};
+
+int __init msm_dummy_clk_init(void)
+{
+ return platform_driver_register(&msm_clock_dummy_driver);
+}
+arch_initcall(msm_dummy_clk_init);
+
diff --git a/drivers/clk/qcom/clock-generic.c b/drivers/clk/qcom/clock-generic.c
new file mode 100644
index 000000000000..9efc9f8c1de6
--- /dev/null
+++ b/drivers/clk/qcom/clock-generic.c
@@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clock-generic.h>
+
+/* ==================== Mux clock ==================== */
+
+int parent_to_src_sel(struct clk_src *parents, int num_parents, struct clk *p)
+{
+ int i;
+
+ for (i = 0; i < num_parents; i++) {
+ if (parents[i].src == p)
+ return parents[i].sel;
+ }
+
+ return -EINVAL;
+}
+
+static int mux_set_parent(struct clk *c, struct clk *p)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = parent_to_src_sel(mux->parents, mux->num_parents, p);
+ struct clk *old_parent;
+ int rc = 0, i;
+ unsigned long flags;
+
+ if (sel < 0 && mux->rec_set_par) {
+ for (i = 0; i < mux->num_parents; i++) {
+ rc = clk_set_parent(mux->parents[i].src, p);
+ if (!rc) {
+ sel = mux->parents[i].sel;
+ /*
+ * This is necessary to ensure prepare/enable
+ * counts get propagated correctly.
+ */
+ p = mux->parents[i].src;
+ break;
+ }
+ }
+ }
+
+ if (sel < 0)
+ return sel;
+
+ rc = __clk_pre_reparent(c, p, &flags);
+ if (rc)
+ goto out;
+
+ rc = mux->ops->set_mux_sel(mux, sel);
+ if (rc)
+ goto set_fail;
+
+ old_parent = c->parent;
+ c->parent = p;
+ c->rate = clk_get_rate(p);
+ __clk_post_reparent(c, old_parent, &flags);
+
+ return 0;
+
+set_fail:
+ __clk_post_reparent(c, p, &flags);
+out:
+ return rc;
+}
+
+static long mux_round_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int i;
+ unsigned long prate, rrate = 0;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ prate = clk_round_rate(mux->parents[i].src, rate);
+ if (is_better_rate(rate, rrate, prate))
+ rrate = prate;
+ }
+ if (!rrate)
+ return -EINVAL;
+
+ return rrate;
+}
+
+static int mux_set_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ struct clk *new_parent = NULL;
+ int rc = 0, i;
+ unsigned long new_par_curr_rate;
+ unsigned long flags;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (clk_round_rate(mux->parents[i].src, rate) == rate) {
+ new_parent = mux->parents[i].src;
+ break;
+ }
+ }
+ if (new_parent == NULL)
+ return -EINVAL;
+
+ /*
+ * Switch to safe parent since the old and new parent might be the
+ * same and the parent might temporarily turn off while switching
+ * rates.
+ */
+ if (mux->safe_sel >= 0) {
+ /*
+ * Some mux implementations might switch to/from a low power
+ * parent as part of their disable/enable ops. Grab the
+ * enable lock to avoid racing with these implementations.
+ */
+ spin_lock_irqsave(&c->lock, flags);
+ rc = mux->ops->set_mux_sel(mux, mux->safe_sel);
+ spin_unlock_irqrestore(&c->lock, flags);
+ }
+ if (rc)
+ return rc;
+
+ new_par_curr_rate = clk_get_rate(new_parent);
+ rc = clk_set_rate(new_parent, rate);
+ if (rc)
+ goto set_rate_fail;
+
+ rc = mux_set_parent(c, new_parent);
+ if (rc)
+ goto set_par_fail;
+
+ return 0;
+
+set_par_fail:
+ clk_set_rate(new_parent, new_par_curr_rate);
+set_rate_fail:
+ WARN(mux->ops->set_mux_sel(mux,
+ parent_to_src_sel(mux->parents, mux->num_parents, c->parent)),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+ return rc;
+}
+
+static int mux_enable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->enable)
+ return mux->ops->enable(mux);
+ return 0;
+}
+
+static void mux_disable(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ if (mux->ops->disable)
+ return mux->ops->disable(mux);
+}
+
+static struct clk *mux_get_parent(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+ int sel = mux->ops->get_mux_sel(mux);
+ int i;
+
+ for (i = 0; i < mux->num_parents; i++) {
+ if (mux->parents[i].sel == sel)
+ return mux->parents[i].src;
+ }
+
+ /* Unfamiliar parent. */
+ return NULL;
+}
+
+static enum handoff mux_handoff(struct clk *c)
+{
+ struct mux_clk *mux = to_mux_clk(c);
+
+ c->rate = clk_get_rate(c->parent);
+ mux->safe_sel = parent_to_src_sel(mux->parents, mux->num_parents,
+ mux->safe_parent);
+
+ if (mux->en_mask && mux->ops && mux->ops->is_enabled)
+ return mux->ops->is_enabled(mux)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_gen_mux = {
+ .enable = mux_enable,
+ .disable = mux_disable,
+ .set_parent = mux_set_parent,
+ .round_rate = mux_round_rate,
+ .set_rate = mux_set_rate,
+ .handoff = mux_handoff,
+ .get_parent = mux_get_parent,
+};
+
+/* ==================== Divider clock ==================== */
+
+static long __div_round_rate(struct div_data *data, unsigned long rate,
+ struct clk *parent, unsigned int *best_div, unsigned long *best_prate)
+{
+ unsigned int div, min_div, max_div, _best_div = 1;
+ unsigned long prate, _best_prate = 0, rrate = 0;
+
+ rate = max(rate, 1UL);
+
+ min_div = max(data->min_div, 1U);
+ max_div = min(data->max_div, (unsigned int) (ULONG_MAX / rate));
+
+ for (div = min_div; div <= max_div; div++) {
+ prate = clk_round_rate(parent, rate * div);
+ if (IS_ERR_VALUE(prate))
+ break;
+
+ if (is_better_rate(rate, rrate, prate / div)) {
+ rrate = prate / div;
+ _best_div = div;
+ _best_prate = prate;
+ }
+
+ /*
+ * Trying higher dividers is only going to ask the parent for
+ * a higher rate. If it can't even output a rate higher than
+ * the one we request for this divider, the parent is not
+ * going to be able to output an even higher rate required
+ * for a higher divider. So, stop trying higher dividers.
+ */
+ if (prate / div < rate)
+ break;
+
+ if (rrate <= rate + data->rate_margin)
+ break;
+ }
+
+ if (!rrate)
+ return -EINVAL;
+ if (best_div)
+ *best_div = _best_div;
+ if (best_prate)
+ *best_prate = _best_prate;
+
+ return rrate;
+}
+
+static long div_round_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+
+ return __div_round_rate(&d->data, rate, c->parent, NULL, NULL);
+}
+
+static int div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int div, rc = 0;
+ long rrate, old_prate, new_prate;
+ struct div_data *data = &d->data;
+
+ rrate = __div_round_rate(data, rate, c->parent, &div, &new_prate);
+ if (rrate != rate)
+ return -EINVAL;
+
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __div_round_rate() ensures div ==
+ * d->div if !d->ops.
+ */
+ if (div > data->div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ return rc;
+
+ old_prate = clk_get_rate(c->parent);
+ rc = clk_set_rate(c->parent, new_prate);
+ if (rc)
+ goto set_rate_fail;
+
+ if (div < data->div)
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ goto div_dec_fail;
+
+ data->div = div;
+
+ return 0;
+
+div_dec_fail:
+ WARN(clk_set_rate(c->parent, old_prate),
+ "Set rate failed for %s. Also in bad state!\n", c->dbg_name);
+set_rate_fail:
+ if (div > data->div)
+ WARN(d->ops->set_div(d, data->div),
+ "Set rate failed for %s. Also in bad state!\n",
+ c->dbg_name);
+ return rc;
+}
+
+static int div_enable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops && d->ops->enable)
+ return d->ops->enable(d);
+ return 0;
+}
+
+static void div_disable(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (d->ops && d->ops->disable)
+ return d->ops->disable(d);
+}
+
+static enum handoff div_handoff(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div = d->data.div;
+
+ if (d->ops && d->ops->get_div)
+ div = max(d->ops->get_div(d), 1);
+ div = max(div, 1U);
+ c->rate = clk_get_rate(c->parent) / div;
+
+ if (!d->ops || !d->ops->set_div)
+ d->data.min_div = d->data.max_div = div;
+ d->data.div = div;
+
+ if (d->en_mask && d->ops && d->ops->is_enabled)
+ return d->ops->is_enabled(d)
+ ? HANDOFF_ENABLED_CLK
+ : HANDOFF_DISABLED_CLK;
+
+ /*
+ * If this function returns 'enabled' even when the clock downstream
+ * of this clock is disabled, then handoff code will unnecessarily
+ * enable the current parent of this clock. If this function always
+ * returns 'disabled' and a clock downstream is on, the clock handoff
+ * code will bump up the ref count for this clock and its current
+ * parent as necessary. So, clocks without an actual HW gate can
+ * always return disabled.
+ */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = div_round_rate,
+ .set_rate = div_set_rate,
+ .handoff = div_handoff,
+};
+
+static long __slave_div_round_rate(struct clk *c, unsigned long rate,
+ int *best_div)
+{
+ struct div_clk *d = to_div_clk(c);
+ unsigned int div, min_div, max_div;
+ long p_rate;
+
+ rate = max(rate, 1UL);
+
+ min_div = d->data.min_div;
+ max_div = d->data.max_div;
+
+ p_rate = clk_get_rate(c->parent);
+ div = p_rate / rate;
+ div = max(div, min_div);
+ div = min(div, max_div);
+ if (best_div)
+ *best_div = div;
+
+ return p_rate / div;
+}
+
+static long slave_div_round_rate(struct clk *c, unsigned long rate)
+{
+ return __slave_div_round_rate(c, rate, NULL);
+}
+
+static int slave_div_set_rate(struct clk *c, unsigned long rate)
+{
+ struct div_clk *d = to_div_clk(c);
+ int div, rc = 0;
+ long rrate;
+
+ rrate = __slave_div_round_rate(c, rate, &div);
+ if (rrate != rate)
+ return -EINVAL;
+
+ if (div == d->data.div)
+ return 0;
+
+ /*
+ * For fixed divider clock we don't want to return an error if the
+ * requested rate matches the achievable rate. So, don't check for
+ * !d->ops and return an error. __slave_div_round_rate() ensures
+ * div == d->data.div if !d->ops.
+ */
+ rc = d->ops->set_div(d, div);
+ if (rc)
+ return rc;
+
+ d->data.div = div;
+
+ return 0;
+}
+
+static unsigned long slave_div_get_rate(struct clk *c)
+{
+ struct div_clk *d = to_div_clk(c);
+ if (!d->data.div)
+ return 0;
+ return clk_get_rate(c->parent) / d->data.div;
+}
+
+struct clk_ops clk_ops_slave_div = {
+ .enable = div_enable,
+ .disable = div_disable,
+ .round_rate = slave_div_round_rate,
+ .set_rate = slave_div_set_rate,
+ .get_rate = slave_div_get_rate,
+ .handoff = div_handoff,
+};
+
+
+/**
+ * External clock
+ * Some clock controllers have input clock signal that come from outside the
+ * clock controller. That input clock signal might then be used as a source for
+ * several clocks inside the clock controller. This external clock
+ * implementation models this input clock signal by just passing on the requests
+ * to the clock's parent, the original external clock source. The driver for the
+ * clock controller should clk_get() the original external clock in the probe
+ * function and set is as a parent to this external clock..
+ */
+
+static long ext_round_rate(struct clk *c, unsigned long rate)
+{
+ return clk_round_rate(c->parent, rate);
+}
+
+static int ext_set_rate(struct clk *c, unsigned long rate)
+{
+ return clk_set_rate(c->parent, rate);
+}
+
+static unsigned long ext_get_rate(struct clk *c)
+{
+ return clk_get_rate(c->parent);
+}
+
+static int ext_set_parent(struct clk *c, struct clk *p)
+{
+ return clk_set_parent(c->parent, p);
+}
+
+static enum handoff ext_handoff(struct clk *c)
+{
+ c->rate = clk_get_rate(c->parent);
+ /* Similar reasoning applied in div_handoff, see comment there. */
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_ext = {
+ .handoff = ext_handoff,
+ .round_rate = ext_round_rate,
+ .set_rate = ext_set_rate,
+ .get_rate = ext_get_rate,
+ .set_parent = ext_set_parent,
+};
+
+
+/* ==================== Mux_div clock ==================== */
+
+static int mux_div_clk_enable(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (md->ops->enable)
+ return md->ops->enable(md);
+ return 0;
+}
+
+static void mux_div_clk_disable(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (md->ops->disable)
+ return md->ops->disable(md);
+}
+
+static long __mux_div_round_rate(struct clk *c, unsigned long rate,
+ struct clk **best_parent, int *best_div, unsigned long *best_prate)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned int i;
+ unsigned long rrate, best = 0, _best_div = 0, _best_prate = 0;
+ struct clk *_best_parent = 0;
+
+ for (i = 0; i < md->num_parents; i++) {
+ int div;
+ unsigned long prate;
+
+ rrate = __div_round_rate(&md->data, rate, md->parents[i].src,
+ &div, &prate);
+
+ if (is_better_rate(rate, best, rrate)) {
+ best = rrate;
+ _best_div = div;
+ _best_prate = prate;
+ _best_parent = md->parents[i].src;
+ }
+
+ if (rate <= rrate && rrate <= rate + md->data.rate_margin)
+ break;
+ }
+
+ if (best_div)
+ *best_div = _best_div;
+ if (best_prate)
+ *best_prate = _best_prate;
+ if (best_parent)
+ *best_parent = _best_parent;
+
+ if (best)
+ return best;
+ return -EINVAL;
+}
+
+static long mux_div_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ return __mux_div_round_rate(c, rate, NULL, NULL, NULL);
+}
+
+/* requires enable lock to be held */
+static int __set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div)
+{
+ u32 rc = 0, src_sel;
+
+ src_sel = parent_to_src_sel(md->parents, md->num_parents, parent);
+ /*
+ * If the clock is disabled, don't change to the new settings until
+ * the clock is reenabled
+ */
+ if (md->c.count)
+ rc = md->ops->set_src_div(md, src_sel, div);
+ if (!rc) {
+ md->data.div = div;
+ md->src_sel = src_sel;
+ }
+
+ return rc;
+}
+
+static int set_src_div(struct mux_div_clk *md, struct clk *parent, u32 div)
+{
+ unsigned long flags;
+ u32 rc;
+
+ spin_lock_irqsave(&md->c.lock, flags);
+ rc = __set_src_div(md, parent, div);
+ spin_unlock_irqrestore(&md->c.lock, flags);
+
+ return rc;
+}
+
+/* Must be called after handoff to ensure parent clock rates are initialized */
+static int safe_parent_init_once(struct clk *c)
+{
+ unsigned long rrate;
+ u32 best_div;
+ struct clk *best_parent;
+ struct mux_div_clk *md = to_mux_div_clk(c);
+
+ if (IS_ERR(md->safe_parent))
+ return -EINVAL;
+ if (!md->safe_freq || md->safe_parent)
+ return 0;
+
+ rrate = __mux_div_round_rate(c, md->safe_freq, &best_parent,
+ &best_div, NULL);
+
+ if (rrate == md->safe_freq) {
+ md->safe_div = best_div;
+ md->safe_parent = best_parent;
+ } else {
+ md->safe_parent = ERR_PTR(-EINVAL);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int mux_div_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned long flags, rrate;
+ unsigned long new_prate, old_prate;
+ struct clk *old_parent, *new_parent;
+ u32 new_div, old_div;
+ int rc;
+
+ rc = safe_parent_init_once(c);
+ if (rc)
+ return rc;
+
+ rrate = __mux_div_round_rate(c, rate, &new_parent, &new_div,
+ &new_prate);
+ if (rrate != rate)
+ return -EINVAL;
+
+ old_parent = c->parent;
+ old_div = md->data.div;
+ old_prate = clk_get_rate(c->parent);
+
+ /* Refer to the description of safe_freq in clock-generic.h */
+ if (md->safe_freq)
+ rc = set_src_div(md, md->safe_parent, md->safe_div);
+
+ else if (new_parent == old_parent && new_div >= old_div) {
+ /*
+ * If both the parent_rate and divider changes, there may be an
+ * intermediate frequency generated. Ensure this intermediate
+ * frequency is less than both the new rate and previous rate.
+ */
+ rc = set_src_div(md, old_parent, new_div);
+ }
+ if (rc)
+ return rc;
+
+ rc = clk_set_rate(new_parent, new_prate);
+ if (rc) {
+ pr_err("failed to set %s to %ld\n",
+ new_parent->dbg_name, new_prate);
+ goto err_set_rate;
+ }
+
+ rc = __clk_pre_reparent(c, new_parent, &flags);
+ if (rc)
+ goto err_pre_reparent;
+
+ /* Set divider and mux src atomically */
+ rc = __set_src_div(md, new_parent, new_div);
+ if (rc)
+ goto err_set_src_div;
+
+ c->parent = new_parent;
+
+ __clk_post_reparent(c, old_parent, &flags);
+ return 0;
+
+err_set_src_div:
+ /* Not switching to new_parent, so disable it */
+ __clk_post_reparent(c, new_parent, &flags);
+err_pre_reparent:
+ rc = clk_set_rate(old_parent, old_prate);
+ WARN(rc, "%s: error changing parent (%s) rate to %ld\n",
+ c->dbg_name, old_parent->dbg_name, old_prate);
+err_set_rate:
+ rc = set_src_div(md, old_parent, old_div);
+ WARN(rc, "%s: error changing back to original div (%d) and parent (%s)\n",
+ c->dbg_name, old_div, old_parent->dbg_name);
+
+ return rc;
+}
+
+static struct clk *mux_div_clk_get_parent(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ u32 i, div, src_sel;
+
+ md->ops->get_src_div(md, &src_sel, &div);
+
+ md->data.div = div;
+ md->src_sel = src_sel;
+
+ for (i = 0; i < md->num_parents; i++) {
+ if (md->parents[i].sel == src_sel)
+ return md->parents[i].src;
+ }
+
+ return NULL;
+}
+
+static enum handoff mux_div_clk_handoff(struct clk *c)
+{
+ struct mux_div_clk *md = to_mux_div_clk(c);
+ unsigned long parent_rate;
+
+ parent_rate = clk_get_rate(c->parent);
+ c->rate = parent_rate / md->data.div;
+
+ if (!md->ops->is_enabled)
+ return HANDOFF_DISABLED_CLK;
+ if (md->ops->is_enabled(md))
+ return HANDOFF_ENABLED_CLK;
+ return HANDOFF_DISABLED_CLK;
+}
+
+struct clk_ops clk_ops_mux_div_clk = {
+ .enable = mux_div_clk_enable,
+ .disable = mux_div_clk_disable,
+ .set_rate = mux_div_clk_set_rate,
+ .round_rate = mux_div_clk_round_rate,
+ .get_parent = mux_div_clk_get_parent,
+ .handoff = mux_div_clk_handoff,
+};
diff --git a/drivers/clk/qcom/clock.c b/drivers/clk/qcom/clock.c
new file mode 100644
index 000000000000..e69f0cbdd659
--- /dev/null
+++ b/drivers/clk/qcom/clock.c
@@ -0,0 +1,905 @@
+/* arch/arm/mach-msm/clock.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2007-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/list.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <trace/events/power.h>
+#include "clock.h"
+
+struct handoff_clk {
+ struct list_head list;
+ struct clk *clk;
+};
+static LIST_HEAD(handoff_list);
+
+struct handoff_vdd {
+ struct list_head list;
+ struct clk_vdd_class *vdd_class;
+};
+static LIST_HEAD(handoff_vdd_list);
+
+static DEFINE_MUTEX(msm_clock_init_lock);
+
+/* Find the voltage level required for a given rate. */
+int find_vdd_level(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ for (level = 0; level < clk->num_fmax; level++)
+ if (rate <= clk->fmax[level])
+ break;
+
+ if (level == clk->num_fmax) {
+ pr_err("Rate %lu for %s is greater than highest Fmax\n", rate,
+ clk->dbg_name);
+ return -EINVAL;
+ }
+
+ return level;
+}
+
+/* Update voltage level given the current votes. */
+static int update_vdd(struct clk_vdd_class *vdd_class)
+{
+ int level, rc = 0, i, ignore;
+ struct regulator **r = vdd_class->regulator;
+ int *uv = vdd_class->vdd_uv;
+ int *ua = vdd_class->vdd_ua;
+ int n_reg = vdd_class->num_regulators;
+ int cur_lvl = vdd_class->cur_level;
+ int max_lvl = vdd_class->num_levels - 1;
+ int cur_base = cur_lvl * n_reg;
+ int new_base;
+
+ /* aggregate votes */
+ for (level = max_lvl; level > 0; level--)
+ if (vdd_class->level_votes[level])
+ break;
+
+ if (level == cur_lvl)
+ return 0;
+
+ max_lvl = max_lvl * n_reg;
+ new_base = level * n_reg;
+ for (i = 0; i < vdd_class->num_regulators; i++) {
+ rc = regulator_set_voltage(r[i], uv[new_base + i],
+ uv[max_lvl + i]);
+ if (rc)
+ goto set_voltage_fail;
+
+ if (ua) {
+ rc = regulator_set_optimum_mode(r[i], ua[new_base + i]);
+ rc = rc > 0 ? 0 : rc;
+ if (rc)
+ goto set_mode_fail;
+ }
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ rc = regulator_enable(r[i]);
+ else if (level == 0)
+ rc = regulator_disable(r[i]);
+ if (rc)
+ goto enable_disable_fail;
+ }
+ if (vdd_class->set_vdd && !vdd_class->num_regulators)
+ rc = vdd_class->set_vdd(vdd_class, level);
+
+ if (!rc)
+ vdd_class->cur_level = level;
+
+ return rc;
+
+enable_disable_fail:
+ /*
+ * set_optimum_mode could use voltage to derive mode. Restore
+ * previous voltage setting for r[i] first.
+ */
+ if (ua) {
+ regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]);
+ regulator_set_optimum_mode(r[i], ua[cur_base + i]);
+ }
+
+set_mode_fail:
+ regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]);
+
+set_voltage_fail:
+ for (i--; i >= 0; i--) {
+ regulator_set_voltage(r[i], uv[cur_base + i], uv[max_lvl + i]);
+ if (ua)
+ regulator_set_optimum_mode(r[i], ua[cur_base + i]);
+ if (cur_lvl == 0 || cur_lvl == vdd_class->num_levels)
+ regulator_disable(r[i]);
+ else if (level == 0)
+ ignore = regulator_enable(r[i]);
+ }
+ return rc;
+}
+
+/* Vote for a voltage level. */
+int vote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+ vdd_class->level_votes[level]++;
+ rc = update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]--;
+ mutex_unlock(&vdd_class->lock);
+
+ return rc;
+}
+
+/* Remove vote for a voltage level. */
+int unvote_vdd_level(struct clk_vdd_class *vdd_class, int level)
+{
+ int rc = 0;
+
+ if (level >= vdd_class->num_levels)
+ return -EINVAL;
+
+ mutex_lock(&vdd_class->lock);
+ if (WARN(!vdd_class->level_votes[level],
+ "Reference counts are incorrect for %s level %d\n",
+ vdd_class->class_name, level))
+ goto out;
+ vdd_class->level_votes[level]--;
+ rc = update_vdd(vdd_class);
+ if (rc)
+ vdd_class->level_votes[level]++;
+out:
+ mutex_unlock(&vdd_class->lock);
+ return rc;
+}
+
+/* Vote for a voltage level corresponding to a clock's rate. */
+static int vote_rate_vdd(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return 0;
+
+ level = find_vdd_level(clk, rate);
+ if (level < 0)
+ return level;
+
+ return vote_vdd_level(clk->vdd_class, level);
+}
+
+/* Remove vote for a voltage level corresponding to a clock's rate. */
+static void unvote_rate_vdd(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return;
+
+ level = find_vdd_level(clk, rate);
+ if (level < 0)
+ return;
+
+ unvote_vdd_level(clk->vdd_class, level);
+}
+
+/* Check if the rate is within the voltage limits of the clock. */
+static bool is_rate_valid(struct clk *clk, unsigned long rate)
+{
+ int level;
+
+ if (!clk->vdd_class)
+ return true;
+
+ level = find_vdd_level(clk, rate);
+ return level >= 0;
+}
+
+/**
+ * __clk_pre_reparent() - Set up the new parent before switching to it and
+ * prevent the enable state of the child clock from changing.
+ * @c: The child clock that's going to switch parents
+ * @new: The new parent that the child clock is going to switch to
+ * @flags: Pointer to scratch space to save spinlock flags
+ *
+ * Cannot be called from atomic context.
+ *
+ * Use this API to set up the @new parent clock to be able to support the
+ * current prepare and enable state of the child clock @c. Once the parent is
+ * set up, the child clock can safely switch to it.
+ *
+ * The caller shall grab the prepare_lock of clock @c before calling this API
+ * and only release it after calling __clk_post_reparent() for clock @c (or
+ * if this API fails). This is necessary to prevent the prepare state of the
+ * child clock @c from changing while the reparenting is in progress. Since
+ * this API takes care of grabbing the enable lock of @c, only atomic
+ * operation are allowed between calls to __clk_pre_reparent and
+ * __clk_post_reparent()
+ *
+ * The scratch space pointed to by @flags should not be altered before
+ * calling __clk_post_reparent() for clock @c.
+ *
+ * See also: __clk_post_reparent()
+ */
+int __clk_pre_reparent(struct clk *c, struct clk *new, unsigned long *flags)
+{
+ int rc;
+
+ if (c->prepare_count) {
+ rc = clk_prepare(new);
+ if (rc)
+ return rc;
+ }
+
+ spin_lock_irqsave(&c->lock, *flags);
+ if (c->count) {
+ rc = clk_enable(new);
+ if (rc) {
+ spin_unlock_irqrestore(&c->lock, *flags);
+ clk_unprepare(new);
+ return rc;
+ }
+ }
+ return 0;
+}
+
+/**
+ * __clk_post_reparent() - Release requirements on old parent after switching
+ * away from it and allow changes to the child clock's enable state.
+ * @c: The child clock that switched parents
+ * @old: The old parent that the child clock switched away from or the new
+ * parent of a failed reparent attempt.
+ * @flags: Pointer to scratch space where spinlock flags were saved
+ *
+ * Cannot be called from atomic context.
+ *
+ * This API works in tandem with __clk_pre_reparent. Use this API to
+ * - Remove prepare and enable requirements from the @old parent after
+ * switching away from it
+ * - Or, undo the effects of __clk_pre_reparent() after a failed attempt to
+ * change parents
+ *
+ * The caller shall release the prepare_lock of @c that was grabbed before
+ * calling __clk_pre_reparent() only after this API is called (or if
+ * __clk_pre_reparent() fails). This is necessary to prevent the prepare
+ * state of the child clock @c from changing while the reparenting is in
+ * progress. Since this API releases the enable lock of @c, the limit to
+ * atomic operations set by __clk_pre_reparent() is no longer present.
+ *
+ * The scratch space pointed to by @flags shall not be altered since the call
+ * to __clk_pre_reparent() for clock @c.
+ *
+ * See also: __clk_pre_reparent()
+ */
+void __clk_post_reparent(struct clk *c, struct clk *old, unsigned long *flags)
+{
+ if (c->count)
+ clk_disable(old);
+ spin_unlock_irqrestore(&c->lock, *flags);
+
+ if (c->prepare_count)
+ clk_unprepare(old);
+}
+
+int clk_prepare(struct clk *clk)
+{
+ int ret = 0;
+ struct clk *parent;
+
+ if (!clk)
+ return 0;
+ if (IS_ERR(clk))
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+ if (clk->prepare_count == 0) {
+ parent = clk->parent;
+
+ ret = clk_prepare(parent);
+ if (ret)
+ goto out;
+ ret = clk_prepare(clk->depends);
+ if (ret)
+ goto err_prepare_depends;
+
+ ret = vote_rate_vdd(clk, clk->rate);
+ if (ret)
+ goto err_vote_vdd;
+ if (clk->ops->prepare)
+ ret = clk->ops->prepare(clk);
+ if (ret)
+ goto err_prepare_clock;
+ }
+ clk->prepare_count++;
+out:
+ mutex_unlock(&clk->prepare_lock);
+ return ret;
+err_prepare_clock:
+ unvote_rate_vdd(clk, clk->rate);
+err_vote_vdd:
+ clk_unprepare(clk->depends);
+err_prepare_depends:
+ clk_unprepare(parent);
+ goto out;
+}
+EXPORT_SYMBOL(clk_prepare);
+
+/*
+ * Standard clock functions defined in include/linux/clk.h
+ */
+int clk_enable(struct clk *clk)
+{
+ int ret = 0;
+ unsigned long flags;
+ struct clk *parent;
+ const char *name = clk ? clk->dbg_name : NULL;
+
+ if (!clk)
+ return 0;
+ if (IS_ERR(clk))
+ return -EINVAL;
+
+ spin_lock_irqsave(&clk->lock, flags);
+ WARN(!clk->prepare_count,
+ "%s: Don't call enable on unprepared clocks\n", name);
+ if (clk->count == 0) {
+ parent = clk->parent;
+
+ ret = clk_enable(parent);
+ if (ret)
+ goto err_enable_parent;
+ ret = clk_enable(clk->depends);
+ if (ret)
+ goto err_enable_depends;
+
+ trace_clock_enable(name, 1, smp_processor_id());
+ if (clk->ops->enable)
+ ret = clk->ops->enable(clk);
+ if (ret)
+ goto err_enable_clock;
+ }
+ clk->count++;
+ spin_unlock_irqrestore(&clk->lock, flags);
+
+ return 0;
+
+err_enable_clock:
+ clk_disable(clk->depends);
+err_enable_depends:
+ clk_disable(parent);
+err_enable_parent:
+ spin_unlock_irqrestore(&clk->lock, flags);
+ return ret;
+}
+EXPORT_SYMBOL(clk_enable);
+
+void clk_disable(struct clk *clk)
+{
+ const char *name = clk ? clk->dbg_name : NULL;
+ unsigned long flags;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ spin_lock_irqsave(&clk->lock, flags);
+ WARN(!clk->prepare_count,
+ "%s: Never called prepare or calling disable after unprepare\n",
+ name);
+ if (WARN(clk->count == 0, "%s is unbalanced", name))
+ goto out;
+ if (clk->count == 1) {
+ struct clk *parent = clk->parent;
+
+ trace_clock_disable(name, 0, smp_processor_id());
+ if (clk->ops->disable)
+ clk->ops->disable(clk);
+ clk_disable(clk->depends);
+ clk_disable(parent);
+ }
+ clk->count--;
+out:
+ spin_unlock_irqrestore(&clk->lock, flags);
+}
+EXPORT_SYMBOL(clk_disable);
+
+void clk_unprepare(struct clk *clk)
+{
+ const char *name = clk ? clk->dbg_name : NULL;
+
+ if (IS_ERR_OR_NULL(clk))
+ return;
+
+ mutex_lock(&clk->prepare_lock);
+ if (WARN(!clk->prepare_count, "%s is unbalanced (prepare)", name))
+ goto out;
+ if (clk->prepare_count == 1) {
+ struct clk *parent = clk->parent;
+
+ WARN(clk->count,
+ "%s: Don't call unprepare when the clock is enabled\n",
+ name);
+
+ if (clk->ops->unprepare)
+ clk->ops->unprepare(clk);
+ unvote_rate_vdd(clk, clk->rate);
+ clk_unprepare(clk->depends);
+ clk_unprepare(parent);
+ }
+ clk->prepare_count--;
+out:
+ mutex_unlock(&clk->prepare_lock);
+}
+EXPORT_SYMBOL(clk_unprepare);
+
+int clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->reset)
+ return -ENOSYS;
+
+ return clk->ops->reset(clk, action);
+}
+EXPORT_SYMBOL(clk_reset);
+
+unsigned long clk_get_rate(struct clk *clk)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return 0;
+
+ if (!clk->ops->get_rate)
+ return clk->rate;
+
+ return clk->ops->get_rate(clk);
+}
+EXPORT_SYMBOL(clk_get_rate);
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned long start_rate;
+ int rc = 0;
+ const char *name = clk ? clk->dbg_name : NULL;
+
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->set_rate)
+ return -ENOSYS;
+
+ if (!is_rate_valid(clk, rate))
+ return -EINVAL;
+
+ mutex_lock(&clk->prepare_lock);
+
+ /* Return early if the rate isn't going to change */
+ if (clk->rate == rate && !(clk->flags & CLKFLAG_NO_RATE_CACHE))
+ goto out;
+
+ trace_clock_set_rate(name, rate, raw_smp_processor_id());
+
+ start_rate = clk->rate;
+
+ if (clk->ops->pre_set_rate)
+ rc = clk->ops->pre_set_rate(clk, rate);
+ if (rc)
+ goto out;
+
+ /* Enforce vdd requirements for target frequency. */
+ if (clk->prepare_count) {
+ rc = vote_rate_vdd(clk, rate);
+ if (rc)
+ goto err_vote_vdd;
+ }
+
+ rc = clk->ops->set_rate(clk, rate);
+ if (rc)
+ goto err_set_rate;
+ clk->rate = rate;
+
+ /* Release vdd requirements for starting frequency. */
+ if (clk->prepare_count)
+ unvote_rate_vdd(clk, start_rate);
+
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, start_rate);
+
+out:
+ mutex_unlock(&clk->prepare_lock);
+ return rc;
+
+err_set_rate:
+ if (clk->prepare_count)
+ unvote_rate_vdd(clk, rate);
+err_vote_vdd:
+ /* clk->rate is still the old rate. So, pass the new rate instead. */
+ if (clk->ops->post_set_rate)
+ clk->ops->post_set_rate(clk, rate);
+ goto out;
+}
+EXPORT_SYMBOL(clk_set_rate);
+
+long clk_round_rate(struct clk *clk, unsigned long rate)
+{
+ long rrate;
+ unsigned long fmax = 0, i;
+
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->round_rate)
+ return -ENOSYS;
+
+ for (i = 0; i < clk->num_fmax; i++)
+ fmax = max(fmax, clk->fmax[i]);
+
+ if (!fmax)
+ fmax = ULONG_MAX;
+
+ rate = min(rate, fmax);
+ rrate = clk->ops->round_rate(clk, rate);
+ if (rrate > fmax)
+ return -EINVAL;
+ return rrate;
+}
+EXPORT_SYMBOL(clk_round_rate);
+
+int clk_set_max_rate(struct clk *clk, unsigned long rate)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+
+ if (!clk->ops->set_max_rate)
+ return -ENOSYS;
+
+ return clk->ops->set_max_rate(clk, rate);
+}
+EXPORT_SYMBOL(clk_set_max_rate);
+
+int clk_set_parent(struct clk *clk, struct clk *parent)
+{
+ int rc = 0;
+
+ if (!clk->ops->set_parent && clk->parent == parent)
+ return 0;
+
+ if (!clk->ops->set_parent)
+ return -ENOSYS;
+
+ mutex_lock(&clk->prepare_lock);
+ if (clk->parent == parent && !(clk->flags & CLKFLAG_NO_RATE_CACHE))
+ goto out;
+ rc = clk->ops->set_parent(clk, parent);
+out:
+ mutex_unlock(&clk->prepare_lock);
+
+ return rc;
+}
+EXPORT_SYMBOL(clk_set_parent);
+
+struct clk *clk_get_parent(struct clk *clk)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return NULL;
+
+ return clk->parent;
+}
+EXPORT_SYMBOL(clk_get_parent);
+
+int clk_set_flags(struct clk *clk, unsigned long flags)
+{
+ if (IS_ERR_OR_NULL(clk))
+ return -EINVAL;
+ if (!clk->ops->set_flags)
+ return -ENOSYS;
+
+ return clk->ops->set_flags(clk, flags);
+}
+EXPORT_SYMBOL(clk_set_flags);
+
+static LIST_HEAD(initdata_list);
+
+static void init_sibling_lists(struct clk_lookup *clock_tbl, size_t num_clocks)
+{
+ struct clk *clk, *parent;
+ unsigned n;
+
+ for (n = 0; n < num_clocks; n++) {
+ clk = clock_tbl[n].clk;
+ parent = clk->parent;
+ if (parent && list_empty(&clk->siblings))
+ list_add(&clk->siblings, &parent->children);
+ }
+}
+
+static void vdd_class_init(struct clk_vdd_class *vdd)
+{
+ struct handoff_vdd *v;
+
+ if (!vdd)
+ return;
+
+ list_for_each_entry(v, &handoff_vdd_list, list) {
+ if (v->vdd_class == vdd)
+ return;
+ }
+
+ pr_debug("voting for vdd_class %s\n", vdd->class_name);
+ if (vote_vdd_level(vdd, vdd->num_levels - 1))
+ pr_err("failed to vote for %s\n", vdd->class_name);
+
+ v = kmalloc(sizeof(*v), GFP_KERNEL);
+ if (!v) {
+ pr_err("Unable to kmalloc. %s will be stuck at max.\n",
+ vdd->class_name);
+ return;
+ }
+
+ v->vdd_class = vdd;
+ list_add_tail(&v->list, &handoff_vdd_list);
+}
+
+static int __handoff_clk(struct clk *clk)
+{
+ enum handoff state = HANDOFF_DISABLED_CLK;
+ struct handoff_clk *h = NULL;
+ int rc;
+
+ if (clk == NULL || clk->flags & CLKFLAG_INIT_DONE ||
+ clk->flags & CLKFLAG_SKIP_HANDOFF)
+ return 0;
+
+ if (clk->flags & CLKFLAG_INIT_ERR)
+ return -ENXIO;
+
+ /* Handoff any 'depends' clock first. */
+ rc = __handoff_clk(clk->depends);
+ if (rc)
+ goto err;
+
+ /*
+ * Handoff functions for the parent must be called before the
+ * children can be handed off. Without handing off the parents and
+ * knowing their rate and state (on/off), it's impossible to figure
+ * out the rate and state of the children.
+ */
+ if (clk->ops->get_parent)
+ clk->parent = clk->ops->get_parent(clk);
+
+ if (IS_ERR(clk->parent)) {
+ rc = PTR_ERR(clk->parent);
+ goto err;
+ }
+
+ rc = __handoff_clk(clk->parent);
+ if (rc)
+ goto err;
+
+ if (clk->ops->handoff)
+ state = clk->ops->handoff(clk);
+
+ if (state == HANDOFF_ENABLED_CLK) {
+
+ h = kmalloc(sizeof(*h), GFP_KERNEL);
+ if (!h) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = clk_prepare_enable(clk->parent);
+ if (rc)
+ goto err;
+
+ rc = clk_prepare_enable(clk->depends);
+ if (rc)
+ goto err_depends;
+
+ rc = vote_rate_vdd(clk, clk->rate);
+ WARN(rc, "%s unable to vote for voltage!\n", clk->dbg_name);
+
+ clk->count = 1;
+ clk->prepare_count = 1;
+ h->clk = clk;
+ list_add_tail(&h->list, &handoff_list);
+
+ pr_debug("Handed off %s rate=%lu\n", clk->dbg_name, clk->rate);
+ }
+
+ clk->flags |= CLKFLAG_INIT_DONE;
+
+ return 0;
+
+err_depends:
+ clk_disable_unprepare(clk->parent);
+err:
+ kfree(h);
+ clk->flags |= CLKFLAG_INIT_ERR;
+ pr_err("%s handoff failed (%d)\n", clk->dbg_name, rc);
+ return rc;
+}
+
+/**
+ * msm_clock_register() - Register additional clock tables
+ * @table: Table of clocks
+ * @size: Size of @table
+ *
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
+ */
+int msm_clock_register(struct clk_lookup *table, size_t size)
+{
+ int n = 0;
+
+ mutex_lock(&msm_clock_init_lock);
+
+ init_sibling_lists(table, size);
+
+ /*
+ * Enable regulators and temporarily set them up at maximum voltage.
+ * Once all the clocks have made their respective vote, remove this
+ * temporary vote. The removing of the temporary vote is done at
+ * late_init, by which time we assume all the clocks would have been
+ * handed off.
+ */
+ for (n = 0; n < size; n++)
+ vdd_class_init(table[n].clk->vdd_class);
+
+ /*
+ * Detect and preserve initial clock state until clock_late_init() or
+ * a driver explicitly changes it, whichever is first.
+ */
+ for (n = 0; n < size; n++)
+ __handoff_clk(table[n].clk);
+
+ clkdev_add_table(table, size);
+
+ clock_debug_register(table, size);
+
+ mutex_unlock(&msm_clock_init_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(msm_clock_register);
+
+struct of_msm_provider_data {
+ struct clk_lookup *table;
+ size_t size;
+};
+
+static struct clk *of_clk_src_get(struct of_phandle_args *clkspec,
+ void *data)
+{
+ struct of_msm_provider_data *ofdata = data;
+ int n;
+
+ for (n = 0; n < ofdata->size; n++) {
+ if (clkspec->args[0] == ofdata->table[n].of_idx)
+ return ofdata->table[n].clk;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
+/**
+ * of_msm_clock_register() - Register clock tables with clkdev and with the
+ * clock DT framework
+ * @table: Table of clocks
+ * @size: Size of @table
+ * @np: Device pointer corresponding to the clock-provider device
+ *
+ * Upon return, clock APIs may be used to control clocks registered using this
+ * function.
+ */
+int of_msm_clock_register(struct device_node *np, struct clk_lookup *table,
+ size_t size)
+{
+ int ret = 0;
+ struct of_msm_provider_data *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->table = table;
+ data->size = size;
+
+ ret = of_clk_add_provider(np, of_clk_src_get, data);
+ if (ret) {
+ kfree(data);
+ return -ENOMEM;
+ }
+
+ return msm_clock_register(table, size);
+}
+EXPORT_SYMBOL(of_msm_clock_register);
+
+/**
+ * msm_clock_init() - Register and initialize a clock driver
+ * @data: Driver-specific clock initialization data
+ *
+ * Upon return from this call, clock APIs may be used to control
+ * clocks registered with this API.
+ */
+int __init msm_clock_init(struct clock_init_data *data)
+{
+ if (!data)
+ return -EINVAL;
+
+ if (data->pre_init)
+ data->pre_init();
+
+ mutex_lock(&msm_clock_init_lock);
+ if (data->late_init)
+ list_add(&data->list, &initdata_list);
+ mutex_unlock(&msm_clock_init_lock);
+
+ msm_clock_register(data->table, data->size);
+
+ if (data->post_init)
+ data->post_init();
+
+ return 0;
+}
+
+static int __init clock_late_init(void)
+{
+ struct handoff_clk *h, *h_temp;
+ struct handoff_vdd *v, *v_temp;
+ struct clock_init_data *initdata, *initdata_temp;
+ int ret = 0;
+
+ pr_info("%s: Removing enables held for handed-off clocks\n", __func__);
+
+ mutex_lock(&msm_clock_init_lock);
+
+ list_for_each_entry_safe(initdata, initdata_temp,
+ &initdata_list, list) {
+ ret = initdata->late_init();
+ if (ret)
+ pr_err("%s: %pS failed late_init.\n", __func__,
+ initdata);
+ }
+
+ list_for_each_entry_safe(h, h_temp, &handoff_list, list) {
+ clk_disable_unprepare(h->clk);
+ list_del(&h->list);
+ kfree(h);
+ }
+
+ list_for_each_entry_safe(v, v_temp, &handoff_vdd_list, list) {
+ unvote_vdd_level(v->vdd_class, v->vdd_class->num_levels - 1);
+ list_del(&v->list);
+ kfree(v);
+ }
+
+ mutex_unlock(&msm_clock_init_lock);
+
+ return ret;
+}
+/* clock_late_init should run only after all deferred probing
+ * (excluding DLKM probes) has completed.
+ */
+late_initcall_sync(clock_late_init);
diff --git a/drivers/clk/msm/clock.h b/drivers/clk/qcom/clock.h
index b0152d8c1fe1..b0152d8c1fe1 100644
--- a/drivers/clk/msm/clock.h
+++ b/drivers/clk/qcom/clock.h
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index e09ecdd49a03..56684aed5701 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -415,6 +415,7 @@ u64 arch_counter_get_cntpct(void)
else
return arch_counter_get_cntpct_mem();
}
+EXPORT_SYMBOL(arch_counter_get_cntpct);
u64 arch_counter_get_cntvct(void)
{
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/coresight/coresight-tmc.c
index e85e4220ebaf..265e16ad8a89 100644
--- a/drivers/coresight/coresight-tmc.c
+++ b/drivers/coresight/coresight-tmc.c
@@ -1295,16 +1295,26 @@ static ssize_t tmc_etr_store_byte_cntr_value(struct device *dev,
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val;
- if (!drvdata->byte_cntr_present || drvdata->byte_cntr_enable)
+ mutex_lock(&drvdata->byte_cntr_lock);
+ if (!drvdata->byte_cntr_present || drvdata->byte_cntr_enable) {
+ mutex_unlock(&drvdata->byte_cntr_lock);
return -EPERM;
- if (sscanf(buf, "%lx", &val) != 1)
+ }
+ if (sscanf(buf, "%lx", &val) != 1) {
+ mutex_unlock(&drvdata->byte_cntr_lock);
return -EINVAL;
- if ((drvdata->size / 8) < val)
+ }
+ if ((drvdata->size / 8) < val) {
+ mutex_unlock(&drvdata->byte_cntr_lock);
return -EINVAL;
- if (val && drvdata->size % (val * 8) != 0)
+ }
+ if (val && drvdata->size % (val * 8) != 0) {
+ mutex_unlock(&drvdata->byte_cntr_lock);
return -EINVAL;
+ }
drvdata->byte_cntr_value = val;
+ mutex_unlock(&drvdata->byte_cntr_lock);
return size;
}
static DEVICE_ATTR(byte_cntr_value, S_IRUGO | S_IWUSR,
diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index 9dd53eb968d7..afe80b927fbd 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -17,15 +17,11 @@ config CPU_FREQ
if CPU_FREQ
-config CPU_FREQ_TABLE
- tristate
-
config CPU_FREQ_GOV_COMMON
bool
config CPU_FREQ_STAT
tristate "CPU frequency translation statistics"
- select CPU_FREQ_TABLE
default y
help
This driver exports CPU frequency statistics information through sysfs
@@ -153,7 +149,6 @@ config CPU_FREQ_GOV_USERSPACE
config CPU_FREQ_GOV_ONDEMAND
tristate "'ondemand' cpufreq policy governor"
- select CPU_FREQ_TABLE
select CPU_FREQ_GOV_COMMON
help
'ondemand' - This driver adds a dynamic cpufreq policy governor.
@@ -225,7 +220,6 @@ config CPU_BOOST
config GENERIC_CPUFREQ_CPU0
tristate "Generic CPU0 cpufreq driver"
depends on HAVE_CLK && REGULATOR && PM_OPP && OF
- select CPU_FREQ_TABLE
help
This adds a generic cpufreq driver for CPU0 frequency management.
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
@@ -261,7 +255,6 @@ depends on IA64
config IA64_ACPI_CPUFREQ
tristate "ACPI Processor P-States driver"
- select CPU_FREQ_TABLE
depends on ACPI_PROCESSOR
help
This driver adds a CPUFreq driver which utilizes the ACPI
@@ -278,7 +271,6 @@ depends on MIPS
config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
- select CPU_FREQ_TABLE
help
This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency.
@@ -300,7 +292,6 @@ menu "SPARC CPU frequency scaling drivers"
depends on SPARC64
config SPARC_US3_CPUFREQ
tristate "UltraSPARC-III CPU Frequency driver"
- select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for UltraSPARC-III processors.
@@ -310,7 +301,6 @@ config SPARC_US3_CPUFREQ
config SPARC_US2E_CPUFREQ
tristate "UltraSPARC-IIe CPU Frequency driver"
- select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for UltraSPARC-IIe processors.
@@ -323,7 +313,6 @@ menu "SH CPU Frequency scaling"
depends on SUPERH
config SH_CPU_FREQ
tristate "SuperH CPU Frequency driver"
- select CPU_FREQ_TABLE
help
This adds the cpufreq driver for SuperH. Any CPU that supports
clock rate rounding through the clock framework can use this
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index 6e57543fe0b9..a6dd7be49991 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -55,7 +55,6 @@ config ARM_EXYNOS5440_CPUFREQ
config ARM_HIGHBANK_CPUFREQ
tristate "Calxeda Highbank-based"
depends on ARCH_HIGHBANK
- select CPU_FREQ_TABLE
select GENERIC_CPUFREQ_CPU0
select PM_OPP
select REGULATOR
@@ -94,7 +93,6 @@ config ARM_OMAP2PLUS_CPUFREQ
bool "TI OMAP2+"
depends on ARCH_OMAP2PLUS
default ARCH_OMAP2PLUS
- select CPU_FREQ_TABLE
config ARM_S3C2416_CPUFREQ
bool "S3C2416 CPU Frequency scaling support"
@@ -130,7 +128,6 @@ config ARM_S3C64XX_CPUFREQ
config ARM_S5PV210_CPUFREQ
bool "Samsung S5PV210 and S5PC110"
depends on CPU_S5PV210
- select CPU_FREQ_TABLE
default y
help
This adds the CPUFreq driver for Samsung S5PV210 and
diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc
index 9c926ca0d718..43221bf5fe12 100644
--- a/drivers/cpufreq/Kconfig.powerpc
+++ b/drivers/cpufreq/Kconfig.powerpc
@@ -19,7 +19,6 @@ config CPU_FREQ_CBE_PMI
config CPU_FREQ_MAPLE
bool "Support for Maple 970FX Evaluation Board"
depends on PPC_MAPLE
- select CPU_FREQ_TABLE
help
This adds support for frequency switching on Maple 970FX
Evaluation Board and compatible boards (IBM JS2x blades).
diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86
index 6bd63d63d356..6897ad85b046 100644
--- a/drivers/cpufreq/Kconfig.x86
+++ b/drivers/cpufreq/Kconfig.x86
@@ -31,7 +31,6 @@ config X86_PCC_CPUFREQ
config X86_ACPI_CPUFREQ
tristate "ACPI Processor P-States driver"
- select CPU_FREQ_TABLE
depends on ACPI_PROCESSOR
help
This driver adds a CPUFreq driver which utilizes the ACPI
@@ -60,7 +59,6 @@ config X86_ACPI_CPUFREQ_CPB
config ELAN_CPUFREQ
tristate "AMD Elan SC400 and SC410"
- select CPU_FREQ_TABLE
depends on MELAN
---help---
This adds the CPUFreq driver for AMD Elan SC400 and SC410
@@ -76,7 +74,6 @@ config ELAN_CPUFREQ
config SC520_CPUFREQ
tristate "AMD Elan SC520"
- select CPU_FREQ_TABLE
depends on MELAN
---help---
This adds the CPUFreq driver for AMD Elan SC520 processor.
@@ -88,7 +85,6 @@ config SC520_CPUFREQ
config X86_POWERNOW_K6
tristate "AMD Mobile K6-2/K6-3 PowerNow!"
- select CPU_FREQ_TABLE
depends on X86_32
help
This adds the CPUFreq driver for mobile AMD K6-2+ and mobile
@@ -100,7 +96,6 @@ config X86_POWERNOW_K6
config X86_POWERNOW_K7
tristate "AMD Mobile Athlon/Duron PowerNow!"
- select CPU_FREQ_TABLE
depends on X86_32
help
This adds the CPUFreq driver for mobile AMD K7 mobile processors.
@@ -118,7 +113,6 @@ config X86_POWERNOW_K7_ACPI
config X86_POWERNOW_K8
tristate "AMD Opteron/Athlon64 PowerNow!"
- select CPU_FREQ_TABLE
depends on ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ
help
This adds the CPUFreq driver for K8/early Opteron/Athlon64 processors.
@@ -159,7 +153,6 @@ config X86_GX_SUSPMOD
config X86_SPEEDSTEP_CENTRINO
tristate "Intel Enhanced SpeedStep (deprecated)"
- select CPU_FREQ_TABLE
select X86_SPEEDSTEP_CENTRINO_TABLE if X86_32
depends on X86_32 || (X86_64 && ACPI_PROCESSOR)
help
@@ -189,7 +182,6 @@ config X86_SPEEDSTEP_CENTRINO_TABLE
config X86_SPEEDSTEP_ICH
tristate "Intel Speedstep on ICH-M chipsets (ioport interface)"
- select CPU_FREQ_TABLE
depends on X86_32
help
This adds the CPUFreq driver for certain mobile Intel Pentium III
@@ -203,7 +195,6 @@ config X86_SPEEDSTEP_ICH
config X86_SPEEDSTEP_SMI
tristate "Intel SpeedStep on 440BX/ZX/MX chipsets (SMI interface)"
- select CPU_FREQ_TABLE
depends on X86_32
help
This adds the CPUFreq driver for certain mobile Intel Pentium III
@@ -216,7 +207,6 @@ config X86_SPEEDSTEP_SMI
config X86_P4_CLOCKMOD
tristate "Intel Pentium 4 clock modulation"
- select CPU_FREQ_TABLE
help
This adds the CPUFreq driver for Intel Pentium 4 / XEON
processors. When enabled it will lower CPU temperature by skipping
@@ -258,7 +248,6 @@ config X86_LONGRUN
config X86_LONGHAUL
tristate "VIA Cyrix III Longhaul"
- select CPU_FREQ_TABLE
depends on X86_32 && ACPI_PROCESSOR
help
This adds the CPUFreq driver for VIA Samuel/CyrixIII,
@@ -271,7 +260,6 @@ config X86_LONGHAUL
config X86_E_POWERSAVER
tristate "VIA C7 Enhanced PowerSaver (DANGEROUS)"
- select CPU_FREQ_TABLE
depends on X86_32 && ACPI_PROCESSOR
help
This adds the CPUFreq driver for VIA C7 processors. However, this driver
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index e2603ddd1e7b..2ed1e50615c9 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -1,5 +1,5 @@
# CPUfreq core
-obj-$(CONFIG_CPU_FREQ) += cpufreq.o
+obj-$(CONFIG_CPU_FREQ) += cpufreq.o freq_table.o
# CPUfreq stats
obj-$(CONFIG_CPU_FREQ_STAT) += cpufreq_stats.o
@@ -13,9 +13,6 @@ obj-$(CONFIG_CPU_FREQ_GOV_INTERACTIVE) += cpufreq_interactive.o
obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
obj-$(CONFIG_CPU_BOOST) += cpu-boost.o
-# CPUfreq cross-arch helpers
-obj-$(CONFIG_CPU_FREQ_TABLE) += freq_table.o
-
obj-$(CONFIG_GENERIC_CPUFREQ_CPU0) += cpufreq-cpu0.o
##################################################################################
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index edc089e9d0c4..d26f04e6601e 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -70,6 +70,7 @@ struct acpi_cpufreq_data {
struct cpufreq_frequency_table *freq_table;
unsigned int resume;
unsigned int cpu_feature;
+ cpumask_var_t freqdomain_cpus;
};
static DEFINE_PER_CPU(struct acpi_cpufreq_data *, acfreq_data);
@@ -176,6 +177,15 @@ static struct global_attr global_boost = __ATTR(boost, 0644,
show_global_boost,
store_global_boost);
+static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf)
+{
+ struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu);
+
+ return cpufreq_show_cpus(data->freqdomain_cpus, buf);
+}
+
+cpufreq_freq_attr_ro(freqdomain_cpus);
+
#ifdef CONFIG_X86_ACPI_CPUFREQ_CPB
static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf,
size_t count)
@@ -232,7 +242,7 @@ static unsigned extract_msr(u32 msr, struct acpi_cpufreq_data *data)
perf = data->acpi_data;
for (i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
- if (msr == perf->states[data->freq_table[i].index].status)
+ if (msr == perf->states[data->freq_table[i].driver_data].status)
return data->freq_table[i].frequency;
}
return data->freq_table[0].frequency;
@@ -442,7 +452,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
goto out;
}
- next_perf_state = data->freq_table[next_state].index;
+ next_perf_state = data->freq_table[next_state].driver_data;
if (perf->state == next_perf_state) {
if (unlikely(data->resume)) {
pr_debug("Called after resume, resetting to P%d\n",
@@ -698,10 +708,15 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
return blacklisted;
#endif
- data = kzalloc(sizeof(struct acpi_cpufreq_data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
+ if (!zalloc_cpumask_var(&data->freqdomain_cpus, GFP_KERNEL)) {
+ result = -ENOMEM;
+ goto err_free;
+ }
+
data->acpi_data = per_cpu_ptr(acpi_perf_data, cpu);
per_cpu(acfreq_data, cpu) = data;
@@ -710,7 +725,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
result = acpi_processor_register_performance(data->acpi_data, cpu);
if (result)
- goto err_free;
+ goto err_free_mask;
perf = data->acpi_data;
policy->shared_type = perf->shared_type;
@@ -723,6 +738,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) {
cpumask_copy(policy->cpus, perf->shared_cpu_map);
}
+ cpumask_copy(data->freqdomain_cpus, perf->shared_cpu_map);
#ifdef CONFIG_SMP
dmi_check_system(sw_any_bug_dmi_table);
@@ -734,6 +750,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
if (check_amd_hwpstate_cpu(cpu) && !acpi_pstate_strict) {
cpumask_clear(policy->cpus);
cpumask_set_cpu(cpu, policy->cpus);
+ cpumask_copy(data->freqdomain_cpus, cpu_sibling_mask(cpu));
policy->shared_type = CPUFREQ_SHARED_TYPE_HW;
pr_info_once(PFX "overriding BIOS provided _PSD data\n");
}
@@ -781,7 +798,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto err_unreg;
}
- data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
+ data->freq_table = kmalloc(sizeof(*data->freq_table) *
(perf->state_count+1), GFP_KERNEL);
if (!data->freq_table) {
result = -ENOMEM;
@@ -811,7 +828,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
data->freq_table[valid_states-1].frequency / 1000)
continue;
- data->freq_table[valid_states].index = i;
+ data->freq_table[valid_states].driver_data = i;
data->freq_table[valid_states].frequency =
perf->states[i].core_frequency * 1000;
valid_states++;
@@ -868,6 +885,8 @@ err_freqfree:
kfree(data->freq_table);
err_unreg:
acpi_processor_unregister_performance(perf, cpu);
+err_free_mask:
+ free_cpumask_var(data->freqdomain_cpus);
err_free:
kfree(data);
per_cpu(acfreq_data, cpu) = NULL;
@@ -886,6 +905,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
per_cpu(acfreq_data, policy->cpu) = NULL;
acpi_processor_unregister_performance(data->acpi_data,
policy->cpu);
+ free_cpumask_var(data->freqdomain_cpus);
kfree(data->freq_table);
kfree(data);
}
@@ -906,6 +926,7 @@ static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
static struct freq_attr *acpi_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
+ &freqdomain_cpus,
NULL, /* this is a placeholder for cpb, do not remove */
NULL,
};
@@ -918,7 +939,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.exit = acpi_cpufreq_cpu_exit,
.resume = acpi_cpufreq_resume,
.name = "acpi-cpufreq",
- .owner = THIS_MODULE,
.attr = acpi_cpufreq_attr,
};
@@ -947,7 +967,7 @@ static void __init acpi_cpufreq_boost_init(void)
/* We create the boost file in any case, though for systems without
* hardware support it will be read-only and hardwired to return 0.
*/
- if (sysfs_create_file(cpufreq_global_kobject, &(global_boost.attr)))
+ if (cpufreq_sysfs_create_file(&(global_boost.attr)))
pr_warn(PFX "could not register global boost sysfs file\n");
else
pr_debug("registered global boost sysfs file\n");
@@ -955,7 +975,7 @@ static void __init acpi_cpufreq_boost_init(void)
static void __exit acpi_cpufreq_boost_exit(void)
{
- sysfs_remove_file(cpufreq_global_kobject, &(global_boost.attr));
+ cpufreq_sysfs_remove_file(&(global_boost.attr));
if (msrs) {
unregister_cpu_notifier(&boost_nb);
diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c
index 5d7f53fcd6f5..5519933813ea 100644
--- a/drivers/cpufreq/arm_big_little.c
+++ b/drivers/cpufreq/arm_big_little.c
@@ -24,112 +24,323 @@
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/of_platform.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/slab.h>
#include <linux/topology.h>
#include <linux/types.h>
+#include <asm/bL_switcher.h>
#include "arm_big_little.h"
/* Currently we support only two clusters */
+#define A15_CLUSTER 0
+#define A7_CLUSTER 1
#define MAX_CLUSTERS 2
+#ifdef CONFIG_BL_SWITCHER
+static bool bL_switching_enabled;
+#define is_bL_switching_enabled() bL_switching_enabled
+#define set_switching_enabled(x) (bL_switching_enabled = (x))
+#else
+#define is_bL_switching_enabled() false
+#define set_switching_enabled(x) do { } while (0)
+#endif
+
+#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
+#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
+
static struct cpufreq_arm_bL_ops *arm_bL_ops;
static struct clk *clk[MAX_CLUSTERS];
-static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
-static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
+static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
+static atomic_t cluster_usage[MAX_CLUSTERS + 1];
+
+static unsigned int clk_big_min; /* (Big) clock frequencies */
+static unsigned int clk_little_max; /* Maximum clock frequency (Little) */
+
+static DEFINE_PER_CPU(unsigned int, physical_cluster);
+static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq);
-static unsigned int bL_cpufreq_get(unsigned int cpu)
+static struct mutex cluster_lock[MAX_CLUSTERS];
+
+static inline int raw_cpu_to_cluster(int cpu)
{
- u32 cur_cluster = cpu_to_cluster(cpu);
+ return topology_physical_package_id(cpu);
+}
- return clk_get_rate(clk[cur_cluster]) / 1000;
+static inline int cpu_to_cluster(int cpu)
+{
+ return is_bL_switching_enabled() ?
+ MAX_CLUSTERS : raw_cpu_to_cluster(cpu);
}
-/* Validate policy frequency range */
-static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy)
+static unsigned int find_cluster_maxfreq(int cluster)
{
- u32 cur_cluster = cpu_to_cluster(policy->cpu);
+ int j;
+ u32 max_freq = 0, cpu_freq;
+
+ for_each_online_cpu(j) {
+ cpu_freq = per_cpu(cpu_last_req_freq, j);
+
+ if ((cluster == per_cpu(physical_cluster, j)) &&
+ (max_freq < cpu_freq))
+ max_freq = cpu_freq;
+ }
+
+ pr_debug("%s: cluster: %d, max freq: %d\n", __func__, cluster,
+ max_freq);
+
+ return max_freq;
+}
+
+static unsigned int clk_get_cpu_rate(unsigned int cpu)
+{
+ u32 cur_cluster = per_cpu(physical_cluster, cpu);
+ u32 rate = clk_get_rate(clk[cur_cluster]) / 1000;
+
+ /* For switcher we use virtual A7 clock rates */
+ if (is_bL_switching_enabled())
+ rate = VIRT_FREQ(cur_cluster, rate);
+
+ pr_debug("%s: cpu: %d, cluster: %d, freq: %u\n", __func__, cpu,
+ cur_cluster, rate);
+
+ return rate;
+}
+
+static unsigned int bL_cpufreq_get_rate(unsigned int cpu)
+{
+ if (is_bL_switching_enabled()) {
+ pr_debug("%s: freq: %d\n", __func__, per_cpu(cpu_last_req_freq,
+ cpu));
+
+ return per_cpu(cpu_last_req_freq, cpu);
+ } else {
+ return clk_get_cpu_rate(cpu);
+ }
+}
+
+static unsigned int
+bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate)
+{
+ u32 new_rate, prev_rate;
+ int ret;
+ bool bLs = is_bL_switching_enabled();
+
+ mutex_lock(&cluster_lock[new_cluster]);
- return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]);
+ if (bLs) {
+ prev_rate = per_cpu(cpu_last_req_freq, cpu);
+ per_cpu(cpu_last_req_freq, cpu) = rate;
+ per_cpu(physical_cluster, cpu) = new_cluster;
+
+ new_rate = find_cluster_maxfreq(new_cluster);
+ new_rate = ACTUAL_FREQ(new_cluster, new_rate);
+ } else {
+ new_rate = rate;
+ }
+
+ pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d, freq: %d\n",
+ __func__, cpu, old_cluster, new_cluster, new_rate);
+
+ ret = clk_set_rate(clk[new_cluster], new_rate * 1000);
+ if (WARN_ON(ret)) {
+ pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret,
+ new_cluster);
+ if (bLs) {
+ per_cpu(cpu_last_req_freq, cpu) = prev_rate;
+ per_cpu(physical_cluster, cpu) = old_cluster;
+ }
+
+ mutex_unlock(&cluster_lock[new_cluster]);
+
+ return ret;
+ }
+
+ mutex_unlock(&cluster_lock[new_cluster]);
+
+ /* Recalc freq for old cluster when switching clusters */
+ if (old_cluster != new_cluster) {
+ pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n",
+ __func__, cpu, old_cluster, new_cluster);
+
+ /* Switch cluster */
+ bL_switch_request(cpu, new_cluster);
+
+ mutex_lock(&cluster_lock[old_cluster]);
+
+ /* Set freq of old cluster if there are cpus left on it */
+ new_rate = find_cluster_maxfreq(old_cluster);
+ new_rate = ACTUAL_FREQ(old_cluster, new_rate);
+
+ if (new_rate) {
+ pr_debug("%s: Updating rate of old cluster: %d, to freq: %d\n",
+ __func__, old_cluster, new_rate);
+
+ if (clk_set_rate(clk[old_cluster], new_rate * 1000))
+ pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n",
+ __func__, ret, old_cluster);
+ }
+ mutex_unlock(&cluster_lock[old_cluster]);
+ }
+
+ return 0;
}
/* Set clock frequency */
static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
- unsigned int target_freq, unsigned int relation)
+ unsigned int index)
{
- struct cpufreq_freqs freqs;
- u32 cpu = policy->cpu, freq_tab_idx, cur_cluster;
- int ret = 0;
+ u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster;
+ unsigned int freqs_new;
+
+ cur_cluster = cpu_to_cluster(cpu);
+ new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
+
+ freqs_new = freq_table[cur_cluster][index].frequency;
+
+ if (is_bL_switching_enabled()) {
+ if ((actual_cluster == A15_CLUSTER) &&
+ (freqs_new < clk_big_min)) {
+ new_cluster = A7_CLUSTER;
+ } else if ((actual_cluster == A7_CLUSTER) &&
+ (freqs_new > clk_little_max)) {
+ new_cluster = A15_CLUSTER;
+ }
+ }
- cur_cluster = cpu_to_cluster(policy->cpu);
+ return bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new);
+}
- freqs.old = bL_cpufreq_get(policy->cpu);
+static inline u32 get_table_count(struct cpufreq_frequency_table *table)
+{
+ int count;
- /* Determine valid target frequency using freq_table */
- cpufreq_frequency_table_target(policy, freq_table[cur_cluster],
- target_freq, relation, &freq_tab_idx);
- freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency;
+ for (count = 0; table[count].frequency != CPUFREQ_TABLE_END; count++)
+ ;
- pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n",
- __func__, cpu, cur_cluster, freqs.old, target_freq,
- freqs.new);
+ return count;
+}
- if (freqs.old == freqs.new)
- return 0;
+/* get the minimum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_min(struct cpufreq_frequency_table *table)
+{
+ int i;
+ uint32_t min_freq = ~0;
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+ if (table[i].frequency < min_freq)
+ min_freq = table[i].frequency;
+ return min_freq;
+}
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+/* get the maximum frequency in the cpufreq_frequency_table */
+static inline u32 get_table_max(struct cpufreq_frequency_table *table)
+{
+ int i;
+ uint32_t max_freq = 0;
+ for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++)
+ if (table[i].frequency > max_freq)
+ max_freq = table[i].frequency;
+ return max_freq;
+}
- ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
- if (ret) {
- pr_err("clk_set_rate failed: %d\n", ret);
- return ret;
+static int merge_cluster_tables(void)
+{
+ int i, j, k = 0, count = 1;
+ struct cpufreq_frequency_table *table;
+
+ for (i = 0; i < MAX_CLUSTERS; i++)
+ count += get_table_count(freq_table[i]);
+
+ table = kzalloc(sizeof(*table) * count, GFP_KERNEL);
+ if (!table)
+ return -ENOMEM;
+
+ freq_table[MAX_CLUSTERS] = table;
+
+ /* Add in reverse order to get freqs in increasing order */
+ for (i = MAX_CLUSTERS - 1; i >= 0; i--) {
+ for (j = 0; freq_table[i][j].frequency != CPUFREQ_TABLE_END;
+ j++) {
+ table[k].frequency = VIRT_FREQ(i,
+ freq_table[i][j].frequency);
+ pr_debug("%s: index: %d, freq: %d\n", __func__, k,
+ table[k].frequency);
+ k++;
+ }
}
- policy->cur = freqs.new;
+ table[k].driver_data = k;
+ table[k].frequency = CPUFREQ_TABLE_END;
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+ pr_debug("%s: End, table: %p, count: %d\n", __func__, table, k);
- return ret;
+ return 0;
+}
+
+static void _put_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+ u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
+
+ if (!freq_table[cluster])
+ return;
+
+ clk_put(clk[cluster]);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
+ dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
}
static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
{
u32 cluster = cpu_to_cluster(cpu_dev->id);
+ int i;
+
+ if (atomic_dec_return(&cluster_usage[cluster]))
+ return;
+
+ if (cluster < MAX_CLUSTERS)
+ return _put_cluster_clk_and_freq_table(cpu_dev);
- if (!atomic_dec_return(&cluster_usage[cluster])) {
- clk_put(clk[cluster]);
- opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
- dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return;
+ }
+
+ _put_cluster_clk_and_freq_table(cdev);
}
+
+ /* free virtual table */
+ kfree(freq_table[cluster]);
}
-static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
+static int _get_cluster_clk_and_freq_table(struct device *cpu_dev)
{
- u32 cluster = cpu_to_cluster(cpu_dev->id);
+ u32 cluster = raw_cpu_to_cluster(cpu_dev->id);
char name[14] = "cpu-cluster.";
int ret;
- if (atomic_inc_return(&cluster_usage[cluster]) != 1)
+ if (freq_table[cluster])
return 0;
ret = arm_bL_ops->init_opp_table(cpu_dev);
if (ret) {
dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
- goto atomic_dec;
+ goto out;
}
- ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
if (ret) {
dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
__func__, cpu_dev->id, ret);
- goto atomic_dec;
+ goto out;
}
name[12] = cluster + '0';
- clk[cluster] = clk_get_sys(name, NULL);
+ clk[cluster] = clk_get(cpu_dev, name);
if (!IS_ERR(clk[cluster])) {
dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
__func__, clk[cluster], freq_table[cluster],
@@ -140,15 +351,74 @@ static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
__func__, cpu_dev->id, cluster);
ret = PTR_ERR(clk[cluster]);
- opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
-atomic_dec:
- atomic_dec(&cluster_usage[cluster]);
+out:
dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
cluster);
return ret;
}
+static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
+{
+ u32 cluster = cpu_to_cluster(cpu_dev->id);
+ int i, ret;
+
+ if (atomic_inc_return(&cluster_usage[cluster]) != 1)
+ return 0;
+
+ if (cluster < MAX_CLUSTERS) {
+ ret = _get_cluster_clk_and_freq_table(cpu_dev);
+ if (ret)
+ atomic_dec(&cluster_usage[cluster]);
+ return ret;
+ }
+
+ /*
+ * Get data for all clusters and fill virtual cluster with a merge of
+ * both
+ */
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return -ENODEV;
+ }
+
+ ret = _get_cluster_clk_and_freq_table(cdev);
+ if (ret)
+ goto put_clusters;
+ }
+
+ ret = merge_cluster_tables();
+ if (ret)
+ goto put_clusters;
+
+ /* Assuming 2 cluster, set clk_big_min and clk_little_max */
+ clk_big_min = get_table_min(freq_table[0]);
+ clk_little_max = VIRT_FREQ(1, get_table_max(freq_table[1]));
+
+ pr_debug("%s: cluster: %d, clk_big_min: %d, clk_little_max: %d\n",
+ __func__, cluster, clk_big_min, clk_little_max);
+
+ return 0;
+
+put_clusters:
+ for_each_present_cpu(i) {
+ struct device *cdev = get_cpu_device(i);
+ if (!cdev) {
+ pr_err("%s: failed to get cpu%d device\n", __func__, i);
+ return -ENODEV;
+ }
+
+ _put_cluster_clk_and_freq_table(cdev);
+ }
+
+ atomic_dec(&cluster_usage[cluster]);
+
+ return ret;
+}
+
/* Per-CPU initialization */
static int bL_cpufreq_init(struct cpufreq_policy *policy)
{
@@ -167,7 +437,7 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
if (ret)
return ret;
- ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
+ ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]);
if (ret) {
dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
policy->cpu, cur_cluster);
@@ -175,7 +445,14 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
return ret;
}
- cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
+ if (cur_cluster < MAX_CLUSTERS) {
+ cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
+
+ per_cpu(physical_cluster, policy->cpu) = cur_cluster;
+ } else {
+ /* Assumption: during init, we are always running on A15 */
+ per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER;
+ }
if (arm_bL_ops->get_transition_latency)
policy->cpuinfo.transition_latency =
@@ -183,9 +460,8 @@ static int bL_cpufreq_init(struct cpufreq_policy *policy)
else
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
- policy->cur = bL_cpufreq_get(policy->cpu);
-
- cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
+ if (is_bL_switching_enabled())
+ per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);
dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
return 0;
@@ -202,33 +478,60 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy)
return -ENODEV;
}
+ cpufreq_frequency_table_put_attr(policy->cpu);
put_cluster_clk_and_freq_table(cpu_dev);
dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu);
return 0;
}
-/* Export freq_table to sysfs */
-static struct freq_attr *bL_cpufreq_attr[] = {
- &cpufreq_freq_attr_scaling_available_freqs,
- NULL,
-};
-
static struct cpufreq_driver bL_cpufreq_driver = {
.name = "arm-big-little",
- .flags = CPUFREQ_STICKY,
- .verify = bL_cpufreq_verify_policy,
- .target = bL_cpufreq_set_target,
- .get = bL_cpufreq_get,
+ .flags = CPUFREQ_STICKY |
+ CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
+ .verify = cpufreq_generic_frequency_table_verify,
+ .target_index = bL_cpufreq_set_target,
+ .get = bL_cpufreq_get_rate,
.init = bL_cpufreq_init,
.exit = bL_cpufreq_exit,
- .have_governor_per_policy = true,
- .attr = bL_cpufreq_attr,
+ .attr = cpufreq_generic_attr,
+};
+
+static int bL_cpufreq_switcher_notifier(struct notifier_block *nfb,
+ unsigned long action, void *_arg)
+{
+ pr_debug("%s: action: %ld\n", __func__, action);
+
+ switch (action) {
+ case BL_NOTIFY_PRE_ENABLE:
+ case BL_NOTIFY_PRE_DISABLE:
+ cpufreq_unregister_driver(&bL_cpufreq_driver);
+ break;
+
+ case BL_NOTIFY_POST_ENABLE:
+ set_switching_enabled(true);
+ cpufreq_register_driver(&bL_cpufreq_driver);
+ break;
+
+ case BL_NOTIFY_POST_DISABLE:
+ set_switching_enabled(false);
+ cpufreq_register_driver(&bL_cpufreq_driver);
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block bL_switcher_notifier = {
+ .notifier_call = bL_cpufreq_switcher_notifier,
};
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
{
- int ret;
+ int ret, i;
if (arm_bL_ops) {
pr_debug("%s: Already registered: %s, exiting\n", __func__,
@@ -243,16 +546,29 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
arm_bL_ops = ops;
+ ret = bL_switcher_get_enabled();
+ set_switching_enabled(ret);
+
+ for (i = 0; i < MAX_CLUSTERS; i++)
+ mutex_init(&cluster_lock[i]);
+
ret = cpufreq_register_driver(&bL_cpufreq_driver);
if (ret) {
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
__func__, ops->name, ret);
arm_bL_ops = NULL;
} else {
- pr_info("%s: Registered platform driver: %s\n", __func__,
- ops->name);
+ ret = bL_switcher_register_notifier(&bL_switcher_notifier);
+ if (ret) {
+ cpufreq_unregister_driver(&bL_cpufreq_driver);
+ arm_bL_ops = NULL;
+ } else {
+ pr_info("%s: Registered platform driver: %s\n",
+ __func__, ops->name);
+ }
}
+ bL_switcher_put_enabled();
return ret;
}
EXPORT_SYMBOL_GPL(bL_cpufreq_register);
@@ -265,7 +581,10 @@ void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
return;
}
+ bL_switcher_get_enabled();
+ bL_switcher_unregister_notifier(&bL_switcher_notifier);
cpufreq_unregister_driver(&bL_cpufreq_driver);
+ bL_switcher_put_enabled();
pr_info("%s: Un-registered platform driver: %s\n", __func__,
arm_bL_ops->name);
arm_bL_ops = NULL;
diff --git a/drivers/cpufreq/arm_big_little.h b/drivers/cpufreq/arm_big_little.h
index 79b2ce17884d..70f18fc12d4a 100644
--- a/drivers/cpufreq/arm_big_little.h
+++ b/drivers/cpufreq/arm_big_little.h
@@ -34,11 +34,6 @@ struct cpufreq_arm_bL_ops {
int (*init_opp_table)(struct device *cpu_dev);
};
-static inline int cpu_to_cluster(int cpu)
-{
- return topology_physical_package_id(cpu);
-}
-
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops);
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops);
diff --git a/drivers/cpufreq/arm_big_little_dt.c b/drivers/cpufreq/arm_big_little_dt.c
index fd9e3ea6a480..8d9d59108906 100644
--- a/drivers/cpufreq/arm_big_little_dt.c
+++ b/drivers/cpufreq/arm_big_little_dt.c
@@ -19,13 +19,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/device.h>
#include <linux/export.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/opp.h>
+#include <linux/of_device.h>
+#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -34,27 +33,13 @@
/* get cpu node with valid operating-points */
static struct device_node *get_cpu_node_with_valid_op(int cpu)
{
- struct device_node *np = NULL, *parent;
- int count = 0;
+ struct device_node *np = of_cpu_device_node_get(cpu);
- parent = of_find_node_by_path("/cpus");
- if (!parent) {
- pr_err("failed to find OF /cpus\n");
- return NULL;
+ if (!of_get_property(np, "operating-points", NULL)) {
+ of_node_put(np);
+ np = NULL;
}
- for_each_child_of_node(parent, np) {
- if (count++ != cpu)
- continue;
- if (!of_get_property(np, "operating-points", NULL)) {
- of_node_put(np);
- np = NULL;
- }
-
- break;
- }
-
- of_node_put(parent);
return np;
}
@@ -63,11 +48,12 @@ static int dt_init_opp_table(struct device *cpu_dev)
struct device_node *np;
int ret;
- np = get_cpu_node_with_valid_op(cpu_dev->id);
- if (!np)
- return -ENODATA;
+ np = of_node_get(cpu_dev->of_node);
+ if (!np) {
+ pr_err("failed to find cpu%d node\n", cpu_dev->id);
+ return -ENOENT;
+ }
- cpu_dev->of_node = np;
ret = of_init_opp_table(cpu_dev);
of_node_put(np);
@@ -79,9 +65,11 @@ static int dt_get_transition_latency(struct device *cpu_dev)
struct device_node *np;
u32 transition_latency = CPUFREQ_ETERNAL;
- np = get_cpu_node_with_valid_op(cpu_dev->id);
- if (!np)
+ np = of_node_get(cpu_dev->of_node);
+ if (!np) {
+ pr_info("Failed to find cpu node. Use CPUFREQ_ETERNAL transition latency\n");
return CPUFREQ_ETERNAL;
+ }
of_property_read_u32(np, "clock-latency", &transition_latency);
of_node_put(np);
diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c
index 654488723cb5..e0c38d938997 100644
--- a/drivers/cpufreq/at32ap-cpufreq.c
+++ b/drivers/cpufreq/at32ap-cpufreq.c
@@ -108,7 +108,6 @@ static int __init at32_cpufreq_driver_init(struct cpufreq_policy *policy)
static struct cpufreq_driver at32_driver = {
.name = "at32ap",
- .owner = THIS_MODULE,
.init = at32_cpufreq_driver_init,
.verify = at32_verify_speed,
.target = at32_set_target,
diff --git a/drivers/cpufreq/blackfin-cpufreq.c b/drivers/cpufreq/blackfin-cpufreq.c
index 995511e80bef..ef05978a7237 100644
--- a/drivers/cpufreq/blackfin-cpufreq.c
+++ b/drivers/cpufreq/blackfin-cpufreq.c
@@ -20,23 +20,23 @@
/* this is the table of CCLK frequencies, in Hz */
-/* .index is the entry in the auxiliary dpm_state_table[] */
+/* .driver_data is the entry in the auxiliary dpm_state_table[] */
static struct cpufreq_frequency_table bfin_freq_table[] = {
{
.frequency = CPUFREQ_TABLE_END,
- .index = 0,
+ .driver_data = 0,
},
{
.frequency = CPUFREQ_TABLE_END,
- .index = 1,
+ .driver_data = 1,
},
{
.frequency = CPUFREQ_TABLE_END,
- .index = 2,
+ .driver_data = 2,
},
{
.frequency = CPUFREQ_TABLE_END,
- .index = 0,
+ .driver_data = 0,
},
};
@@ -225,7 +225,6 @@ static struct cpufreq_driver bfin_driver = {
.get = bfin_getfreq_khz,
.init = __bfin_cpu_init,
.name = "bfin cpufreq",
- .owner = THIS_MODULE,
.attr = bfin_freq_attr,
};
diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c
index ad1fde277661..16d3979c485e 100644
--- a/drivers/cpufreq/cpufreq-cpu0.c
+++ b/drivers/cpufreq/cpufreq-cpu0.c
@@ -16,7 +16,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -71,7 +71,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy,
if (cpu_reg) {
rcu_read_lock();
- opp = opp_find_freq_ceil(cpu_dev, &freq_Hz);
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_Hz);
if (IS_ERR(opp)) {
rcu_read_unlock();
pr_err("failed to find OPP for %ld\n", freq_Hz);
@@ -79,7 +79,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy,
ret = PTR_ERR(opp);
goto post_notify;
}
- volt = opp_get_voltage(opp);
+ volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
tol = volt * voltage_tolerance / 100;
volt_old = regulator_get_voltage(cpu_reg);
@@ -226,7 +226,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
goto out_put_node;
}
- ret = opp_init_cpufreq_table(cpu_dev, &freq_table);
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
pr_err("failed to init cpufreq table: %d\n", ret);
goto out_put_node;
@@ -250,12 +250,12 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)
;
rcu_read_lock();
- opp = opp_find_freq_exact(cpu_dev,
+ opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
- min_uV = opp_get_voltage(opp);
- opp = opp_find_freq_exact(cpu_dev,
+ min_uV = dev_pm_opp_get_voltage(opp);
+ opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[i-1].frequency * 1000, true);
- max_uV = opp_get_voltage(opp);
+ max_uV = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
ret = regulator_set_voltage_time(cpu_reg, min_uV, max_uV);
if (ret > 0)
@@ -273,7 +273,7 @@ static int cpu0_cpufreq_probe(struct platform_device *pdev)
return 0;
out_free_table:
- opp_free_cpufreq_table(cpu_dev, &freq_table);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_put_node:
of_node_put(np);
out_put_parent:
@@ -284,7 +284,7 @@ out_put_parent:
static int cpu0_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&cpu0_cpufreq_driver);
- opp_free_cpufreq_table(cpu_dev, &freq_table);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
return 0;
}
diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c
index af1542d41440..56c964c16a66 100644
--- a/drivers/cpufreq/cpufreq-nforce2.c
+++ b/drivers/cpufreq/cpufreq-nforce2.c
@@ -303,9 +303,7 @@ static int nforce2_verify(struct cpufreq_policy *policy)
if (policy->min < (fsb_pol_max * fid * 100))
policy->max = (fsb_pol_max + 1) * fid * 100;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
return 0;
}
@@ -379,7 +377,6 @@ static struct cpufreq_driver nforce2_driver = {
.get = nforce2_get,
.init = nforce2_cpu_init,
.exit = nforce2_cpu_exit,
- .owner = THIS_MODULE,
};
#ifdef MODULE
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 9397798e5586..2f5134212abe 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2001 Russell King
* (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ * (C) 2013 Viresh Kumar <viresh.kumar@linaro.org>
*
* Oct 2005 - Ashok Raj <ashok.raj@intel.com>
* Added handling for CPU hotplug
@@ -12,26 +13,21 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
- *
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/notifier.h>
+#include <linux/cpu.h>
#include <linux/cpufreq.h>
#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
#include <linux/device.h>
-#include <linux/slab.h>
-#include <linux/cpu.h>
-#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/slab.h>
#include <linux/syscore_ops.h>
-
+#include <linux/tick.h>
#include <trace/events/power.h>
/**
@@ -41,6 +37,11 @@
*/
static struct cpufreq_driver *cpufreq_driver;
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
+static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
+static DEFINE_RWLOCK(cpufreq_driver_lock);
+static DEFINE_MUTEX(cpufreq_governor_lock);
+static LIST_HEAD(cpufreq_policy_list);
+
#ifdef CONFIG_HOTPLUG_CPU
/*
* This one keeps track of the previously set governor and user-set
@@ -52,52 +53,17 @@ struct cpufreq_cpu_save_data {
};
static DEFINE_PER_CPU(struct cpufreq_cpu_save_data, cpufreq_policy_save);
#endif
-static DEFINE_RWLOCK(cpufreq_driver_lock);
-static DEFINE_MUTEX(cpufreq_governor_lock);
-/*
- * cpu_policy_rwsem is a per CPU reader-writer semaphore designed to cure
- * all cpufreq/hotplug/workqueue/etc related lock issues.
- *
- * The rules for this semaphore:
- * - Any routine that wants to read from the policy structure will
- * do a down_read on this semaphore.
- * - Any routine that will write to the policy structure and/or may take away
- * the policy altogether (eg. CPU hotplug), will hold this lock in write
- * mode before doing so.
- *
- * Additional rules:
- * - Governor routines that can be called in cpufreq hotplug path should not
- * take this sem as top level hotplug notifier handler takes this.
- * - Lock should not be held across
- * __cpufreq_governor(data, CPUFREQ_GOV_STOP);
- */
-static DEFINE_PER_CPU(int, cpufreq_policy_cpu);
-static DEFINE_PER_CPU(struct rw_semaphore, cpu_policy_rwsem);
-
-#define lock_policy_rwsem(mode, cpu) \
-static int lock_policy_rwsem_##mode(int cpu) \
-{ \
- int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \
- BUG_ON(policy_cpu == -1); \
- down_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \
- \
- return 0; \
-}
-
-lock_policy_rwsem(read, cpu);
-lock_policy_rwsem(write, cpu);
-
-#define unlock_policy_rwsem(mode, cpu) \
-static void unlock_policy_rwsem_##mode(int cpu) \
-{ \
- int policy_cpu = per_cpu(cpufreq_policy_cpu, cpu); \
- BUG_ON(policy_cpu == -1); \
- up_##mode(&per_cpu(cpu_policy_rwsem, policy_cpu)); \
+static inline bool has_target(void)
+{
+ return cpufreq_driver->target_index || cpufreq_driver->target;
}
-unlock_policy_rwsem(read, cpu);
-unlock_policy_rwsem(write, cpu);
+/*
+ * rwsem to guarantee that cpufreq driver module doesn't unload during critical
+ * sections
+ */
+static DECLARE_RWSEM(cpufreq_rwsem);
/* internal prototypes */
static int __cpufreq_governor(struct cpufreq_policy *policy,
@@ -138,82 +104,125 @@ static DEFINE_MUTEX(cpufreq_governor_mutex);
bool have_governor_per_policy(void)
{
- return cpufreq_driver->have_governor_per_policy;
+ return !!(cpufreq_driver->flags & CPUFREQ_HAVE_GOVERNOR_PER_POLICY);
}
+EXPORT_SYMBOL_GPL(have_governor_per_policy);
-static struct cpufreq_policy *__cpufreq_cpu_get(unsigned int cpu, bool sysfs)
+struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
{
- struct cpufreq_policy *data;
- unsigned long flags;
+ if (have_governor_per_policy())
+ return &policy->kobj;
+ else
+ return cpufreq_global_kobject;
+}
+EXPORT_SYMBOL_GPL(get_governor_parent_kobj);
- if (cpu >= nr_cpu_ids)
- goto err_out;
+static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall)
+{
+ u64 idle_time;
+ u64 cur_wall_time;
+ u64 busy_time;
- /* get the cpufreq driver */
- read_lock_irqsave(&cpufreq_driver_lock, flags);
+ cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
- if (!cpufreq_driver)
- goto err_out_unlock;
+ busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
+ busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
- if (!try_module_get(cpufreq_driver->owner))
- goto err_out_unlock;
+ idle_time = cur_wall_time - busy_time;
+ if (wall)
+ *wall = cputime_to_usecs(cur_wall_time);
+ return cputime_to_usecs(idle_time);
+}
- /* get the CPU */
- data = per_cpu(cpufreq_cpu_data, cpu);
+u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
+{
+ u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL);
- if (!data)
- goto err_out_put_module;
+ if (idle_time == -1ULL)
+ return get_cpu_idle_time_jiffy(cpu, wall);
+ else if (!io_busy)
+ idle_time += get_cpu_iowait_time_us(cpu, wall);
- if (!sysfs && !kobject_get(&data->kobj))
- goto err_out_put_module;
+ return idle_time;
+}
+EXPORT_SYMBOL_GPL(get_cpu_idle_time);
- read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- return data;
+/*
+ * This is a generic cpufreq init() routine which can be used by cpufreq
+ * drivers of SMP systems. It will do following:
+ * - validate & show freq table passed
+ * - set policies transition latency
+ * - policy->cpus with all possible CPUs
+ */
+int cpufreq_generic_init(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *table,
+ unsigned int transition_latency)
+{
+ int ret;
-err_out_put_module:
- module_put(cpufreq_driver->owner);
-err_out_unlock:
- read_unlock_irqrestore(&cpufreq_driver_lock, flags);
-err_out:
- return NULL;
+ ret = cpufreq_table_validate_and_show(policy, table);
+ if (ret) {
+ pr_err("%s: invalid frequency table: %d\n", __func__, ret);
+ return ret;
+ }
+
+ policy->cpuinfo.transition_latency = transition_latency;
+
+ /*
+ * The driver only supports the SMP configuartion where all processors
+ * share the clock and voltage and clock.
+ */
+ cpumask_setall(policy->cpus);
+
+ return 0;
}
+EXPORT_SYMBOL_GPL(cpufreq_generic_init);
struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
- if (cpufreq_disabled())
+ struct cpufreq_policy *policy = NULL;
+ unsigned long flags;
+
+ if (cpufreq_disabled() || (cpu >= nr_cpu_ids))
return NULL;
- return __cpufreq_cpu_get(cpu, false);
-}
-EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
+ if (!down_read_trylock(&cpufreq_rwsem))
+ return NULL;
-static struct cpufreq_policy *cpufreq_cpu_get_sysfs(unsigned int cpu)
-{
- return __cpufreq_cpu_get(cpu, true);
-}
+ /* get the cpufreq driver */
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
-static void __cpufreq_cpu_put(struct cpufreq_policy *data, bool sysfs)
-{
- if (!sysfs)
- kobject_put(&data->kobj);
- module_put(cpufreq_driver->owner);
+ if (cpufreq_driver) {
+ /* get the CPU */
+ policy = per_cpu(cpufreq_cpu_data, cpu);
+ if (policy)
+ kobject_get(&policy->kobj);
+ }
+
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ if (!policy)
+ up_read(&cpufreq_rwsem);
+
+ return policy;
}
+EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
-void cpufreq_cpu_put(struct cpufreq_policy *data)
+void cpufreq_cpu_put(struct cpufreq_policy *policy)
{
if (cpufreq_disabled())
return;
- __cpufreq_cpu_put(data, false);
+ kobject_put(&policy->kobj);
+ up_read(&cpufreq_rwsem);
}
EXPORT_SYMBOL_GPL(cpufreq_cpu_put);
-static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data)
-{
- __cpufreq_cpu_put(data, true);
-}
-
/*********************************************************************
* EXTERNALLY AFFECTING FREQUENCY CHANGES *
*********************************************************************/
@@ -228,7 +237,7 @@ static void cpufreq_cpu_put_sysfs(struct cpufreq_policy *data)
*/
#ifndef CONFIG_SMP
static unsigned long l_p_j_ref;
-static unsigned int l_p_j_ref_freq;
+static unsigned int l_p_j_ref_freq;
static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
{
@@ -241,7 +250,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
pr_debug("saving %lu as reference value for loops_per_jiffy; "
"freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq);
}
- if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) ||
+ if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) ||
(val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) {
loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq,
ci->new);
@@ -256,8 +265,7 @@ static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci)
}
#endif
-
-void __cpufreq_notify_transition(struct cpufreq_policy *policy,
+static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state)
{
BUG_ON(irqs_disabled());
@@ -304,6 +312,7 @@ void __cpufreq_notify_transition(struct cpufreq_policy *policy,
break;
}
}
+
/**
* cpufreq_notify_transition - call notifier chain and adjust_jiffies
* on frequency transition.
@@ -321,7 +330,6 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy,
EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
-
/*********************************************************************
* SYSFS INTERFACE *
*********************************************************************/
@@ -357,7 +365,7 @@ static int cpufreq_parse_governor(char *str_governor, unsigned int *policy,
*policy = CPUFREQ_POLICY_POWERSAVE;
err = 0;
}
- } else if (cpufreq_driver->target) {
+ } else if (has_target()) {
struct cpufreq_governor *t;
mutex_lock(&cpufreq_governor_mutex);
@@ -386,7 +394,6 @@ out:
return err;
}
-
/**
* cpufreq_per_cpu_attr_read() / show_##file_name() -
* print out cpufreq information
@@ -409,8 +416,8 @@ show_one(scaling_min_freq, min);
show_one(scaling_max_freq, max);
show_one(scaling_cur_freq, cur);
-static int __cpufreq_set_policy(struct cpufreq_policy *data,
- struct cpufreq_policy *policy);
+static int cpufreq_set_policy(struct cpufreq_policy *policy,
+ struct cpufreq_policy *new_policy);
/**
* cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access
@@ -419,7 +426,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
static ssize_t store_##file_name \
(struct cpufreq_policy *policy, const char *buf, size_t count) \
{ \
- unsigned int ret; \
+ int ret; \
struct cpufreq_policy new_policy; \
\
ret = cpufreq_get_policy(&new_policy, policy->cpu); \
@@ -435,7 +442,7 @@ static ssize_t store_##file_name \
pr_err("cpufreq: Frequency verification failed\n"); \
\
policy->user_policy.object = new_policy.object; \
- ret = __cpufreq_set_policy(policy, &new_policy); \
+ ret = cpufreq_set_policy(policy, &new_policy); \
\
return ret ? ret : count; \
}
@@ -455,7 +462,6 @@ static ssize_t show_cpuinfo_cur_freq(struct cpufreq_policy *policy,
return sprintf(buf, "%u\n", cur_freq);
}
-
/**
* show_scaling_governor - show the current policy for the specified CPU
*/
@@ -471,14 +477,13 @@ static ssize_t show_scaling_governor(struct cpufreq_policy *policy, char *buf)
return -EINVAL;
}
-
/**
* store_scaling_governor - store policy for the specified CPU
*/
static ssize_t store_scaling_governor(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
- unsigned int ret;
+ int ret;
char str_governor[16];
struct cpufreq_policy new_policy;
@@ -494,9 +499,7 @@ static ssize_t store_scaling_governor(struct cpufreq_policy *policy,
&new_policy.governor))
return -EINVAL;
- /* Do not use cpufreq_set_policy here or the user_policy.max
- will be wrongly overridden */
- ret = __cpufreq_set_policy(policy, &new_policy);
+ ret = cpufreq_set_policy(policy, &new_policy);
policy->user_policy.policy = policy->policy;
policy->user_policy.governor = policy->governor;
@@ -526,7 +529,7 @@ static ssize_t show_scaling_available_governors(struct cpufreq_policy *policy,
ssize_t i = 0;
struct cpufreq_governor *t;
- if (!cpufreq_driver->target) {
+ if (!has_target()) {
i += sprintf(buf, "performance powersave");
goto out;
}
@@ -542,7 +545,7 @@ out:
return i;
}
-static ssize_t show_cpus(const struct cpumask *mask, char *buf)
+ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf)
{
ssize_t i = 0;
unsigned int cpu;
@@ -557,6 +560,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf)
i += sprintf(&buf[i], "\n");
return i;
}
+EXPORT_SYMBOL_GPL(cpufreq_show_cpus);
/**
* show_related_cpus - show the CPUs affected by each transition even if
@@ -564,7 +568,7 @@ static ssize_t show_cpus(const struct cpumask *mask, char *buf)
*/
static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf)
{
- return show_cpus(policy->related_cpus, buf);
+ return cpufreq_show_cpus(policy->related_cpus, buf);
}
/**
@@ -572,7 +576,7 @@ static ssize_t show_related_cpus(struct cpufreq_policy *policy, char *buf)
*/
static ssize_t show_affected_cpus(struct cpufreq_policy *policy, char *buf)
{
- return show_cpus(policy->cpus, buf);
+ return cpufreq_show_cpus(policy->cpus, buf);
}
static ssize_t store_scaling_setspeed(struct cpufreq_policy *policy,
@@ -646,9 +650,6 @@ static struct attribute *default_attrs[] = {
NULL
};
-struct kobject *cpufreq_global_kobject;
-EXPORT_SYMBOL(cpufreq_global_kobject);
-
#define to_policy(k) container_of(k, struct cpufreq_policy, kobj)
#define to_attr(a) container_of(a, struct freq_attr, attr)
@@ -656,23 +657,21 @@ static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct cpufreq_policy *policy = to_policy(kobj);
struct freq_attr *fattr = to_attr(attr);
- ssize_t ret = -EINVAL;
- policy = cpufreq_cpu_get_sysfs(policy->cpu);
- if (!policy)
- goto no_policy;
+ ssize_t ret;
+
+ if (!down_read_trylock(&cpufreq_rwsem))
+ return -EINVAL;
- if (lock_policy_rwsem_read(policy->cpu) < 0)
- goto fail;
+ down_read(&policy->rwsem);
if (fattr->show)
ret = fattr->show(policy, buf);
else
ret = -EIO;
- unlock_policy_rwsem_read(policy->cpu);
-fail:
- cpufreq_cpu_put_sysfs(policy);
-no_policy:
+ up_read(&policy->rwsem);
+ up_read(&cpufreq_rwsem);
+
return ret;
}
@@ -682,22 +681,28 @@ static ssize_t store(struct kobject *kobj, struct attribute *attr,
struct cpufreq_policy *policy = to_policy(kobj);
struct freq_attr *fattr = to_attr(attr);
ssize_t ret = -EINVAL;
- policy = cpufreq_cpu_get_sysfs(policy->cpu);
- if (!policy)
- goto no_policy;
- if (lock_policy_rwsem_write(policy->cpu) < 0)
- goto fail;
+ get_online_cpus();
+
+ if (!cpu_online(policy->cpu))
+ goto unlock;
+
+ if (!down_read_trylock(&cpufreq_rwsem))
+ goto unlock;
+
+ down_write(&policy->rwsem);
if (fattr->store)
ret = fattr->store(policy, buf, count);
else
ret = -EIO;
- unlock_policy_rwsem_write(policy->cpu);
-fail:
- cpufreq_cpu_put_sysfs(policy);
-no_policy:
+ up_write(&policy->rwsem);
+
+ up_read(&cpufreq_rwsem);
+unlock:
+ put_online_cpus();
+
return ret;
}
@@ -719,42 +724,76 @@ static struct kobj_type ktype_cpufreq = {
.release = cpufreq_sysfs_release,
};
+struct kobject *cpufreq_global_kobject;
+EXPORT_SYMBOL(cpufreq_global_kobject);
+
+static int cpufreq_global_kobject_usage;
+
+int cpufreq_get_global_kobject(void)
+{
+ if (!cpufreq_global_kobject_usage++)
+ return kobject_add(cpufreq_global_kobject,
+ &cpu_subsys.dev_root->kobj, "%s", "cpufreq");
+
+ return 0;
+}
+EXPORT_SYMBOL(cpufreq_get_global_kobject);
+
+void cpufreq_put_global_kobject(void)
+{
+ if (!--cpufreq_global_kobject_usage)
+ kobject_del(cpufreq_global_kobject);
+}
+EXPORT_SYMBOL(cpufreq_put_global_kobject);
+
+int cpufreq_sysfs_create_file(const struct attribute *attr)
+{
+ int ret = cpufreq_get_global_kobject();
+
+ if (!ret) {
+ ret = sysfs_create_file(cpufreq_global_kobject, attr);
+ if (ret)
+ cpufreq_put_global_kobject();
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(cpufreq_sysfs_create_file);
+
+void cpufreq_sysfs_remove_file(const struct attribute *attr)
+{
+ sysfs_remove_file(cpufreq_global_kobject, attr);
+ cpufreq_put_global_kobject();
+}
+EXPORT_SYMBOL(cpufreq_sysfs_remove_file);
+
/* symlink affected CPUs */
-static int cpufreq_add_dev_symlink(unsigned int cpu,
- struct cpufreq_policy *policy)
+static int cpufreq_add_dev_symlink(struct cpufreq_policy *policy)
{
unsigned int j;
int ret = 0;
for_each_cpu(j, policy->cpus) {
- struct cpufreq_policy *managed_policy;
struct device *cpu_dev;
- if (j == cpu)
+ if (j == policy->cpu)
continue;
- pr_debug("CPU %u already managed, adding link\n", j);
- managed_policy = cpufreq_cpu_get(cpu);
+ pr_debug("Adding link for CPU: %u\n", j);
cpu_dev = get_cpu_device(j);
ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
"cpufreq");
- if (ret) {
- cpufreq_cpu_put(managed_policy);
- return ret;
- }
+ if (ret)
+ break;
}
return ret;
}
-static int cpufreq_add_dev_interface(unsigned int cpu,
- struct cpufreq_policy *policy,
+static int cpufreq_add_dev_interface(struct cpufreq_policy *policy,
struct device *dev)
{
- struct cpufreq_policy new_policy;
struct freq_attr **drv_attr;
- unsigned long flags;
int ret = 0;
- unsigned int j;
/* prepare interface data */
ret = kobject_init_and_add(&policy->kobj, &ktype_cpufreq,
@@ -775,7 +814,7 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
if (ret)
goto err_out_kobj_put;
}
- if (cpufreq_driver->target) {
+ if (has_target()) {
ret = sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr);
if (ret)
goto err_out_kobj_put;
@@ -786,23 +825,29 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
goto err_out_kobj_put;
}
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_cpu(j, policy->cpus) {
- per_cpu(cpufreq_cpu_data, j) = policy;
- per_cpu(cpufreq_policy_cpu, j) = policy->cpu;
- }
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
- ret = cpufreq_add_dev_symlink(cpu, policy);
+ ret = cpufreq_add_dev_symlink(policy);
if (ret)
goto err_out_kobj_put;
- memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));
- /* assure that the starting sequence is run in __cpufreq_set_policy */
+ return ret;
+
+err_out_kobj_put:
+ kobject_put(&policy->kobj);
+ wait_for_completion(&policy->kobj_unregister);
+ return ret;
+}
+
+static void cpufreq_init_policy(struct cpufreq_policy *policy)
+{
+ struct cpufreq_policy new_policy;
+ int ret = 0;
+
+ memcpy(&new_policy, policy, sizeof(*policy));
+ /* assure that the starting sequence is run in cpufreq_set_policy */
policy->governor = NULL;
/* set default policy */
- ret = __cpufreq_set_policy(policy, &new_policy);
+ ret = cpufreq_set_policy(policy, &new_policy);
policy->user_policy.policy = policy->policy;
policy->user_policy.governor = policy->governor;
@@ -811,72 +856,125 @@ static int cpufreq_add_dev_interface(unsigned int cpu,
if (cpufreq_driver->exit)
cpufreq_driver->exit(policy);
}
- return ret;
-
-err_out_kobj_put:
- kobject_put(&policy->kobj);
- wait_for_completion(&policy->kobj_unregister);
- return ret;
}
#ifdef CONFIG_HOTPLUG_CPU
-static int cpufreq_add_policy_cpu(unsigned int cpu, unsigned int sibling,
- struct device *dev)
+static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy,
+ unsigned int cpu, struct device *dev,
+ bool frozen)
{
- struct cpufreq_policy *policy;
- int ret = 0, has_target = !!cpufreq_driver->target;
+ int ret = 0;
unsigned long flags;
- policy = cpufreq_cpu_get(sibling);
- WARN_ON(!policy);
-
- if (has_target)
- __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
+ if (has_target()) {
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
+ if (ret) {
+ pr_err("%s: Failed to stop governor\n", __func__);
+ return ret;
+ }
+ }
- lock_policy_rwsem_write(sibling);
+ down_write(&policy->rwsem);
write_lock_irqsave(&cpufreq_driver_lock, flags);
cpumask_set_cpu(cpu, policy->cpus);
- per_cpu(cpufreq_policy_cpu, cpu) = policy->cpu;
per_cpu(cpufreq_cpu_data, cpu) = policy;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- unlock_policy_rwsem_write(sibling);
+ up_write(&policy->rwsem);
- if (has_target) {
- __cpufreq_governor(policy, CPUFREQ_GOV_START);
- __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
+ if (has_target()) {
+ if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) ||
+ (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) {
+ pr_err("%s: Failed to start governor\n", __func__);
+ return ret;
+ }
}
- ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
- if (ret) {
- cpufreq_cpu_put(policy);
- return ret;
- }
+ /* Don't touch sysfs links during light-weight init */
+ if (!frozen)
+ ret = sysfs_create_link(&dev->kobj, &policy->kobj, "cpufreq");
- return 0;
+ return ret;
}
#endif
-/**
- * cpufreq_add_dev - add a CPU device
- *
- * Adds the cpufreq interface for a CPU device.
- *
- * The Oracle says: try running cpufreq registration/unregistration concurrently
- * with with cpu hotplugging and all hell will break loose. Tried to clean this
- * mess up, but more thorough testing is needed. - Mathieu
- */
-static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
+static struct cpufreq_policy *cpufreq_policy_restore(unsigned int cpu)
+{
+ struct cpufreq_policy *policy;
+ unsigned long flags;
+
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+
+ policy = per_cpu(cpufreq_cpu_data_fallback, cpu);
+
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ return policy;
+}
+
+static struct cpufreq_policy *cpufreq_policy_alloc(void)
+{
+ struct cpufreq_policy *policy;
+
+ policy = kzalloc(sizeof(*policy), GFP_KERNEL);
+ if (!policy)
+ return NULL;
+
+ if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
+ goto err_free_policy;
+
+ if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
+ goto err_free_cpumask;
+
+ INIT_LIST_HEAD(&policy->policy_list);
+ init_rwsem(&policy->rwsem);
+
+ return policy;
+
+err_free_cpumask:
+ free_cpumask_var(policy->cpus);
+err_free_policy:
+ kfree(policy);
+
+ return NULL;
+}
+
+static void cpufreq_policy_free(struct cpufreq_policy *policy)
+{
+ free_cpumask_var(policy->related_cpus);
+ free_cpumask_var(policy->cpus);
+ kfree(policy);
+}
+
+static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
+{
+ if (WARN_ON(cpu == policy->cpu))
+ return;
+
+ down_write(&policy->rwsem);
+
+ policy->last_cpu = policy->cpu;
+ policy->cpu = cpu;
+
+ up_write(&policy->rwsem);
+
+ cpufreq_frequency_table_update_policy_cpu(policy);
+ blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
+ CPUFREQ_UPDATE_POLICY_CPU, policy);
+}
+
+static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
+ bool frozen)
{
unsigned int j, cpu = dev->id;
int ret = -ENOMEM;
struct cpufreq_policy *policy;
unsigned long flags;
#ifdef CONFIG_HOTPLUG_CPU
+ struct cpufreq_policy *tpolicy;
struct cpufreq_governor *gov;
- int sibling;
#endif
if (cpu_is_offline(cpu))
@@ -892,43 +990,49 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
cpufreq_cpu_put(policy);
return 0;
}
+#endif
+
+ if (!down_read_trylock(&cpufreq_rwsem))
+ return 0;
#ifdef CONFIG_HOTPLUG_CPU
/* Check if this cpu was hot-unplugged earlier and has siblings */
read_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_online_cpu(sibling) {
- struct cpufreq_policy *cp = per_cpu(cpufreq_cpu_data, sibling);
- if (cp && cpumask_test_cpu(cpu, cp->related_cpus)) {
+ list_for_each_entry(tpolicy, &cpufreq_policy_list, policy_list) {
+ if (cpumask_test_cpu(cpu, tpolicy->related_cpus)) {
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- return cpufreq_add_policy_cpu(cpu, sibling, dev);
+ ret = cpufreq_add_policy_cpu(tpolicy, cpu, dev, frozen);
+ up_read(&cpufreq_rwsem);
+ return ret;
}
}
read_unlock_irqrestore(&cpufreq_driver_lock, flags);
#endif
-#endif
- if (!try_module_get(cpufreq_driver->owner)) {
- ret = -EINVAL;
- goto module_out;
- }
+ if (frozen)
+ /* Restore the saved policy when doing light-weight init */
+ policy = cpufreq_policy_restore(cpu);
+ else
+ policy = cpufreq_policy_alloc();
- policy = kzalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);
if (!policy)
goto nomem_out;
- if (!alloc_cpumask_var(&policy->cpus, GFP_KERNEL))
- goto err_free_policy;
- if (!zalloc_cpumask_var(&policy->related_cpus, GFP_KERNEL))
- goto err_free_cpumask;
+ /*
+ * In the resume path, since we restore a saved policy, the assignment
+ * to policy->cpu is like an update of the existing policy, rather than
+ * the creation of a brand new one. So we need to perform this update
+ * by invoking update_policy_cpu().
+ */
+ if (frozen && cpu != policy->cpu)
+ update_policy_cpu(policy, cpu);
+ else
+ policy->cpu = cpu;
- policy->cpu = cpu;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
cpumask_copy(policy->cpus, cpumask_of(cpu));
- /* Initially set CPU itself as the policy_cpu */
- per_cpu(cpufreq_policy_cpu, cpu) = cpu;
-
init_completion(&policy->kobj_unregister);
INIT_WORK(&policy->update, handle_update);
@@ -941,6 +1045,14 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
goto err_set_policy_cpu;
}
+ if (cpufreq_driver->get) {
+ policy->cur = cpufreq_driver->get(policy->cpu);
+ if (!policy->cur) {
+ pr_err("%s: ->get() failed\n", __func__);
+ goto err_get_freq;
+ }
+ }
+
/* related cpus should atleast have policy->cpus */
cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);
@@ -975,12 +1087,26 @@ static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
policy->min, policy->max);
#endif
- ret = cpufreq_add_dev_interface(cpu, policy, dev);
- if (ret)
- goto err_out_unregister;
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ for_each_cpu(j, policy->cpus)
+ per_cpu(cpufreq_cpu_data, j) = policy;
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ if (!frozen) {
+ ret = cpufreq_add_dev_interface(policy, dev);
+ if (ret)
+ goto err_out_unregister;
+ }
+
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ list_add(&policy->policy_list, &cpufreq_policy_list);
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ cpufreq_init_policy(policy);
kobject_uevent(&policy->kobj, KOBJ_ADD);
- module_put(cpufreq_driver->owner);
+ up_read(&cpufreq_rwsem);
+
pr_debug("initialization complete\n");
return 0;
@@ -991,171 +1117,234 @@ err_out_unregister:
per_cpu(cpufreq_cpu_data, j) = NULL;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- kobject_put(&policy->kobj);
- wait_for_completion(&policy->kobj_unregister);
-
+err_get_freq:
+ if (cpufreq_driver->exit)
+ cpufreq_driver->exit(policy);
err_set_policy_cpu:
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
- free_cpumask_var(policy->related_cpus);
-err_free_cpumask:
- free_cpumask_var(policy->cpus);
-err_free_policy:
- kfree(policy);
+ cpufreq_policy_free(policy);
nomem_out:
- module_put(cpufreq_driver->owner);
-module_out:
+ up_read(&cpufreq_rwsem);
+
return ret;
}
-static void update_policy_cpu(struct cpufreq_policy *policy, unsigned int cpu)
+/**
+ * cpufreq_add_dev - add a CPU device
+ *
+ * Adds the cpufreq interface for a CPU device.
+ *
+ * The Oracle says: try running cpufreq registration/unregistration concurrently
+ * with with cpu hotplugging and all hell will break loose. Tried to clean this
+ * mess up, but more thorough testing is needed. - Mathieu
+ */
+static int cpufreq_add_dev(struct device *dev, struct subsys_interface *sif)
{
- int j;
+ return __cpufreq_add_dev(dev, sif, false);
+}
- policy->last_cpu = policy->cpu;
- policy->cpu = cpu;
+static int cpufreq_nominate_new_policy_cpu(struct cpufreq_policy *policy,
+ unsigned int old_cpu, bool frozen)
+{
+ struct device *cpu_dev;
+ int ret;
- for_each_cpu(j, policy->cpus)
- per_cpu(cpufreq_policy_cpu, j) = cpu;
+ /* first sibling now owns the new sysfs dir */
+ cpu_dev = get_cpu_device(cpumask_any_but(policy->cpus, old_cpu));
-#ifdef CONFIG_CPU_FREQ_TABLE
- cpufreq_frequency_table_update_policy_cpu(policy);
-#endif
- blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_UPDATE_POLICY_CPU, policy);
+ /* Don't touch sysfs files during light-weight tear-down */
+ if (frozen)
+ return cpu_dev->id;
+
+ sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
+ ret = kobject_move(&policy->kobj, &cpu_dev->kobj);
+ if (ret) {
+ pr_err("%s: Failed to move kobj: %d", __func__, ret);
+
+ down_write(&policy->rwsem);
+ cpumask_set_cpu(old_cpu, policy->cpus);
+ up_write(&policy->rwsem);
+
+ ret = sysfs_create_link(&cpu_dev->kobj, &policy->kobj,
+ "cpufreq");
+
+ return -EINVAL;
+ }
+
+ return cpu_dev->id;
}
-/**
- * __cpufreq_remove_dev - remove a CPU device
- *
- * Removes the cpufreq interface for a CPU device.
- * Caller should already have policy_rwsem in write mode for this CPU.
- * This routine frees the rwsem before returning.
- */
-static int __cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
+static int __cpufreq_remove_dev_prepare(struct device *dev,
+ struct subsys_interface *sif,
+ bool frozen)
{
- unsigned int cpu = dev->id, ret, cpus;
+ unsigned int cpu = dev->id, cpus;
+ int new_cpu, ret;
unsigned long flags;
- struct cpufreq_policy *data;
- struct kobject *kobj;
- struct completion *cmp;
- struct device *cpu_dev;
+ struct cpufreq_policy *policy;
pr_debug("%s: unregistering CPU %u\n", __func__, cpu);
write_lock_irqsave(&cpufreq_driver_lock, flags);
- data = per_cpu(cpufreq_cpu_data, cpu);
- per_cpu(cpufreq_cpu_data, cpu) = NULL;
+ policy = per_cpu(cpufreq_cpu_data, cpu);
+
+ /* Save the policy somewhere when doing a light-weight tear-down */
+ if (frozen)
+ per_cpu(cpufreq_cpu_data_fallback, cpu) = policy;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- if (!data) {
+ if (!policy) {
pr_debug("%s: No cpu_data found\n", __func__);
return -EINVAL;
}
- if (cpufreq_driver->target)
- __cpufreq_governor(data, CPUFREQ_GOV_STOP);
+ if (has_target()) {
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
+ if (ret) {
+ pr_err("%s: Failed to stop governor\n", __func__);
+ return ret;
+ }
+ }
#ifdef CONFIG_HOTPLUG_CPU
if (!cpufreq_driver->setpolicy)
strlcpy(per_cpu(cpufreq_policy_save, cpu).gov,
- data->governor->name, CPUFREQ_NAME_LEN);
- per_cpu(cpufreq_policy_save, cpu).min = data->user_policy.min;
- per_cpu(cpufreq_policy_save, cpu).max = data->user_policy.max;
+ policy->governor->name, CPUFREQ_NAME_LEN);
+ per_cpu(cpufreq_policy_save, cpu).min = policy->user_policy.min;
+ per_cpu(cpufreq_policy_save, cpu).max = policy->user_policy.max;
pr_debug("Saving CPU%d user policy min %d and max %d\n",
- cpu, data->user_policy.min, data->user_policy.max);
+ cpu, policy->user_policy.min, policy->user_policy.max);
#endif
- WARN_ON(lock_policy_rwsem_write(cpu));
- cpus = cpumask_weight(data->cpus);
-
- if (cpus > 1)
- cpumask_clear_cpu(cpu, data->cpus);
- unlock_policy_rwsem_write(cpu);
+ down_read(&policy->rwsem);
+ cpus = cpumask_weight(policy->cpus);
+ up_read(&policy->rwsem);
- if (cpu != data->cpu) {
- sysfs_remove_link(&dev->kobj, "cpufreq");
+ if (cpu != policy->cpu) {
+ if (!frozen)
+ sysfs_remove_link(&dev->kobj, "cpufreq");
} else if (cpus > 1) {
- /* first sibling now owns the new sysfs dir */
- cpu_dev = get_cpu_device(cpumask_first(data->cpus));
- sysfs_remove_link(&cpu_dev->kobj, "cpufreq");
- ret = kobject_move(&data->kobj, &cpu_dev->kobj);
- if (ret) {
- pr_err("%s: Failed to move kobj: %d", __func__, ret);
+ new_cpu = cpufreq_nominate_new_policy_cpu(policy, cpu, frozen);
+ if (new_cpu >= 0) {
+ update_policy_cpu(policy, new_cpu);
- WARN_ON(lock_policy_rwsem_write(cpu));
- cpumask_set_cpu(cpu, data->cpus);
+ if (!frozen) {
+ pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
+ __func__, new_cpu, cpu);
+ }
+ }
+ }
- write_lock_irqsave(&cpufreq_driver_lock, flags);
- per_cpu(cpufreq_cpu_data, cpu) = data;
- write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ return 0;
+}
- unlock_policy_rwsem_write(cpu);
+static int __cpufreq_remove_dev_finish(struct device *dev,
+ struct subsys_interface *sif,
+ bool frozen)
+{
+ unsigned int cpu = dev->id, cpus;
+ int ret;
+ unsigned long flags;
+ struct cpufreq_policy *policy;
+ struct kobject *kobj;
+ struct completion *cmp;
- ret = sysfs_create_link(&cpu_dev->kobj, &data->kobj,
- "cpufreq");
- return -EINVAL;
- }
+ read_lock_irqsave(&cpufreq_driver_lock, flags);
+ policy = per_cpu(cpufreq_cpu_data, cpu);
+ read_unlock_irqrestore(&cpufreq_driver_lock, flags);
- WARN_ON(lock_policy_rwsem_write(cpu));
- update_policy_cpu(data, cpu_dev->id);
- unlock_policy_rwsem_write(cpu);
- pr_debug("%s: policy Kobject moved to cpu: %d from: %d\n",
- __func__, cpu_dev->id, cpu);
+ if (!policy) {
+ pr_debug("%s: No cpu_data found\n", __func__);
+ return -EINVAL;
}
+ down_write(&policy->rwsem);
+ cpus = cpumask_weight(policy->cpus);
+
+ if (cpus > 1)
+ cpumask_clear_cpu(cpu, policy->cpus);
+ up_write(&policy->rwsem);
+
/* If cpu is last user of policy, free policy */
if (cpus == 1) {
- if (cpufreq_driver->target)
- __cpufreq_governor(data, CPUFREQ_GOV_POLICY_EXIT);
-
- lock_policy_rwsem_read(cpu);
- kobj = &data->kobj;
- cmp = &data->kobj_unregister;
- unlock_policy_rwsem_read(cpu);
- kobject_put(kobj);
-
- /* we need to make sure that the underlying kobj is actually
- * not referenced anymore by anybody before we proceed with
- * unloading.
- */
- pr_debug("waiting for dropping of refcount\n");
- wait_for_completion(cmp);
- pr_debug("wait complete\n");
+ if (has_target()) {
+ ret = __cpufreq_governor(policy,
+ CPUFREQ_GOV_POLICY_EXIT);
+ if (ret) {
+ pr_err("%s: Failed to exit governor\n",
+ __func__);
+ return ret;
+ }
+ }
+ if (!frozen) {
+ down_read(&policy->rwsem);
+ kobj = &policy->kobj;
+ cmp = &policy->kobj_unregister;
+ up_read(&policy->rwsem);
+ kobject_put(kobj);
+
+ /*
+ * We need to make sure that the underlying kobj is
+ * actually not referenced anymore by anybody before we
+ * proceed with unloading.
+ */
+ pr_debug("waiting for dropping of refcount\n");
+ wait_for_completion(cmp);
+ pr_debug("wait complete\n");
+ }
+
+ /*
+ * Perform the ->exit() even during light-weight tear-down,
+ * since this is a core component, and is essential for the
+ * subsequent light-weight ->init() to succeed.
+ */
if (cpufreq_driver->exit)
- cpufreq_driver->exit(data);
+ cpufreq_driver->exit(policy);
- free_cpumask_var(data->related_cpus);
- free_cpumask_var(data->cpus);
- kfree(data);
+ /* Remove policy from list of active policies */
+ write_lock_irqsave(&cpufreq_driver_lock, flags);
+ list_del(&policy->policy_list);
+ write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+
+ if (!frozen)
+ cpufreq_policy_free(policy);
} else {
- pr_debug("%s: removing link, cpu: %d\n", __func__, cpu);
- cpufreq_cpu_put(data);
- if (cpufreq_driver->target) {
- __cpufreq_governor(data, CPUFREQ_GOV_START);
- __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
+ if (has_target()) {
+ if ((ret = __cpufreq_governor(policy, CPUFREQ_GOV_START)) ||
+ (ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS))) {
+ pr_err("%s: Failed to start governor\n",
+ __func__);
+ return ret;
+ }
}
}
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
+ per_cpu(cpufreq_cpu_data, cpu) = NULL;
return 0;
}
-
+/**
+ * cpufreq_remove_dev - remove a CPU device
+ *
+ * Removes the cpufreq interface for a CPU device.
+ */
static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif)
{
unsigned int cpu = dev->id;
- int retval;
+ int ret;
if (cpu_is_offline(cpu))
return 0;
- retval = __cpufreq_remove_dev(dev, sif);
- return retval;
-}
+ ret = __cpufreq_remove_dev_prepare(dev, sif, false);
+ if (!ret)
+ ret = __cpufreq_remove_dev_finish(dev, sif, false);
+
+ return ret;
+}
static void handle_update(struct work_struct *work)
{
@@ -1167,7 +1356,8 @@ static void handle_update(struct work_struct *work)
}
/**
- * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're in deep trouble.
+ * cpufreq_out_of_sync - If actual and saved CPU frequency differs, we're
+ * in deep trouble.
* @cpu: cpu number
* @old_freq: CPU frequency the kernel thinks the CPU runs at
* @new_freq: CPU frequency the CPU actually runs at
@@ -1182,7 +1372,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
struct cpufreq_freqs freqs;
unsigned long flags;
-
pr_debug("Warning: CPU frequency out of sync: cpufreq and timing "
"core thinks of %u, is %u kHz.\n", old_freq, new_freq);
@@ -1197,7 +1386,6 @@ static void cpufreq_out_of_sync(unsigned int cpu, unsigned int old_freq,
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
}
-
/**
* cpufreq_quick_get - get the CPU frequency (in kHz) from policy->cur
* @cpu: CPU number
@@ -1243,7 +1431,6 @@ unsigned int cpufreq_quick_get_max(unsigned int cpu)
}
EXPORT_SYMBOL(cpufreq_quick_get_max);
-
static unsigned int __cpufreq_get(unsigned int cpu)
{
struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
@@ -1275,22 +1462,24 @@ static unsigned int __cpufreq_get(unsigned int cpu)
*/
unsigned int cpufreq_get(unsigned int cpu)
{
+ struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu);
unsigned int ret_freq = 0;
- struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
- if (!policy)
- goto out;
+ if (cpufreq_disabled() || !cpufreq_driver)
+ return -ENOENT;
- if (unlikely(lock_policy_rwsem_read(cpu)))
- goto out_policy;
+ BUG_ON(!policy);
+
+ if (!down_read_trylock(&cpufreq_rwsem))
+ return 0;
+
+ down_read(&policy->rwsem);
ret_freq = __cpufreq_get(cpu);
- unlock_policy_rwsem_read(cpu);
+ up_read(&policy->rwsem);
+ up_read(&cpufreq_rwsem);
-out_policy:
- cpufreq_cpu_put(policy);
-out:
return ret_freq;
}
EXPORT_SYMBOL(cpufreq_get);
@@ -1302,7 +1491,6 @@ static struct subsys_interface cpufreq_interface = {
.remove_dev = cpufreq_remove_dev,
};
-
/**
* cpufreq_bp_suspend - Prepare the boot CPU for system suspend.
*
@@ -1314,23 +1502,23 @@ static int cpufreq_bp_suspend(void)
int ret = 0;
int cpu = smp_processor_id();
- struct cpufreq_policy *cpu_policy;
+ struct cpufreq_policy *policy;
pr_debug("suspending cpu %u\n", cpu);
/* If there's no policy for the boot CPU, we have nothing to do. */
- cpu_policy = cpufreq_cpu_get(cpu);
- if (!cpu_policy)
+ policy = cpufreq_cpu_get(cpu);
+ if (!policy)
return 0;
if (cpufreq_driver->suspend) {
- ret = cpufreq_driver->suspend(cpu_policy);
+ ret = cpufreq_driver->suspend(policy);
if (ret)
printk(KERN_ERR "cpufreq: suspend failed in ->suspend "
- "step on CPU %u\n", cpu_policy->cpu);
+ "step on CPU %u\n", policy->cpu);
}
- cpufreq_cpu_put(cpu_policy);
+ cpufreq_cpu_put(policy);
return ret;
}
@@ -1352,28 +1540,28 @@ static void cpufreq_bp_resume(void)
int ret = 0;
int cpu = smp_processor_id();
- struct cpufreq_policy *cpu_policy;
+ struct cpufreq_policy *policy;
pr_debug("resuming cpu %u\n", cpu);
/* If there's no policy for the boot CPU, we have nothing to do. */
- cpu_policy = cpufreq_cpu_get(cpu);
- if (!cpu_policy)
+ policy = cpufreq_cpu_get(cpu);
+ if (!policy)
return;
if (cpufreq_driver->resume) {
- ret = cpufreq_driver->resume(cpu_policy);
+ ret = cpufreq_driver->resume(policy);
if (ret) {
printk(KERN_ERR "cpufreq: resume failed in ->resume "
- "step on CPU %u\n", cpu_policy->cpu);
+ "step on CPU %u\n", policy->cpu);
goto fail;
}
}
- schedule_work(&cpu_policy->update);
+ schedule_work(&policy->update);
fail:
- cpufreq_cpu_put(cpu_policy);
+ cpufreq_cpu_put(policy);
}
static struct syscore_ops cpufreq_syscore_ops = {
@@ -1439,11 +1627,10 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
}
EXPORT_SYMBOL(cpufreq_register_notifier);
-
/**
* cpufreq_unregister_notifier - unregister a driver with cpufreq
* @nb: notifier block to be unregistered
- * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
+ * @list: CPUFREQ_TRANSITION_NOTIFIER or CPUFREQ_POLICY_NOTIFIER
*
* Remove a driver from the CPU frequency notifier list.
*
@@ -1479,7 +1666,6 @@ EXPORT_SYMBOL(cpufreq_unregister_notifier);
* GOVERNORS *
*********************************************************************/
-
int __cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
@@ -1499,12 +1685,75 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n",
policy->cpu, target_freq, relation, old_target_freq);
+ /*
+ * This might look like a redundant call as we are checking it again
+ * after finding index. But it is left intentionally for cases where
+ * exactly same freq is called again and so we can save on few function
+ * calls.
+ */
if (target_freq == policy->cur)
return 0;
if (cpufreq_driver->target)
retval = cpufreq_driver->target(policy, target_freq, relation);
+ else if (cpufreq_driver->target_index) {
+ struct cpufreq_frequency_table *freq_table;
+ struct cpufreq_freqs freqs;
+ bool notify;
+ int index;
+
+ freq_table = cpufreq_frequency_get_table(policy->cpu);
+ if (unlikely(!freq_table)) {
+ pr_err("%s: Unable to find freq_table\n", __func__);
+ goto out;
+ }
+
+ retval = cpufreq_frequency_table_target(policy, freq_table,
+ target_freq, relation, &index);
+ if (unlikely(retval)) {
+ pr_err("%s: Unable to find matching freq\n", __func__);
+ goto out;
+ }
+
+ if (freq_table[index].frequency == policy->cur) {
+ retval = 0;
+ goto out;
+ }
+
+ notify = !(cpufreq_driver->flags & CPUFREQ_ASYNC_NOTIFICATION);
+
+ if (notify) {
+ freqs.old = policy->cur;
+ freqs.new = freq_table[index].frequency;
+ freqs.flags = 0;
+
+ pr_debug("%s: cpu: %d, oldfreq: %u, new freq: %u\n",
+ __func__, policy->cpu, freqs.old,
+ freqs.new);
+ cpufreq_notify_transition(policy, &freqs,
+ CPUFREQ_PRECHANGE);
+ }
+
+ retval = cpufreq_driver->target_index(policy, index);
+ if (retval)
+ pr_err("%s: Failed to change cpu frequency: %d\n",
+ __func__, retval);
+
+ if (notify) {
+ /*
+ * Notify with old freq in case we failed to change
+ * frequency
+ */
+ if (retval)
+ freqs.new = freqs.old;
+
+ cpufreq_notify_transition(policy, &freqs,
+ CPUFREQ_POSTCHANGE);
+ }
+ }
+
+out:
return retval;
}
EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
@@ -1515,45 +1764,16 @@ int cpufreq_driver_target(struct cpufreq_policy *policy,
{
int ret = -EINVAL;
- policy = cpufreq_cpu_get(policy->cpu);
- if (!policy)
- goto no_policy;
-
- if (unlikely(lock_policy_rwsem_write(policy->cpu)))
- goto fail;
+ down_write(&policy->rwsem);
ret = __cpufreq_driver_target(policy, target_freq, relation);
- unlock_policy_rwsem_write(policy->cpu);
+ up_write(&policy->rwsem);
-fail:
- cpufreq_cpu_put(policy);
-no_policy:
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_driver_target);
-int __cpufreq_driver_getavg(struct cpufreq_policy *policy, unsigned int cpu)
-{
- int ret = 0;
-
- if (cpufreq_disabled())
- return ret;
-
- if (!cpufreq_driver->getavg)
- return 0;
-
- policy = cpufreq_cpu_get(policy->cpu);
- if (!policy)
- return -EINVAL;
-
- ret = cpufreq_driver->getavg(policy, cpu);
-
- cpufreq_cpu_put(policy);
- return ret;
-}
-EXPORT_SYMBOL_GPL(__cpufreq_driver_getavg);
-
/*
* when "event" is CPUFREQ_GOV_LIMITS
*/
@@ -1588,15 +1808,17 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
}
}
- if (!try_module_get(policy->governor->owner))
- return -EINVAL;
+ if (event == CPUFREQ_GOV_POLICY_INIT)
+ if (!try_module_get(policy->governor->owner))
+ return -EINVAL;
pr_debug("__cpufreq_governor for CPU %u, event %u\n",
policy->cpu, event);
mutex_lock(&cpufreq_governor_lock);
- if ((!policy->governor_enabled && (event == CPUFREQ_GOV_STOP)) ||
- (policy->governor_enabled && (event == CPUFREQ_GOV_START))) {
+ if ((policy->governor_enabled && event == CPUFREQ_GOV_START)
+ || (!policy->governor_enabled
+ && (event == CPUFREQ_GOV_LIMITS || event == CPUFREQ_GOV_STOP))) {
mutex_unlock(&cpufreq_governor_lock);
return -EBUSY;
}
@@ -1625,17 +1847,13 @@ static int __cpufreq_governor(struct cpufreq_policy *policy,
mutex_unlock(&cpufreq_governor_lock);
}
- /* we keep one module reference alive for
- each CPU governed by this CPU */
- if ((event != CPUFREQ_GOV_START) || ret)
- module_put(policy->governor->owner);
- if ((event == CPUFREQ_GOV_STOP) && !ret)
+ if (((event == CPUFREQ_GOV_POLICY_INIT) && ret) ||
+ ((event == CPUFREQ_GOV_POLICY_EXIT) && !ret))
module_put(policy->governor->owner);
return ret;
}
-
int cpufreq_register_governor(struct cpufreq_governor *governor)
{
int err;
@@ -1660,7 +1878,6 @@ int cpufreq_register_governor(struct cpufreq_governor *governor)
}
EXPORT_SYMBOL_GPL(cpufreq_register_governor);
-
void cpufreq_unregister_governor(struct cpufreq_governor *governor)
{
#ifdef CONFIG_HOTPLUG_CPU
@@ -1692,7 +1909,6 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
-
/*********************************************************************
* POLICY INTERFACE *
*********************************************************************/
@@ -1714,105 +1930,105 @@ int cpufreq_get_policy(struct cpufreq_policy *policy, unsigned int cpu)
if (!cpu_policy)
return -EINVAL;
- memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy));
+ memcpy(policy, cpu_policy, sizeof(*policy));
cpufreq_cpu_put(cpu_policy);
return 0;
}
EXPORT_SYMBOL(cpufreq_get_policy);
-
/*
- * data : current policy.
- * policy : policy to be set.
+ * policy : current policy.
+ * new_policy: policy to be set.
*/
-static int __cpufreq_set_policy(struct cpufreq_policy *data,
- struct cpufreq_policy *policy)
+static int cpufreq_set_policy(struct cpufreq_policy *policy,
+ struct cpufreq_policy *new_policy)
{
int ret = 0, failed = 1;
- pr_debug("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
- policy->min, policy->max);
+ pr_debug("setting new policy for CPU %u: %u - %u kHz\n", new_policy->cpu,
+ new_policy->min, new_policy->max);
- memcpy(&policy->cpuinfo, &data->cpuinfo,
- sizeof(struct cpufreq_cpuinfo));
+ memcpy(&new_policy->cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo));
- if (policy->min > data->max || policy->max < data->min) {
+ if (new_policy->min > policy->max || new_policy->max < policy->min) {
ret = -EINVAL;
goto error_out;
}
/* verify the cpu speed can be set within this limit */
- ret = cpufreq_driver->verify(policy);
+ ret = cpufreq_driver->verify(new_policy);
if (ret)
goto error_out;
/* adjust if necessary - all reasons */
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_ADJUST, policy);
+ CPUFREQ_ADJUST, new_policy);
/* adjust if necessary - hardware incompatibility*/
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_INCOMPATIBLE, policy);
+ CPUFREQ_INCOMPATIBLE, new_policy);
- /* verify the cpu speed can be set within this limit,
- which might be different to the first one */
- ret = cpufreq_driver->verify(policy);
+ /*
+ * verify the cpu speed can be set within this limit, which might be
+ * different to the first one
+ */
+ ret = cpufreq_driver->verify(new_policy);
if (ret)
goto error_out;
/* notification of the new policy */
blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
- CPUFREQ_NOTIFY, policy);
+ CPUFREQ_NOTIFY, new_policy);
- data->min = policy->min;
- data->max = policy->max;
+ policy->min = new_policy->min;
+ policy->max = new_policy->max;
pr_debug("new min and max freqs are %u - %u kHz\n",
- data->min, data->max);
+ policy->min, policy->max);
if (cpufreq_driver->setpolicy) {
- data->policy = policy->policy;
+ policy->policy = new_policy->policy;
pr_debug("setting range\n");
- ret = cpufreq_driver->setpolicy(policy);
+ ret = cpufreq_driver->setpolicy(new_policy);
} else {
- if (policy->governor != data->governor) {
+ if (new_policy->governor != policy->governor) {
/* save old, working values */
- struct cpufreq_governor *old_gov = data->governor;
+ struct cpufreq_governor *old_gov = policy->governor;
pr_debug("governor switch\n");
/* end old governor */
- if (data->governor) {
- __cpufreq_governor(data, CPUFREQ_GOV_STOP);
- unlock_policy_rwsem_write(policy->cpu);
- __cpufreq_governor(data,
+ if (policy->governor) {
+ __cpufreq_governor(policy, CPUFREQ_GOV_STOP);
+ up_write(&policy->rwsem);
+ __cpufreq_governor(policy,
CPUFREQ_GOV_POLICY_EXIT);
- lock_policy_rwsem_write(policy->cpu);
+ down_write(&policy->rwsem);
}
/* start new governor */
- data->governor = policy->governor;
- if (!__cpufreq_governor(data, CPUFREQ_GOV_POLICY_INIT)) {
- if (!__cpufreq_governor(data, CPUFREQ_GOV_START)) {
+ policy->governor = new_policy->governor;
+ if (!__cpufreq_governor(policy, CPUFREQ_GOV_POLICY_INIT)) {
+ if (!__cpufreq_governor(policy, CPUFREQ_GOV_START)) {
failed = 0;
} else {
- unlock_policy_rwsem_write(policy->cpu);
- __cpufreq_governor(data,
+ up_write(&policy->rwsem);
+ __cpufreq_governor(policy,
CPUFREQ_GOV_POLICY_EXIT);
- lock_policy_rwsem_write(policy->cpu);
+ down_write(&policy->rwsem);
}
}
if (failed) {
/* new governor failed, so re-start old one */
pr_debug("starting governor %s failed\n",
- data->governor->name);
+ policy->governor->name);
if (old_gov) {
- data->governor = old_gov;
- __cpufreq_governor(data,
+ policy->governor = old_gov;
+ __cpufreq_governor(policy,
CPUFREQ_GOV_POLICY_INIT);
- __cpufreq_governor(data,
+ __cpufreq_governor(policy,
CPUFREQ_GOV_START);
}
ret = -EINVAL;
@@ -1821,7 +2037,7 @@ static int __cpufreq_set_policy(struct cpufreq_policy *data,
/* might be a policy change, too, so fall through */
}
pr_debug("governor: change or update limits\n");
- __cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
+ ret = __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS);
}
error_out:
@@ -1837,72 +2053,79 @@ error_out:
*/
int cpufreq_update_policy(unsigned int cpu)
{
- struct cpufreq_policy *data = cpufreq_cpu_get(cpu);
- struct cpufreq_policy policy;
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ struct cpufreq_policy new_policy;
int ret;
- if (!data) {
+ if (!policy) {
ret = -ENODEV;
goto no_policy;
}
- if (unlikely(lock_policy_rwsem_write(cpu))) {
- ret = -EINVAL;
- goto fail;
- }
+ down_write(&policy->rwsem);
pr_debug("updating policy for CPU %u\n", cpu);
- memcpy(&policy, data, sizeof(struct cpufreq_policy));
- policy.min = data->user_policy.min;
- policy.max = data->user_policy.max;
- policy.policy = data->user_policy.policy;
- policy.governor = data->user_policy.governor;
-
- /* BIOS might change freq behind our back
- -> ask driver for current freq and notify governors about a change */
+ memcpy(&new_policy, policy, sizeof(*policy));
+ new_policy.min = policy->user_policy.min;
+ new_policy.max = policy->user_policy.max;
+ new_policy.policy = policy->user_policy.policy;
+ new_policy.governor = policy->user_policy.governor;
+
+ /*
+ * BIOS might change freq behind our back
+ * -> ask driver for current freq and notify governors about a change
+ */
if (cpufreq_driver->get) {
- policy.cur = cpufreq_driver->get(cpu);
- if (!data->cur) {
+ new_policy.cur = cpufreq_driver->get(cpu);
+ if (!policy->cur) {
pr_debug("Driver did not initialize current freq");
- data->cur = policy.cur;
+ policy->cur = new_policy.cur;
} else {
- if (data->cur != policy.cur && cpufreq_driver->target)
- cpufreq_out_of_sync(cpu, data->cur,
- policy.cur);
+ if (policy->cur != new_policy.cur && has_target())
+ cpufreq_out_of_sync(cpu, policy->cur,
+ new_policy.cur);
}
}
- ret = __cpufreq_set_policy(data, &policy);
+ ret = cpufreq_set_policy(policy, &new_policy);
- unlock_policy_rwsem_write(cpu);
+ up_write(&policy->rwsem);
-fail:
- cpufreq_cpu_put(data);
+ cpufreq_cpu_put(policy);
no_policy:
return ret;
}
EXPORT_SYMBOL(cpufreq_update_policy);
-static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb,
+static int cpufreq_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
struct device *dev;
+ bool frozen = false;
dev = get_cpu_device(cpu);
if (dev) {
- switch (action) {
+
+ if (action & CPU_TASKS_FROZEN)
+ frozen = true;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpufreq_add_dev(dev, NULL);
+ __cpufreq_add_dev(dev, NULL, frozen);
+ cpufreq_update_policy(cpu);
break;
+
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- __cpufreq_remove_dev(dev, NULL);
+ __cpufreq_remove_dev_prepare(dev, NULL, frozen);
break;
+
+ case CPU_POST_DEAD:
+ __cpufreq_remove_dev_finish(dev, NULL, frozen);
+ break;
+
case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
- cpufreq_add_dev(dev, NULL);
+ __cpufreq_add_dev(dev, NULL, frozen);
break;
}
}
@@ -1910,7 +2133,7 @@ static int __cpuinit cpufreq_cpu_callback(struct notifier_block *nfb,
}
static struct notifier_block __refdata cpufreq_cpu_notifier = {
- .notifier_call = cpufreq_cpu_callback,
+ .notifier_call = cpufreq_cpu_callback,
};
/*********************************************************************
@@ -1922,7 +2145,7 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = {
* @driver_data: A struct cpufreq_driver containing the values#
* submitted by the CPU Frequency driver.
*
- * Registers a CPU Frequency driver to this core code. This code
+ * Registers a CPU Frequency driver to this core code. This code
* returns zero on success, -EBUSY when another driver got here first
* (and isn't unregistered in the meantime).
*
@@ -1936,7 +2159,8 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
return -ENODEV;
if (!driver_data || !driver_data->verify || !driver_data->init ||
- ((!driver_data->setpolicy) && (!driver_data->target)))
+ !(driver_data->setpolicy || driver_data->target_index ||
+ driver_data->target))
return -EINVAL;
pr_debug("trying to register driver %s\n", driver_data->name);
@@ -1947,7 +2171,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
write_lock_irqsave(&cpufreq_driver_lock, flags);
if (cpufreq_driver) {
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
- return -EBUSY;
+ return -EEXIST;
}
cpufreq_driver = driver_data;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
@@ -1989,11 +2213,10 @@ err_null_driver:
}
EXPORT_SYMBOL_GPL(cpufreq_register_driver);
-
/**
* cpufreq_unregister_driver - unregister the current CPUFreq driver
*
- * Unregister the current CPUFreq driver. Only call this if you have
+ * Unregister the current CPUFreq driver. Only call this if you have
* the right to do so, i.e. if you have succeeded in initialising before!
* Returns zero if successful, and -EINVAL if the cpufreq_driver is
* currently not initialised.
@@ -2010,9 +2233,13 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
subsys_interface_unregister(&cpufreq_interface);
unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
+ down_write(&cpufreq_rwsem);
write_lock_irqsave(&cpufreq_driver_lock, flags);
+
cpufreq_driver = NULL;
+
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
+ up_write(&cpufreq_rwsem);
return 0;
}
@@ -2020,17 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_unregister_driver);
static int __init cpufreq_core_init(void)
{
- int cpu;
-
if (cpufreq_disabled())
return -ENODEV;
- for_each_possible_cpu(cpu) {
- per_cpu(cpufreq_policy_cpu, cpu) = -1;
- init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
- }
-
- cpufreq_global_kobject = kobject_create_and_add("cpufreq", &cpu_subsys.dev_root->kobj);
+ cpufreq_global_kobject = kobject_create();
BUG_ON(!cpufreq_global_kobject);
register_syscore_ops(&cpufreq_syscore_ops);
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 0ceb2eff5a7e..25a70d06c5bf 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -11,19 +11,7 @@
* published by the Free Software Foundation.
*/
-#include <linux/cpufreq.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/kernel_stat.h>
-#include <linux/kobject.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/notifier.h>
-#include <linux/percpu-defs.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
-#include <linux/types.h>
-
#include "cpufreq_governor.h"
/* Conservative governor macros */
@@ -79,6 +67,7 @@ static void cs_check_cpu(int cpu, unsigned int load)
return;
dbs_info->requested_freq += get_freq_target(cs_tuners, policy);
+
if (dbs_info->requested_freq > policy->max)
dbs_info->requested_freq = policy->max;
@@ -94,14 +83,17 @@ static void cs_check_cpu(int cpu, unsigned int load)
/* Check for frequency decrease */
if (load < cs_tuners->down_threshold) {
+ unsigned int freq_target;
/*
* if we cannot reduce the frequency anymore, break out early
*/
if (policy->cur == policy->min)
return;
- dbs_info->requested_freq -= get_freq_target(cs_tuners, policy);
- if (dbs_info->requested_freq < policy->min)
+ freq_target = get_freq_target(cs_tuners, policy);
+ if (dbs_info->requested_freq > freq_target)
+ dbs_info->requested_freq -= freq_target;
+ else
dbs_info->requested_freq = policy->min;
__cpufreq_driver_target(policy, dbs_info->requested_freq,
@@ -221,8 +213,8 @@ static ssize_t store_down_threshold(struct dbs_data *dbs_data, const char *buf,
return count;
}
-static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
- size_t count)
+static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data,
+ const char *buf, size_t count)
{
struct cs_dbs_tuners *cs_tuners = dbs_data->tuners;
unsigned int input, j;
@@ -235,10 +227,10 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
if (input > 1)
input = 1;
- if (input == cs_tuners->ignore_nice) /* nothing to do */
+ if (input == cs_tuners->ignore_nice_load) /* nothing to do */
return count;
- cs_tuners->ignore_nice = input;
+ cs_tuners->ignore_nice_load = input;
/* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
@@ -246,7 +238,7 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
dbs_info = &per_cpu(cs_cpu_dbs_info, j);
dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j,
&dbs_info->cdbs.prev_cpu_wall, 0);
- if (cs_tuners->ignore_nice)
+ if (cs_tuners->ignore_nice_load)
dbs_info->cdbs.prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
}
@@ -279,7 +271,7 @@ show_store_one(cs, sampling_rate);
show_store_one(cs, sampling_down_factor);
show_store_one(cs, up_threshold);
show_store_one(cs, down_threshold);
-show_store_one(cs, ignore_nice);
+show_store_one(cs, ignore_nice_load);
show_store_one(cs, freq_step);
declare_show_sampling_rate_min(cs);
@@ -287,7 +279,7 @@ gov_sys_pol_attr_rw(sampling_rate);
gov_sys_pol_attr_rw(sampling_down_factor);
gov_sys_pol_attr_rw(up_threshold);
gov_sys_pol_attr_rw(down_threshold);
-gov_sys_pol_attr_rw(ignore_nice);
+gov_sys_pol_attr_rw(ignore_nice_load);
gov_sys_pol_attr_rw(freq_step);
gov_sys_pol_attr_ro(sampling_rate_min);
@@ -297,7 +289,7 @@ static struct attribute *dbs_attributes_gov_sys[] = {
&sampling_down_factor_gov_sys.attr,
&up_threshold_gov_sys.attr,
&down_threshold_gov_sys.attr,
- &ignore_nice_gov_sys.attr,
+ &ignore_nice_load_gov_sys.attr,
&freq_step_gov_sys.attr,
NULL
};
@@ -313,7 +305,7 @@ static struct attribute *dbs_attributes_gov_pol[] = {
&sampling_down_factor_gov_pol.attr,
&up_threshold_gov_pol.attr,
&down_threshold_gov_pol.attr,
- &ignore_nice_gov_pol.attr,
+ &ignore_nice_load_gov_pol.attr,
&freq_step_gov_pol.attr,
NULL
};
@@ -329,7 +321,7 @@ static int cs_init(struct dbs_data *dbs_data)
{
struct cs_dbs_tuners *tuners;
- tuners = kzalloc(sizeof(struct cs_dbs_tuners), GFP_KERNEL);
+ tuners = kzalloc(sizeof(*tuners), GFP_KERNEL);
if (!tuners) {
pr_err("%s: kzalloc failed\n", __func__);
return -ENOMEM;
@@ -338,7 +330,7 @@ static int cs_init(struct dbs_data *dbs_data)
tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD;
tuners->down_threshold = DEF_FREQUENCY_DOWN_THRESHOLD;
tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR;
- tuners->ignore_nice = 0;
+ tuners->ignore_nice_load = 0;
tuners->freq_step = DEF_FREQUENCY_STEP;
dbs_data->tuners = tuners;
diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c
index ca21101a4fed..e6be63561fa6 100644
--- a/drivers/cpufreq/cpufreq_governor.c
+++ b/drivers/cpufreq/cpufreq_governor.c
@@ -16,27 +16,12 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <asm/cputime.h>
-#include <linux/cpufreq.h>
-#include <linux/cpumask.h>
#include <linux/export.h>
#include <linux/kernel_stat.h>
-#include <linux/mutex.h>
#include <linux/slab.h>
-#include <linux/tick.h>
-#include <linux/types.h>
-#include <linux/workqueue.h>
#include "cpufreq_governor.h"
-static struct kobject *get_governor_parent_kobj(struct cpufreq_policy *policy)
-{
- if (have_governor_per_policy())
- return &policy->kobj;
- else
- return cpufreq_global_kobject;
-}
-
static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data)
{
if (have_governor_per_policy())
@@ -45,41 +30,6 @@ static struct attribute_group *get_sysfs_attr(struct dbs_data *dbs_data)
return dbs_data->cdata->attr_group_gov_sys;
}
-static inline u64 get_cpu_idle_time_jiffy(unsigned int cpu, u64 *wall)
-{
- u64 idle_time;
- u64 cur_wall_time;
- u64 busy_time;
-
- cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
-
- busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
-
- idle_time = cur_wall_time - busy_time;
- if (wall)
- *wall = cputime_to_usecs(cur_wall_time);
-
- return cputime_to_usecs(idle_time);
-}
-
-u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy)
-{
- u64 idle_time = get_cpu_idle_time_us(cpu, io_busy ? wall : NULL);
-
- if (idle_time == -1ULL)
- return get_cpu_idle_time_jiffy(cpu, wall);
- else if (!io_busy)
- idle_time += get_cpu_iowait_time_us(cpu, wall);
-
- return idle_time;
-}
-EXPORT_SYMBOL_GPL(get_cpu_idle_time);
-
void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
{
struct cpu_dbs_common_info *cdbs = dbs_data->cdata->get_cpu_cdbs(cpu);
@@ -91,13 +41,13 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
unsigned int j;
if (dbs_data->cdata->governor == GOV_ONDEMAND)
- ignore_nice = od_tuners->ignore_nice;
+ ignore_nice = od_tuners->ignore_nice_load;
else
- ignore_nice = cs_tuners->ignore_nice;
+ ignore_nice = cs_tuners->ignore_nice_load;
policy = cdbs->cur_policy;
- /* Get Absolute Load (in terms of freq for ondemand gov) */
+ /* Get Absolute Load */
for_each_cpu(j, policy->cpus) {
struct cpu_dbs_common_info *j_cdbs;
u64 cur_wall_time, cur_idle_time;
@@ -148,14 +98,6 @@ void dbs_check_cpu(struct dbs_data *dbs_data, int cpu)
load = 100 * (wall_time - idle_time) / wall_time;
- if (dbs_data->cdata->governor == GOV_ONDEMAND) {
- int freq_avg = __cpufreq_driver_getavg(policy, j);
- if (freq_avg <= 0)
- freq_avg = policy->cur;
-
- load *= freq_avg;
- }
-
if (load > max_load)
max_load = load;
}
@@ -285,6 +227,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
return rc;
}
+ if (!have_governor_per_policy())
+ WARN_ON(cpufreq_get_global_kobject());
+
rc = sysfs_create_group(get_governor_parent_kobj(policy),
get_sysfs_attr(dbs_data));
if (rc) {
@@ -295,7 +240,7 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
policy->governor_data = dbs_data;
- /* policy latency is in nS. Convert it to uS first */
+ /* policy latency is in ns. Convert it to us first */
latency = policy->cpuinfo.transition_latency / 1000;
if (latency == 0)
latency = 1;
@@ -323,6 +268,9 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
sysfs_remove_group(get_governor_parent_kobj(policy),
get_sysfs_attr(dbs_data));
+ if (!have_governor_per_policy())
+ cpufreq_put_global_kobject();
+
if ((dbs_data->cdata->governor == GOV_CONSERVATIVE) &&
(policy->governor->initialized == 1)) {
struct cs_ops *cs_ops = dbs_data->cdata->gov_ops;
@@ -346,12 +294,12 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
cs_tuners = dbs_data->tuners;
cs_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
sampling_rate = cs_tuners->sampling_rate;
- ignore_nice = cs_tuners->ignore_nice;
+ ignore_nice = cs_tuners->ignore_nice_load;
} else {
od_tuners = dbs_data->tuners;
od_dbs_info = dbs_data->cdata->get_cpu_dbs_info_s(cpu);
sampling_rate = od_tuners->sampling_rate;
- ignore_nice = od_tuners->ignore_nice;
+ ignore_nice = od_tuners->ignore_nice_load;
od_ops = dbs_data->cdata->gov_ops;
io_busy = od_tuners->io_is_busy;
}
@@ -380,10 +328,6 @@ int cpufreq_governor_dbs(struct cpufreq_policy *policy,
dbs_data->cdata->gov_dbs_timer);
}
- /*
- * conservative does not implement micro like ondemand
- * governor, thus we are bound to jiffes/HZ
- */
if (dbs_data->cdata->governor == GOV_CONSERVATIVE) {
cs_dbs_info->down_skip = 0;
cs_dbs_info->enable = 1;
diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h
index e16a96130cb3..b5f2b8618949 100644
--- a/drivers/cpufreq/cpufreq_governor.h
+++ b/drivers/cpufreq/cpufreq_governor.h
@@ -18,19 +18,18 @@
#define _CPUFREQ_GOVERNOR_H
#include <linux/cpufreq.h>
-#include <linux/kobject.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/workqueue.h>
-#include <linux/sysfs.h>
/*
* The polling frequency depends on the capability of the processor. Default
* polling frequency is 1000 times the transition latency of the processor. The
- * governor will work on any processor with transition latency <= 10mS, using
+ * governor will work on any processor with transition latency <= 10ms, using
* appropriate sampling rate.
*
- * For CPUs with transition latency > 10mS (mostly drivers with CPUFREQ_ETERNAL)
- * this governor will not work. All times here are in uS.
+ * For CPUs with transition latency > 10ms (mostly drivers with CPUFREQ_ETERNAL)
+ * this governor will not work. All times here are in us (micro seconds).
*/
#define MIN_SAMPLING_RATE_RATIO (2)
#define LATENCY_MULTIPLIER (1000)
@@ -81,7 +80,7 @@ static ssize_t show_##file_name##_gov_sys \
return sprintf(buf, "%u\n", tuners->file_name); \
} \
\
-static ssize_t show_##file_name##_gov_pol \
+static ssize_t show_##file_name##_gov_pol \
(struct cpufreq_policy *policy, char *buf) \
{ \
struct dbs_data *dbs_data = policy->governor_data; \
@@ -91,7 +90,7 @@ static ssize_t show_##file_name##_gov_pol \
#define store_one(_gov, file_name) \
static ssize_t store_##file_name##_gov_sys \
-(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \
+(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) \
{ \
struct dbs_data *dbs_data = _gov##_dbs_cdata.gdbs_data; \
return store_##file_name(dbs_data, buf, count); \
@@ -163,19 +162,18 @@ struct cs_cpu_dbs_info_s {
unsigned int enable:1;
};
-/* Per policy Governers sysfs tunables */
+/* Per policy Governors sysfs tunables */
struct od_dbs_tuners {
- unsigned int ignore_nice;
+ unsigned int ignore_nice_load;
unsigned int sampling_rate;
unsigned int sampling_down_factor;
unsigned int up_threshold;
- unsigned int adj_up_threshold;
unsigned int powersave_bias;
unsigned int io_is_busy;
};
struct cs_dbs_tuners {
- unsigned int ignore_nice;
+ unsigned int ignore_nice_load;
unsigned int sampling_rate;
unsigned int sampling_down_factor;
unsigned int up_threshold;
@@ -183,7 +181,7 @@ struct cs_dbs_tuners {
unsigned int freq_step;
};
-/* Common Governer data across policies */
+/* Common Governor data across policies */
struct dbs_data;
struct common_dbs_data {
/* Common across governors */
@@ -193,7 +191,10 @@ struct common_dbs_data {
struct attribute_group *attr_group_gov_sys; /* one governor - system */
struct attribute_group *attr_group_gov_pol; /* one governor - policy */
- /* Common data for platforms that don't set have_governor_per_policy */
+ /*
+ * Common data for platforms that don't set
+ * CPUFREQ_HAVE_GOVERNOR_PER_POLICY
+ */
struct dbs_data *gdbs_data;
struct cpu_dbs_common_info *(*get_cpu_cdbs)(int cpu);
@@ -207,7 +208,7 @@ struct common_dbs_data {
void *gov_ops;
};
-/* Governer Per policy data */
+/* Governor Per policy data */
struct dbs_data {
struct common_dbs_data *cdata;
unsigned int min_sampling_rate;
@@ -223,7 +224,7 @@ struct od_ops {
void (*powersave_bias_init_cpu)(int cpu);
unsigned int (*powersave_bias_target)(struct cpufreq_policy *policy,
unsigned int freq_next, unsigned int relation);
- void (*freq_increase)(struct cpufreq_policy *p, unsigned int freq);
+ void (*freq_increase)(struct cpufreq_policy *policy, unsigned int freq);
};
struct cs_ops {
@@ -256,7 +257,6 @@ static ssize_t show_sampling_rate_min_gov_pol \
return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \
}
-u64 get_cpu_idle_time(unsigned int cpu, u64 *wall, int io_busy);
void dbs_check_cpu(struct dbs_data *dbs_data, int cpu);
bool need_load_eval(struct cpu_dbs_common_info *cdbs,
unsigned int sampling_rate);
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 8feae37183c7..e79e0cca6457 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -144,42 +144,6 @@ struct cpufreq_governor cpufreq_gov_interactive = {
.owner = THIS_MODULE,
};
-static inline cputime64_t get_cpu_idle_time_jiffy(unsigned int cpu,
- cputime64_t *wall)
-{
- u64 idle_time;
- u64 cur_wall_time;
- u64 busy_time;
-
- cur_wall_time = jiffies64_to_cputime64(get_jiffies_64());
-
- busy_time = kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_STEAL];
- busy_time += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
-
- idle_time = cur_wall_time - busy_time;
- if (wall)
- *wall = jiffies_to_usecs(cur_wall_time);
-
- return jiffies_to_usecs(idle_time);
-}
-
-static inline cputime64_t get_cpu_idle_time(unsigned int cpu,
- cputime64_t *wall)
-{
- u64 idle_time = get_cpu_idle_time_us(cpu, wall);
-
- if (idle_time == -1ULL)
- idle_time = get_cpu_idle_time_jiffy(cpu, wall);
- else if (!io_is_busy)
- idle_time += get_cpu_iowait_time_us(cpu, wall);
-
- return idle_time;
-}
-
static void cpufreq_interactive_timer_resched(
struct cpufreq_interactive_cpuinfo *pcpu)
{
@@ -189,7 +153,7 @@ static void cpufreq_interactive_timer_resched(
spin_lock_irqsave(&pcpu->load_lock, flags);
pcpu->time_in_idle =
get_cpu_idle_time(smp_processor_id(),
- &pcpu->time_in_idle_timestamp);
+ &pcpu->time_in_idle_timestamp, io_is_busy);
pcpu->cputime_speedadj = 0;
pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
expires = jiffies + usecs_to_jiffies(timer_rate);
@@ -223,7 +187,8 @@ static void cpufreq_interactive_timer_start(int cpu)
spin_lock_irqsave(&pcpu->load_lock, flags);
pcpu->time_in_idle =
- get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp);
+ get_cpu_idle_time(cpu, &pcpu->time_in_idle_timestamp,
+ io_is_busy);
pcpu->cputime_speedadj = 0;
pcpu->cputime_speedadj_timestamp = pcpu->time_in_idle_timestamp;
spin_unlock_irqrestore(&pcpu->load_lock, flags);
@@ -364,7 +329,7 @@ static u64 update_load(int cpu)
unsigned int delta_time;
u64 active_time;
- now_idle = get_cpu_idle_time(cpu, &now);
+ now_idle = get_cpu_idle_time(cpu, &now, io_is_busy);
delta_idle = (unsigned int)(now_idle - pcpu->time_in_idle);
delta_time = (unsigned int)(now - pcpu->time_in_idle_timestamp);
@@ -1234,7 +1199,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
return 0;
}
- rc = sysfs_create_group(cpufreq_global_kobject,
+ if (!have_governor_per_policy())
+ WARN_ON(cpufreq_get_global_kobject());
+
+ rc = sysfs_create_group(get_governor_parent_kobj(policy),
&interactive_attr_group);
if (rc) {
mutex_unlock(&gov_lock);
@@ -1266,8 +1234,10 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
cpufreq_unregister_notifier(
&cpufreq_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
idle_notifier_unregister(&cpufreq_interactive_idle_nb);
- sysfs_remove_group(cpufreq_global_kobject,
+ sysfs_remove_group(get_governor_parent_kobj(policy),
&interactive_attr_group);
+ if (!have_governor_per_policy())
+ cpufreq_put_global_kobject();
mutex_unlock(&gov_lock);
break;
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index 93eb5cbcc1f6..18d409189092 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -12,28 +12,16 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/cpufreq.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/kernel_stat.h>
-#include <linux/kobject.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
+#include <linux/cpu.h>
#include <linux/percpu-defs.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
#include <linux/tick.h>
-#include <linux/types.h>
-#include <linux/cpu.h>
-
#include "cpufreq_governor.h"
/* On-demand governor macros */
-#define DEF_FREQUENCY_DOWN_DIFFERENTIAL (10)
#define DEF_FREQUENCY_UP_THRESHOLD (80)
#define DEF_SAMPLING_DOWN_FACTOR (1)
#define MAX_SAMPLING_DOWN_FACTOR (100000)
-#define MICRO_FREQUENCY_DOWN_DIFFERENTIAL (3)
#define MICRO_FREQUENCY_UP_THRESHOLD (95)
#define MICRO_FREQUENCY_MIN_SAMPLE_RATE (10000)
#define MIN_FREQUENCY_UP_THRESHOLD (11)
@@ -144,31 +132,27 @@ static void ondemand_powersave_bias_init(void)
}
}
-static void dbs_freq_increase(struct cpufreq_policy *p, unsigned int freq)
+static void dbs_freq_increase(struct cpufreq_policy *policy, unsigned int freq)
{
- struct dbs_data *dbs_data = p->governor_data;
+ struct dbs_data *dbs_data = policy->governor_data;
struct od_dbs_tuners *od_tuners = dbs_data->tuners;
if (od_tuners->powersave_bias)
- freq = od_ops.powersave_bias_target(p, freq,
+ freq = od_ops.powersave_bias_target(policy, freq,
CPUFREQ_RELATION_H);
- else if (p->cur == p->max)
+ else if (policy->cur == policy->max)
return;
- __cpufreq_driver_target(p, freq, od_tuners->powersave_bias ?
+ __cpufreq_driver_target(policy, freq, od_tuners->powersave_bias ?
CPUFREQ_RELATION_L : CPUFREQ_RELATION_H);
}
/*
* Every sampling_rate, we check, if current idle time is less than 20%
- * (default), then we try to increase frequency. Every sampling_rate, we look
- * for the lowest frequency which can sustain the load while keeping idle time
- * over 30%. If such a frequency exist, we try to decrease to this frequency.
- *
- * Any frequency increase takes it to the maximum frequency. Frequency reduction
- * happens at minimum steps of 5% (default) of current frequency
+ * (default), then we try to increase frequency. Else, we adjust the frequency
+ * proportional to load.
*/
-static void od_check_cpu(int cpu, unsigned int load_freq)
+static void od_check_cpu(int cpu, unsigned int load)
{
struct od_cpu_dbs_info_s *dbs_info = &per_cpu(od_cpu_dbs_info, cpu);
struct cpufreq_policy *policy = dbs_info->cdbs.cur_policy;
@@ -178,36 +162,20 @@ static void od_check_cpu(int cpu, unsigned int load_freq)
dbs_info->freq_lo = 0;
/* Check for frequency increase */
- if (load_freq > od_tuners->up_threshold * policy->cur) {
+ if (load > od_tuners->up_threshold) {
/* If switching to max speed, apply sampling_down_factor */
if (policy->cur < policy->max)
dbs_info->rate_mult =
od_tuners->sampling_down_factor;
dbs_freq_increase(policy, policy->max);
- return;
- }
-
- /* Check for frequency decrease */
- /* if we cannot reduce the frequency anymore, break out early */
- if (policy->cur == policy->min)
- return;
-
- /*
- * The optimal frequency is the frequency that is the lowest that can
- * support the current CPU usage without triggering the up policy. To be
- * safe, we focus 10 points under the threshold.
- */
- if (load_freq < od_tuners->adj_up_threshold
- * policy->cur) {
+ } else {
+ /* Calculate the next frequency proportional to load */
unsigned int freq_next;
- freq_next = load_freq / od_tuners->adj_up_threshold;
+ freq_next = load * policy->cpuinfo.max_freq / 100;
/* No longer fully busy, reset rate_mult */
dbs_info->rate_mult = 1;
- if (freq_next < policy->min)
- freq_next = policy->min;
-
if (!od_tuners->powersave_bias) {
__cpufreq_driver_target(policy, freq_next,
CPUFREQ_RELATION_L);
@@ -374,9 +342,6 @@ static ssize_t store_up_threshold(struct dbs_data *dbs_data, const char *buf,
input < MIN_FREQUENCY_UP_THRESHOLD) {
return -EINVAL;
}
- /* Calculate the new adj_up_threshold */
- od_tuners->adj_up_threshold += input;
- od_tuners->adj_up_threshold -= od_tuners->up_threshold;
od_tuners->up_threshold = input;
return count;
@@ -403,8 +368,8 @@ static ssize_t store_sampling_down_factor(struct dbs_data *dbs_data,
return count;
}
-static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
- size_t count)
+static ssize_t store_ignore_nice_load(struct dbs_data *dbs_data,
+ const char *buf, size_t count)
{
struct od_dbs_tuners *od_tuners = dbs_data->tuners;
unsigned int input;
@@ -419,10 +384,10 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
if (input > 1)
input = 1;
- if (input == od_tuners->ignore_nice) { /* nothing to do */
+ if (input == od_tuners->ignore_nice_load) { /* nothing to do */
return count;
}
- od_tuners->ignore_nice = input;
+ od_tuners->ignore_nice_load = input;
/* we need to re-evaluate prev_cpu_idle */
for_each_online_cpu(j) {
@@ -430,7 +395,7 @@ static ssize_t store_ignore_nice(struct dbs_data *dbs_data, const char *buf,
dbs_info = &per_cpu(od_cpu_dbs_info, j);
dbs_info->cdbs.prev_cpu_idle = get_cpu_idle_time(j,
&dbs_info->cdbs.prev_cpu_wall, od_tuners->io_is_busy);
- if (od_tuners->ignore_nice)
+ if (od_tuners->ignore_nice_load)
dbs_info->cdbs.prev_cpu_nice =
kcpustat_cpu(j).cpustat[CPUTIME_NICE];
@@ -461,7 +426,7 @@ show_store_one(od, sampling_rate);
show_store_one(od, io_is_busy);
show_store_one(od, up_threshold);
show_store_one(od, sampling_down_factor);
-show_store_one(od, ignore_nice);
+show_store_one(od, ignore_nice_load);
show_store_one(od, powersave_bias);
declare_show_sampling_rate_min(od);
@@ -469,7 +434,7 @@ gov_sys_pol_attr_rw(sampling_rate);
gov_sys_pol_attr_rw(io_is_busy);
gov_sys_pol_attr_rw(up_threshold);
gov_sys_pol_attr_rw(sampling_down_factor);
-gov_sys_pol_attr_rw(ignore_nice);
+gov_sys_pol_attr_rw(ignore_nice_load);
gov_sys_pol_attr_rw(powersave_bias);
gov_sys_pol_attr_ro(sampling_rate_min);
@@ -478,7 +443,7 @@ static struct attribute *dbs_attributes_gov_sys[] = {
&sampling_rate_gov_sys.attr,
&up_threshold_gov_sys.attr,
&sampling_down_factor_gov_sys.attr,
- &ignore_nice_gov_sys.attr,
+ &ignore_nice_load_gov_sys.attr,
&powersave_bias_gov_sys.attr,
&io_is_busy_gov_sys.attr,
NULL
@@ -494,7 +459,7 @@ static struct attribute *dbs_attributes_gov_pol[] = {
&sampling_rate_gov_pol.attr,
&up_threshold_gov_pol.attr,
&sampling_down_factor_gov_pol.attr,
- &ignore_nice_gov_pol.attr,
+ &ignore_nice_load_gov_pol.attr,
&powersave_bias_gov_pol.attr,
&io_is_busy_gov_pol.attr,
NULL
@@ -513,7 +478,7 @@ static int od_init(struct dbs_data *dbs_data)
u64 idle_time;
int cpu;
- tuners = kzalloc(sizeof(struct od_dbs_tuners), GFP_KERNEL);
+ tuners = kzalloc(sizeof(*tuners), GFP_KERNEL);
if (!tuners) {
pr_err("%s: kzalloc failed\n", __func__);
return -ENOMEM;
@@ -525,8 +490,6 @@ static int od_init(struct dbs_data *dbs_data)
if (idle_time != -1ULL) {
/* Idle micro accounting is supported. Use finer thresholds */
tuners->up_threshold = MICRO_FREQUENCY_UP_THRESHOLD;
- tuners->adj_up_threshold = MICRO_FREQUENCY_UP_THRESHOLD -
- MICRO_FREQUENCY_DOWN_DIFFERENTIAL;
/*
* In nohz/micro accounting case we set the minimum frequency
* not depending on HZ, but fixed (very low). The deferred
@@ -535,8 +498,6 @@ static int od_init(struct dbs_data *dbs_data)
dbs_data->min_sampling_rate = MICRO_FREQUENCY_MIN_SAMPLE_RATE;
} else {
tuners->up_threshold = DEF_FREQUENCY_UP_THRESHOLD;
- tuners->adj_up_threshold = DEF_FREQUENCY_UP_THRESHOLD -
- DEF_FREQUENCY_DOWN_DIFFERENTIAL;
/* For correct statistics, we need 10 ticks for each measure */
dbs_data->min_sampling_rate = MIN_SAMPLING_RATE_RATIO *
@@ -544,7 +505,7 @@ static int od_init(struct dbs_data *dbs_data)
}
tuners->sampling_down_factor = DEF_SAMPLING_DOWN_FACTOR;
- tuners->ignore_nice = 0;
+ tuners->ignore_nice_load = 0;
tuners->powersave_bias = default_powersave_bias;
tuners->io_is_busy = should_io_be_busy();
diff --git a/drivers/cpufreq/cpufreq_performance.c b/drivers/cpufreq/cpufreq_performance.c
index ceee06849b91..cf117deb39b1 100644
--- a/drivers/cpufreq/cpufreq_performance.c
+++ b/drivers/cpufreq/cpufreq_performance.c
@@ -12,11 +12,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
-
+#include <linux/module.h>
static int cpufreq_governor_performance(struct cpufreq_policy *policy,
unsigned int event)
@@ -44,19 +42,16 @@ struct cpufreq_governor cpufreq_gov_performance = {
.owner = THIS_MODULE,
};
-
static int __init cpufreq_gov_performance_init(void)
{
return cpufreq_register_governor(&cpufreq_gov_performance);
}
-
static void __exit cpufreq_gov_performance_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_performance);
}
-
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq policy governor 'performance'");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq_powersave.c b/drivers/cpufreq/cpufreq_powersave.c
index 2d948a171155..e3b874c235ea 100644
--- a/drivers/cpufreq/cpufreq_powersave.c
+++ b/drivers/cpufreq/cpufreq_powersave.c
@@ -1,7 +1,7 @@
/*
- * linux/drivers/cpufreq/cpufreq_powersave.c
+ * linux/drivers/cpufreq/cpufreq_powersave.c
*
- * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
+ * Copyright (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -12,10 +12,9 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
+#include <linux/module.h>
static int cpufreq_governor_powersave(struct cpufreq_policy *policy,
unsigned int event)
@@ -48,13 +47,11 @@ static int __init cpufreq_gov_powersave_init(void)
return cpufreq_register_governor(&cpufreq_gov_powersave);
}
-
static void __exit cpufreq_gov_powersave_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_powersave);
}
-
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>");
MODULE_DESCRIPTION("CPUfreq policy governor 'powersave'");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index 6c287ae7bbba..de23310061f9 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -9,17 +9,10 @@
* published by the Free Software Foundation.
*/
-#include <linux/kernel.h>
-#include <linux/slab.h>
#include <linux/cpu.h>
-#include <linux/sysfs.h>
#include <linux/cpufreq.h>
#include <linux/module.h>
-#include <linux/jiffies.h>
-#include <linux/percpu.h>
-#include <linux/kobject.h>
-#include <linux/spinlock.h>
-#include <linux/notifier.h>
+#include <linux/slab.h>
#include <asm/cputime.h>
static spinlock_t cpufreq_stats_lock;
@@ -27,7 +20,7 @@ static spinlock_t cpufreq_stats_lock;
struct cpufreq_stats {
unsigned int cpu;
unsigned int total_trans;
- unsigned long long last_time;
+ unsigned long long last_time;
unsigned int max_state;
unsigned int state_num;
unsigned int last_index;
@@ -81,7 +74,7 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
for (i = 0; i < stat->state_num; i++) {
len += sprintf(buf + len, "%u %llu\n", stat->freq_table[i],
(unsigned long long)
- cputime64_to_clock_t(stat->time_in_state[i]));
+ jiffies_64_to_clock_t(stat->time_in_state[i]));
}
return len;
}
@@ -116,7 +109,7 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
len += snprintf(buf + len, PAGE_SIZE - len, "%9u: ",
stat->freq_table[i]);
- for (j = 0; j < stat->state_num; j++) {
+ for (j = 0; j < stat->state_num; j++) {
if (len >= PAGE_SIZE)
break;
len += snprintf(buf + len, PAGE_SIZE - len, "%9u ",
@@ -200,22 +193,22 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
{
unsigned int i, j, count = 0, ret = 0;
struct cpufreq_stats *stat;
- struct cpufreq_policy *data;
+ struct cpufreq_policy *current_policy;
unsigned int alloc_size;
unsigned int cpu = policy->cpu;
if (per_cpu(cpufreq_stats_table, cpu))
return -EBUSY;
- stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
+ stat = kzalloc(sizeof(*stat), GFP_KERNEL);
if ((stat) == NULL)
return -ENOMEM;
- data = cpufreq_cpu_get(cpu);
- if (data == NULL) {
+ current_policy = cpufreq_cpu_get(cpu);
+ if (current_policy == NULL) {
ret = -EINVAL;
goto error_get_fail;
}
- ret = sysfs_create_group(&data->kobj, &stats_attr_group);
+ ret = sysfs_create_group(&current_policy->kobj, &stats_attr_group);
if (ret)
goto error_out;
@@ -258,10 +251,10 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
stat->last_time = get_jiffies_64();
stat->last_index = freq_table_get_index(stat, policy->cur);
spin_unlock(&cpufreq_stats_lock);
- cpufreq_cpu_put(data);
+ cpufreq_cpu_put(current_policy);
return 0;
error_out:
- cpufreq_cpu_put(data);
+ cpufreq_cpu_put(current_policy);
error_get_fail:
kfree(stat);
per_cpu(cpufreq_stats_table, cpu) = NULL;
@@ -362,23 +355,17 @@ out:
return ret;
}
-static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
+static int cpufreq_stat_cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpufreq_update_policy(cpu);
- break;
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
cpufreq_stats_free_sysfs(cpu);
break;
case CPU_DEAD:
- case CPU_DEAD_FROZEN:
cpufreq_stats_free_table(cpu);
break;
case CPU_DOWN_FAILED:
@@ -415,8 +402,6 @@ static int __init cpufreq_stats_init(void)
return ret;
register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
- for_each_online_cpu(cpu)
- cpufreq_update_policy(cpu);
ret = cpufreq_register_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER);
diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c
index bbeb9c0720a6..4dbf1db16aca 100644
--- a/drivers/cpufreq/cpufreq_userspace.c
+++ b/drivers/cpufreq/cpufreq_userspace.c
@@ -13,55 +13,13 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/smp.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/interrupt.h>
#include <linux/cpufreq.h>
-#include <linux/cpu.h>
-#include <linux/types.h>
-#include <linux/fs.h>
-#include <linux/sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-/**
- * A few values needed by the userspace governor
- */
-static DEFINE_PER_CPU(unsigned int, cpu_max_freq);
-static DEFINE_PER_CPU(unsigned int, cpu_min_freq);
-static DEFINE_PER_CPU(unsigned int, cpu_cur_freq); /* current CPU freq */
-static DEFINE_PER_CPU(unsigned int, cpu_set_freq); /* CPU freq desired by
- userspace */
static DEFINE_PER_CPU(unsigned int, cpu_is_managed);
-
static DEFINE_MUTEX(userspace_mutex);
-static int cpus_using_userspace_governor;
-
-/* keep track of frequency transitions */
-static int
-userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- struct cpufreq_freqs *freq = data;
-
- if (!per_cpu(cpu_is_managed, freq->cpu))
- return 0;
-
- if (val == CPUFREQ_POSTCHANGE) {
- pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n",
- freq->cpu, freq->new);
- per_cpu(cpu_cur_freq, freq->cpu) = freq->new;
- }
-
- return 0;
-}
-
-static struct notifier_block userspace_cpufreq_notifier_block = {
- .notifier_call = userspace_cpufreq_notifier
-};
-
/**
* cpufreq_set - set the CPU frequency
@@ -80,34 +38,15 @@ static int cpufreq_set(struct cpufreq_policy *policy, unsigned int freq)
if (!per_cpu(cpu_is_managed, policy->cpu))
goto err;
- per_cpu(cpu_set_freq, policy->cpu) = freq;
-
- if (freq < per_cpu(cpu_min_freq, policy->cpu))
- freq = per_cpu(cpu_min_freq, policy->cpu);
- if (freq > per_cpu(cpu_max_freq, policy->cpu))
- freq = per_cpu(cpu_max_freq, policy->cpu);
-
- /*
- * We're safe from concurrent calls to ->target() here
- * as we hold the userspace_mutex lock. If we were calling
- * cpufreq_driver_target, a deadlock situation might occur:
- * A: cpufreq_set (lock userspace_mutex) ->
- * cpufreq_driver_target(lock policy->lock)
- * B: cpufreq_set_policy(lock policy->lock) ->
- * __cpufreq_governor ->
- * cpufreq_governor_userspace (lock userspace_mutex)
- */
ret = __cpufreq_driver_target(policy, freq, CPUFREQ_RELATION_L);
-
err:
mutex_unlock(&userspace_mutex);
return ret;
}
-
static ssize_t show_speed(struct cpufreq_policy *policy, char *buf)
{
- return sprintf(buf, "%u\n", per_cpu(cpu_cur_freq, policy->cpu));
+ return sprintf(buf, "%u\n", policy->cur);
}
static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
@@ -119,73 +58,37 @@ static int cpufreq_governor_userspace(struct cpufreq_policy *policy,
switch (event) {
case CPUFREQ_GOV_START:
BUG_ON(!policy->cur);
- mutex_lock(&userspace_mutex);
-
- if (cpus_using_userspace_governor == 0) {
- cpufreq_register_notifier(
- &userspace_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- }
- cpus_using_userspace_governor++;
+ pr_debug("started managing cpu %u\n", cpu);
+ mutex_lock(&userspace_mutex);
per_cpu(cpu_is_managed, cpu) = 1;
- per_cpu(cpu_min_freq, cpu) = policy->min;
- per_cpu(cpu_max_freq, cpu) = policy->max;
- per_cpu(cpu_cur_freq, cpu) = policy->cur;
- per_cpu(cpu_set_freq, cpu) = policy->cur;
- pr_debug("managing cpu %u started "
- "(%u - %u kHz, currently %u kHz)\n",
- cpu,
- per_cpu(cpu_min_freq, cpu),
- per_cpu(cpu_max_freq, cpu),
- per_cpu(cpu_cur_freq, cpu));
-
mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_STOP:
- mutex_lock(&userspace_mutex);
- cpus_using_userspace_governor--;
- if (cpus_using_userspace_governor == 0) {
- cpufreq_unregister_notifier(
- &userspace_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- }
+ pr_debug("managing cpu %u stopped\n", cpu);
+ mutex_lock(&userspace_mutex);
per_cpu(cpu_is_managed, cpu) = 0;
- per_cpu(cpu_min_freq, cpu) = 0;
- per_cpu(cpu_max_freq, cpu) = 0;
- per_cpu(cpu_set_freq, cpu) = 0;
- pr_debug("managing cpu %u stopped\n", cpu);
mutex_unlock(&userspace_mutex);
break;
case CPUFREQ_GOV_LIMITS:
mutex_lock(&userspace_mutex);
- pr_debug("limit event for cpu %u: %u - %u kHz, "
- "currently %u kHz, last set to %u kHz\n",
+ pr_debug("limit event for cpu %u: %u - %u kHz, currently %u kHz\n",
cpu, policy->min, policy->max,
- per_cpu(cpu_cur_freq, cpu),
- per_cpu(cpu_set_freq, cpu));
- if (policy->max < per_cpu(cpu_set_freq, cpu)) {
+ policy->cur);
+
+ if (policy->max < policy->cur)
__cpufreq_driver_target(policy, policy->max,
CPUFREQ_RELATION_H);
- } else if (policy->min > per_cpu(cpu_set_freq, cpu)) {
+ else if (policy->min > policy->cur)
__cpufreq_driver_target(policy, policy->min,
CPUFREQ_RELATION_L);
- } else {
- __cpufreq_driver_target(policy,
- per_cpu(cpu_set_freq, cpu),
- CPUFREQ_RELATION_L);
- }
- per_cpu(cpu_min_freq, cpu) = policy->min;
- per_cpu(cpu_max_freq, cpu) = policy->max;
- per_cpu(cpu_cur_freq, cpu) = policy->cur;
mutex_unlock(&userspace_mutex);
break;
}
return rc;
}
-
#ifndef CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE
static
#endif
@@ -202,13 +105,11 @@ static int __init cpufreq_gov_userspace_init(void)
return cpufreq_register_governor(&cpufreq_gov_userspace);
}
-
static void __exit cpufreq_gov_userspace_exit(void)
{
cpufreq_unregister_governor(&cpufreq_gov_userspace);
}
-
MODULE_AUTHOR("Dominik Brodowski <linux@brodo.de>, "
"Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("CPUfreq policy governor 'userspace'");
diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c
index ee142c490575..cb8276dd19ca 100644
--- a/drivers/cpufreq/cris-artpec3-cpufreq.c
+++ b/drivers/cpufreq/cris-artpec3-cpufreq.c
@@ -111,7 +111,6 @@ static struct cpufreq_driver cris_freq_driver = {
.init = cris_freq_cpu_init,
.exit = cris_freq_cpu_exit,
.name = "cris_freq",
- .owner = THIS_MODULE,
.attr = cris_freq_attr,
};
diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c
index 12952235d5db..72328f77dc53 100644
--- a/drivers/cpufreq/cris-etraxfs-cpufreq.c
+++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c
@@ -108,7 +108,6 @@ static struct cpufreq_driver cris_freq_driver = {
.init = cris_freq_cpu_init,
.exit = cris_freq_cpu_exit,
.name = "cris_freq",
- .owner = THIS_MODULE,
.attr = cris_freq_attr,
};
diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c
index c33c76c360fa..9b4c58bda31d 100644
--- a/drivers/cpufreq/davinci-cpufreq.c
+++ b/drivers/cpufreq/davinci-cpufreq.c
@@ -50,9 +50,7 @@ static int davinci_verify_speed(struct cpufreq_policy *policy)
if (policy->cpu)
return -EINVAL;
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
-
+ cpufreq_verify_within_cpu_limits(policy);
policy->min = clk_round_rate(armclk, policy->min * 1000) / 1000;
policy->max = clk_round_rate(armclk, policy->max * 1000) / 1000;
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
@@ -68,28 +66,18 @@ static unsigned int davinci_getspeed(unsigned int cpu)
return clk_get_rate(cpufreq.armclk) / 1000;
}
-static int davinci_target(struct cpufreq_policy *policy,
- unsigned int target_freq, unsigned int relation)
+static int davinci_target(struct cpufreq_policy *policy, unsigned int idx)
{
int ret = 0;
- unsigned int idx;
struct cpufreq_freqs freqs;
struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data;
struct clk *armclk = cpufreq.armclk;
freqs.old = davinci_getspeed(0);
- freqs.new = clk_round_rate(armclk, target_freq * 1000) / 1000;
-
- if (freqs.old == freqs.new)
- return ret;
+ freqs.new = pdata->freq_table[idx].frequency;
dev_dbg(cpufreq.dev, "transition: %u --> %u\n", freqs.old, freqs.new);
- ret = cpufreq_frequency_table_target(policy, pdata->freq_table,
- freqs.new, relation, &idx);
- if (ret)
- return -EINVAL;
-
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
/* if moving to higher frequency, up the voltage beforehand */
@@ -170,7 +158,7 @@ static struct freq_attr *davinci_cpufreq_attr[] = {
static struct cpufreq_driver davinci_driver = {
.flags = CPUFREQ_STICKY,
.verify = davinci_verify_speed,
- .target = davinci_target,
+ .target_index = davinci_target,
.get = davinci_getspeed,
.init = davinci_cpu_init,
.exit = davinci_cpu_exit,
diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c
index 6ec6539ae041..8c005ac8b701 100644
--- a/drivers/cpufreq/dbx500-cpufreq.c
+++ b/drivers/cpufreq/dbx500-cpufreq.c
@@ -82,7 +82,7 @@ static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu)
return freq_table[i].frequency;
}
-static int __cpuinit dbx500_cpufreq_init(struct cpufreq_policy *policy)
+static int dbx500_cpufreq_init(struct cpufreq_policy *policy)
{
int res;
diff --git a/drivers/cpufreq/e_powersaver.c b/drivers/cpufreq/e_powersaver.c
index 37380fb92621..637b48107b76 100644
--- a/drivers/cpufreq/e_powersaver.c
+++ b/drivers/cpufreq/e_powersaver.c
@@ -54,7 +54,7 @@ static struct acpi_processor_performance *eps_acpi_cpu_perf;
/* Minimum necessary to get acpi_processor_get_bios_limit() working */
static int eps_acpi_init(void)
{
- eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
+ eps_acpi_cpu_perf = kzalloc(sizeof(*eps_acpi_cpu_perf),
GFP_KERNEL);
if (!eps_acpi_cpu_perf)
return -ENOMEM;
@@ -188,7 +188,7 @@ static int eps_target(struct cpufreq_policy *policy,
}
/* Make frequency transition */
- dest_state = centaur->freq_table[newstate].index & 0xffff;
+ dest_state = centaur->freq_table[newstate].driver_data & 0xffff;
ret = eps_set_state(centaur, policy, dest_state);
if (ret)
printk(KERN_ERR "eps: Timeout!\n");
@@ -363,7 +363,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
states = 2;
/* Allocate private data and frequency table for current cpu */
- centaur = kzalloc(sizeof(struct eps_cpu_data)
+ centaur = kzalloc(sizeof(*centaur)
+ (states + 1) * sizeof(struct cpufreq_frequency_table),
GFP_KERNEL);
if (!centaur)
@@ -380,9 +380,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
f_table = &centaur->freq_table[0];
if (brand != EPS_BRAND_C7M) {
f_table[0].frequency = fsb * min_multiplier;
- f_table[0].index = (min_multiplier << 8) | min_voltage;
+ f_table[0].driver_data = (min_multiplier << 8) | min_voltage;
f_table[1].frequency = fsb * max_multiplier;
- f_table[1].index = (max_multiplier << 8) | max_voltage;
+ f_table[1].driver_data = (max_multiplier << 8) | max_voltage;
f_table[2].frequency = CPUFREQ_TABLE_END;
} else {
k = 0;
@@ -391,7 +391,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
for (i = min_multiplier; i <= max_multiplier; i++) {
voltage = (k * step) / 256 + min_voltage;
f_table[k].frequency = fsb * i;
- f_table[k].index = (i << 8) | voltage;
+ f_table[k].driver_data = (i << 8) | voltage;
k++;
}
f_table[k].frequency = CPUFREQ_TABLE_END;
@@ -433,7 +433,6 @@ static struct cpufreq_driver eps_driver = {
.exit = eps_cpu_exit,
.get = eps_get,
.name = "e_powersaver",
- .owner = THIS_MODULE,
.attr = eps_attr,
};
diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c
index 658d860344b0..823a400d98fd 100644
--- a/drivers/cpufreq/elanfreq.c
+++ b/drivers/cpufreq/elanfreq.c
@@ -274,7 +274,6 @@ static struct cpufreq_driver elanfreq_driver = {
.init = elanfreq_cpu_init,
.exit = elanfreq_cpu_exit,
.name = "elanfreq",
- .owner = THIS_MODULE,
.attr = elanfreq_attr,
};
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
index 475b4f607f0d..4aeb7ffbdd0e 100644
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -283,7 +283,7 @@ static int __init exynos_cpufreq_init(void)
{
int ret = -EINVAL;
- exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL);
+ exynos_info = kzalloc(sizeof(*exynos_info), GFP_KERNEL);
if (!exynos_info)
return -ENOMEM;
diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c
index 0c74018eda47..a73ca88155c7 100644
--- a/drivers/cpufreq/exynos5440-cpufreq.c
+++ b/drivers/cpufreq/exynos5440-cpufreq.c
@@ -20,7 +20,7 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -123,7 +123,7 @@ static int init_div_table(void)
rcu_read_lock();
for (i = 0; freq_tbl[i].frequency != CPUFREQ_TABLE_END; i++) {
- opp = opp_find_freq_exact(dvfs_info->dev,
+ opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
freq_tbl[i].frequency * 1000, true);
if (IS_ERR(opp)) {
rcu_read_unlock();
@@ -142,7 +142,7 @@ static int init_div_table(void)
<< P0_7_CSCLKDEV_SHIFT;
/* Calculate EMA */
- volt_id = opp_get_voltage(opp);
+ volt_id = dev_pm_opp_get_voltage(opp);
volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP;
if (volt_id < PMIC_HIGH_VOLT) {
ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) |
@@ -339,7 +339,7 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
}
static struct cpufreq_driver exynos_driver = {
- .flags = CPUFREQ_STICKY,
+ .flags = CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION,
.verify = exynos_verify_speed,
.target = exynos_target,
.get = exynos_getspeed,
@@ -396,13 +396,14 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
goto err_put_node;
}
- ret = opp_init_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
+ ret = dev_pm_opp_init_cpufreq_table(dvfs_info->dev,
+ &dvfs_info->freq_table);
if (ret) {
dev_err(dvfs_info->dev,
"failed to init cpufreq table: %d\n", ret);
goto err_put_node;
}
- dvfs_info->freq_count = opp_get_opp_count(dvfs_info->dev);
+ dvfs_info->freq_count = dev_pm_opp_get_opp_count(dvfs_info->dev);
exynos_sort_descend_freq_table();
if (of_property_read_u32(np, "clock-latency", &dvfs_info->latency))
@@ -451,7 +452,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
return 0;
err_free_table:
- opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
+ dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
err_put_node:
of_node_put(np);
dev_err(dvfs_info->dev, "%s: failed initialization\n", __func__);
@@ -461,7 +462,7 @@ err_put_node:
static int exynos_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&exynos_driver);
- opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
+ dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
return 0;
}
diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c
index d7a79662e24c..3458d27f63b4 100644
--- a/drivers/cpufreq/freq_table.c
+++ b/drivers/cpufreq/freq_table.c
@@ -11,10 +11,8 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
#include <linux/cpufreq.h>
+#include <linux/module.h>
/*********************************************************************
* FREQUENCY TABLE HELPERS *
@@ -34,8 +32,8 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
continue;
}
- pr_debug("table entry %u: %u kHz, %u index\n",
- i, freq, table[i].index);
+ pr_debug("table entry %u: %u kHz, %u driver_data\n",
+ i, freq, table[i].driver_data);
if (freq < min_freq)
min_freq = freq;
if (freq > max_freq)
@@ -56,31 +54,30 @@ EXPORT_SYMBOL_GPL(cpufreq_frequency_table_cpuinfo);
int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
- unsigned int next_larger = ~0;
- unsigned int i;
- unsigned int count = 0;
+ unsigned int next_larger = ~0, freq, i = 0;
+ bool found = false;
pr_debug("request for verification of policy (%u - %u kHz) for cpu %u\n",
policy->min, policy->max, policy->cpu);
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
- for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) {
- unsigned int freq = table[i].frequency;
+ for (; freq = table[i].frequency, freq != CPUFREQ_TABLE_END; i++) {
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
- if ((freq >= policy->min) && (freq <= policy->max))
- count++;
- else if ((next_larger > freq) && (freq > policy->max))
+ if ((freq >= policy->min) && (freq <= policy->max)) {
+ found = true;
+ break;
+ }
+
+ if ((next_larger > freq) && (freq > policy->max))
next_larger = freq;
}
- if (!count)
+ if (!found) {
policy->max = next_larger;
-
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
+ }
pr_debug("verification lead to (%u - %u kHz) for cpu %u\n",
policy->min, policy->max, policy->cpu);
@@ -89,6 +86,20 @@ int cpufreq_frequency_table_verify(struct cpufreq_policy *policy,
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_verify);
+/*
+ * Generic routine to verify policy & frequency table, requires driver to call
+ * cpufreq_frequency_table_get_attr() prior to it.
+ */
+int cpufreq_generic_frequency_table_verify(struct cpufreq_policy *policy)
+{
+ struct cpufreq_frequency_table *table =
+ cpufreq_frequency_get_table(policy->cpu);
+ if (!table)
+ return -ENODEV;
+
+ return cpufreq_frequency_table_verify(policy, table);
+}
+EXPORT_SYMBOL_GPL(cpufreq_generic_frequency_table_verify);
int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table,
@@ -97,11 +108,11 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
unsigned int *index)
{
struct cpufreq_frequency_table optimal = {
- .index = ~0,
+ .driver_data = ~0,
.frequency = 0,
};
struct cpufreq_frequency_table suboptimal = {
- .index = ~0,
+ .driver_data = ~0,
.frequency = 0,
};
unsigned int i;
@@ -129,12 +140,12 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
if (freq <= target_freq) {
if (freq >= optimal.frequency) {
optimal.frequency = freq;
- optimal.index = i;
+ optimal.driver_data = i;
}
} else {
if (freq <= suboptimal.frequency) {
suboptimal.frequency = freq;
- suboptimal.index = i;
+ suboptimal.driver_data = i;
}
}
break;
@@ -142,26 +153,26 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
if (freq >= target_freq) {
if (freq <= optimal.frequency) {
optimal.frequency = freq;
- optimal.index = i;
+ optimal.driver_data = i;
}
} else {
if (freq >= suboptimal.frequency) {
suboptimal.frequency = freq;
- suboptimal.index = i;
+ suboptimal.driver_data = i;
}
}
break;
}
}
- if (optimal.index > i) {
- if (suboptimal.index > i)
+ if (optimal.driver_data > i) {
+ if (suboptimal.driver_data > i)
return -EINVAL;
- *index = suboptimal.index;
+ *index = suboptimal.driver_data;
} else
- *index = optimal.index;
+ *index = optimal.driver_data;
pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency,
- table[*index].index);
+ table[*index].driver_data);
return 0;
}
@@ -202,6 +213,12 @@ struct freq_attr cpufreq_freq_attr_scaling_available_freqs = {
};
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);
+struct freq_attr *cpufreq_generic_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+EXPORT_SYMBOL_GPL(cpufreq_generic_attr);
+
/*
* if you use these, you must assure that the frequency table is valid
* all the time between get_attr and put_attr!
@@ -221,6 +238,18 @@ void cpufreq_frequency_table_put_attr(unsigned int cpu)
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_put_attr);
+int cpufreq_table_validate_and_show(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *table)
+{
+ int ret = cpufreq_frequency_table_cpuinfo(policy, table);
+
+ if (!ret)
+ cpufreq_frequency_table_get_attr(table, policy->cpu);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpufreq_table_validate_and_show);
+
void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy)
{
pr_debug("Updating show_table for new_cpu %u from last_cpu %u\n",
diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c
index 3dfc99b9ca86..ef5fee7dc0b3 100644
--- a/drivers/cpufreq/gx-suspmod.c
+++ b/drivers/cpufreq/gx-suspmod.c
@@ -446,7 +446,6 @@ static struct cpufreq_driver gx_suspmod_driver = {
.target = cpufreq_gx_target,
.init = cpufreq_gx_cpu_init,
.name = "gx-suspmod",
- .owner = THIS_MODULE,
};
static int __init cpufreq_gx_init(void)
@@ -466,7 +465,7 @@ static int __init cpufreq_gx_init(void)
pr_debug("geode suspend modulation available.\n");
- params = kzalloc(sizeof(struct gxfreq_params), GFP_KERNEL);
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
if (params == NULL)
return -ENOMEM;
diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c
index c0075dbaa633..e126f120b3cb 100644
--- a/drivers/cpufreq/ia64-acpi-cpufreq.c
+++ b/drivers/cpufreq/ia64-acpi-cpufreq.c
@@ -141,7 +141,6 @@ processor_set_freq (
{
int ret = 0;
u32 value = 0;
- struct cpufreq_freqs cpufreq_freqs;
cpumask_t saved_mask;
int retval;
@@ -168,13 +167,6 @@ processor_set_freq (
pr_debug("Transitioning from P%d to P%d\n",
data->acpi_data.state, state);
- /* cpufreq frequency struct */
- cpufreq_freqs.old = data->freq_table[data->acpi_data.state].frequency;
- cpufreq_freqs.new = data->freq_table[state].frequency;
-
- /* notify cpufreq */
- cpufreq_notify_transition(policy, &cpufreq_freqs, CPUFREQ_PRECHANGE);
-
/*
* First we write the target state's 'control' value to the
* control_register.
@@ -186,22 +178,11 @@ processor_set_freq (
ret = processor_set_pstate(value);
if (ret) {
- unsigned int tmp = cpufreq_freqs.new;
- cpufreq_notify_transition(policy, &cpufreq_freqs,
- CPUFREQ_POSTCHANGE);
- cpufreq_freqs.new = cpufreq_freqs.old;
- cpufreq_freqs.old = tmp;
- cpufreq_notify_transition(policy, &cpufreq_freqs,
- CPUFREQ_PRECHANGE);
- cpufreq_notify_transition(policy, &cpufreq_freqs,
- CPUFREQ_POSTCHANGE);
printk(KERN_WARNING "Transition failed with error %d\n", ret);
retval = -ENODEV;
goto migrate_end;
}
- cpufreq_notify_transition(policy, &cpufreq_freqs, CPUFREQ_POSTCHANGE);
-
data->acpi_data.state = state;
retval = 0;
@@ -274,7 +255,7 @@ acpi_cpufreq_cpu_init (
pr_debug("acpi_cpufreq_cpu_init\n");
- data = kzalloc(sizeof(struct cpufreq_acpi_io), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return (-ENOMEM);
@@ -304,7 +285,7 @@ acpi_cpufreq_cpu_init (
}
/* alloc freq_table */
- data->freq_table = kmalloc(sizeof(struct cpufreq_frequency_table) *
+ data->freq_table = kmalloc(sizeof(*data->freq_table) *
(data->acpi_data.state_count + 1),
GFP_KERNEL);
if (!data->freq_table) {
@@ -326,7 +307,7 @@ acpi_cpufreq_cpu_init (
/* table init */
for (i = 0; i <= data->acpi_data.state_count; i++)
{
- data->freq_table[i].index = i;
+ data->freq_table[i].driver_data = i;
if (i < data->acpi_data.state_count) {
data->freq_table[i].frequency =
data->acpi_data.states[i].core_frequency * 1000;
@@ -409,7 +390,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
.init = acpi_cpufreq_cpu_init,
.exit = acpi_cpufreq_cpu_exit,
.name = "acpi-cpufreq",
- .owner = THIS_MODULE,
.attr = acpi_cpufreq_attr,
};
diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c
index b78bc35973ba..5da79271bd27 100644
--- a/drivers/cpufreq/imx6q-cpufreq.c
+++ b/drivers/cpufreq/imx6q-cpufreq.c
@@ -12,7 +12,7 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/of.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
@@ -71,14 +71,14 @@ static int imx6q_set_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
rcu_read_lock();
- opp = opp_find_freq_ceil(cpu_dev, &freq_hz);
+ opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(cpu_dev, "failed to find OPP for %ld\n", freq_hz);
return PTR_ERR(opp);
}
- volt = opp_get_voltage(opp);
+ volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
volt_old = regulator_get_voltage(arm_reg);
@@ -246,14 +246,14 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
}
/* We expect an OPP table supplied by platform */
- num = opp_get_opp_count(cpu_dev);
+ num = dev_pm_opp_get_opp_count(cpu_dev);
if (num < 0) {
ret = num;
dev_err(cpu_dev, "no OPP table is found: %d\n", ret);
goto put_node;
}
- ret = opp_init_cpufreq_table(cpu_dev, &freq_table);
+ ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto put_node;
@@ -268,12 +268,12 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
* same order.
*/
rcu_read_lock();
- opp = opp_find_freq_exact(cpu_dev,
+ opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[0].frequency * 1000, true);
- min_volt = opp_get_voltage(opp);
- opp = opp_find_freq_exact(cpu_dev,
+ min_volt = dev_pm_opp_get_voltage(opp);
+ opp = dev_pm_opp_find_freq_exact(cpu_dev,
freq_table[--num].frequency * 1000, true);
- max_volt = opp_get_voltage(opp);
+ max_volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
ret = regulator_set_voltage_time(arm_reg, min_volt, max_volt);
if (ret > 0)
@@ -301,7 +301,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
return 0;
free_freq_table:
- opp_free_cpufreq_table(cpu_dev, &freq_table);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
put_node:
of_node_put(np);
return ret;
@@ -310,7 +310,7 @@ put_node:
static int imx6q_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&imx6q_cpufreq_driver);
- opp_free_cpufreq_table(cpu_dev, &freq_table);
+ dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
return 0;
}
diff --git a/drivers/cpufreq/integrator-cpufreq.c b/drivers/cpufreq/integrator-cpufreq.c
index f7c99df0880b..8152a9bb7e2c 100644
--- a/drivers/cpufreq/integrator-cpufreq.c
+++ b/drivers/cpufreq/integrator-cpufreq.c
@@ -59,9 +59,7 @@ static int integrator_verify_policy(struct cpufreq_policy *policy)
{
struct icst_vco vco;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
vco = icst_hz_to_vco(&cclk_params, policy->max * 1000);
policy->max = icst_hz(&cclk_params, vco) / 1000;
@@ -69,10 +67,7 @@ static int integrator_verify_policy(struct cpufreq_policy *policy)
vco = icst_hz_to_vco(&cclk_params, policy->min * 1000);
policy->min = icst_hz(&cclk_params, vco) / 1000;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
-
+ cpufreq_verify_within_cpu_limits(policy);
return 0;
}
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 07f2840ad805..000dd7514ec1 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -606,9 +606,7 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
static int intel_pstate_verify_policy(struct cpufreq_policy *policy)
{
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
if ((policy->policy != CPUFREQ_POLICY_POWERSAVE) &&
(policy->policy != CPUFREQ_POLICY_PERFORMANCE))
@@ -617,7 +615,7 @@ static int intel_pstate_verify_policy(struct cpufreq_policy *policy)
return 0;
}
-static int __cpuinit intel_pstate_cpu_exit(struct cpufreq_policy *policy)
+static int intel_pstate_cpu_exit(struct cpufreq_policy *policy)
{
int cpu = policy->cpu;
@@ -627,7 +625,7 @@ static int __cpuinit intel_pstate_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
-static int __cpuinit intel_pstate_cpu_init(struct cpufreq_policy *policy)
+static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
{
int rc, min_pstate, max_pstate;
struct cpudata *cpu;
@@ -665,7 +663,6 @@ static struct cpufreq_driver intel_pstate_driver = {
.init = intel_pstate_cpu_init,
.exit = intel_pstate_cpu_exit,
.name = "intel_pstate",
- .owner = THIS_MODULE,
};
static int __initdata no_load;
diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c
index b2644af985ec..45e4d7fc261d 100644
--- a/drivers/cpufreq/kirkwood-cpufreq.c
+++ b/drivers/cpufreq/kirkwood-cpufreq.c
@@ -59,7 +59,7 @@ static void kirkwood_cpufreq_set_cpu_state(struct cpufreq_policy *policy,
unsigned int index)
{
struct cpufreq_freqs freqs;
- unsigned int state = kirkwood_freq_table[index].index;
+ unsigned int state = kirkwood_freq_table[index].driver_data;
unsigned long reg;
freqs.old = kirkwood_cpufreq_get_cpu_frequency(0);
@@ -158,7 +158,6 @@ static struct cpufreq_driver kirkwood_cpufreq_driver = {
.init = kirkwood_cpufreq_cpu_init,
.exit = kirkwood_cpufreq_cpu_exit,
.name = "kirkwood-cpufreq",
- .owner = THIS_MODULE,
.attr = kirkwood_cpufreq_attr,
};
diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c
index b448638e34de..4ada1cccb052 100644
--- a/drivers/cpufreq/longhaul.c
+++ b/drivers/cpufreq/longhaul.c
@@ -254,7 +254,7 @@ static void longhaul_setstate(struct cpufreq_policy *policy,
u32 bm_timeout = 1000;
unsigned int dir = 0;
- mults_index = longhaul_table[table_index].index;
+ mults_index = longhaul_table[table_index].driver_data;
/* Safety precautions */
mult = mults[mults_index & 0x1f];
if (mult == -1)
@@ -422,7 +422,7 @@ static int guess_fsb(int mult)
}
-static int __cpuinit longhaul_get_ranges(void)
+static int longhaul_get_ranges(void)
{
unsigned int i, j, k = 0;
unsigned int ratio;
@@ -487,7 +487,7 @@ static int __cpuinit longhaul_get_ranges(void)
if (ratio > maxmult || ratio < minmult)
continue;
longhaul_table[k].frequency = calc_speed(ratio);
- longhaul_table[k].index = j;
+ longhaul_table[k].driver_data = j;
k++;
}
if (k <= 1) {
@@ -508,8 +508,8 @@ static int __cpuinit longhaul_get_ranges(void)
if (min_i != j) {
swap(longhaul_table[j].frequency,
longhaul_table[min_i].frequency);
- swap(longhaul_table[j].index,
- longhaul_table[min_i].index);
+ swap(longhaul_table[j].driver_data,
+ longhaul_table[min_i].driver_data);
}
}
@@ -517,7 +517,7 @@ static int __cpuinit longhaul_get_ranges(void)
/* Find index we are running on */
for (j = 0; j < k; j++) {
- if (mults[longhaul_table[j].index & 0x1f] == mult) {
+ if (mults[longhaul_table[j].driver_data & 0x1f] == mult) {
longhaul_index = j;
break;
}
@@ -526,7 +526,7 @@ static int __cpuinit longhaul_get_ranges(void)
}
-static void __cpuinit longhaul_setup_voltagescaling(void)
+static void longhaul_setup_voltagescaling(void)
{
union msr_longhaul longhaul;
struct mV_pos minvid, maxvid, vid;
@@ -613,7 +613,7 @@ static void __cpuinit longhaul_setup_voltagescaling(void)
pos = (speed - min_vid_speed) / kHz_step + minvid.pos;
else
pos = minvid.pos;
- longhaul_table[j].index |= mV_vrm_table[pos] << 8;
+ longhaul_table[j].driver_data |= mV_vrm_table[pos] << 8;
vid = vrm_mV_table[mV_vrm_table[pos]];
printk(KERN_INFO PFX "f: %d kHz, index: %d, vid: %d mV\n",
speed, j, vid.mV);
@@ -656,12 +656,12 @@ static int longhaul_target(struct cpufreq_policy *policy,
* this in hardware, C3 is old and we need to do this
* in software. */
i = longhaul_index;
- current_vid = (longhaul_table[longhaul_index].index >> 8);
+ current_vid = (longhaul_table[longhaul_index].driver_data >> 8);
current_vid &= 0x1f;
if (table_index > longhaul_index)
dir = 1;
while (i != table_index) {
- vid = (longhaul_table[i].index >> 8) & 0x1f;
+ vid = (longhaul_table[i].driver_data >> 8) & 0x1f;
if (vid != current_vid) {
longhaul_setstate(policy, i);
current_vid = vid;
@@ -780,7 +780,7 @@ static int longhaul_setup_southbridge(void)
return 0;
}
-static int __cpuinit longhaul_cpu_init(struct cpufreq_policy *policy)
+static int longhaul_cpu_init(struct cpufreq_policy *policy)
{
struct cpuinfo_x86 *c = &cpu_data(0);
char *cpuname = NULL;
@@ -948,7 +948,6 @@ static struct cpufreq_driver longhaul_driver = {
.init = longhaul_cpu_init,
.exit = longhaul_cpu_exit,
.name = "longhaul",
- .owner = THIS_MODULE,
.attr = longhaul_attr,
};
diff --git a/drivers/cpufreq/longhaul.h b/drivers/cpufreq/longhaul.h
index e2dc436099d1..1928b923a57b 100644
--- a/drivers/cpufreq/longhaul.h
+++ b/drivers/cpufreq/longhaul.h
@@ -56,7 +56,7 @@ union msr_longhaul {
/*
* VIA C3 Samuel 1 & Samuel 2 (stepping 0)
*/
-static const int __cpuinitconst samuel1_mults[16] = {
+static const int samuel1_mults[16] = {
-1, /* 0000 -> RESERVED */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -75,7 +75,7 @@ static const int __cpuinitconst samuel1_mults[16] = {
-1, /* 1111 -> RESERVED */
};
-static const int __cpuinitconst samuel1_eblcr[16] = {
+static const int samuel1_eblcr[16] = {
50, /* 0000 -> RESERVED */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -97,7 +97,7 @@ static const int __cpuinitconst samuel1_eblcr[16] = {
/*
* VIA C3 Samuel2 Stepping 1->15
*/
-static const int __cpuinitconst samuel2_eblcr[16] = {
+static const int samuel2_eblcr[16] = {
50, /* 0000 -> 5.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -119,7 +119,7 @@ static const int __cpuinitconst samuel2_eblcr[16] = {
/*
* VIA C3 Ezra
*/
-static const int __cpuinitconst ezra_mults[16] = {
+static const int ezra_mults[16] = {
100, /* 0000 -> 10.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -138,7 +138,7 @@ static const int __cpuinitconst ezra_mults[16] = {
120, /* 1111 -> 12.0x */
};
-static const int __cpuinitconst ezra_eblcr[16] = {
+static const int ezra_eblcr[16] = {
50, /* 0000 -> 5.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -160,7 +160,7 @@ static const int __cpuinitconst ezra_eblcr[16] = {
/*
* VIA C3 (Ezra-T) [C5M].
*/
-static const int __cpuinitconst ezrat_mults[32] = {
+static const int ezrat_mults[32] = {
100, /* 0000 -> 10.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -196,7 +196,7 @@ static const int __cpuinitconst ezrat_mults[32] = {
-1, /* 1111 -> RESERVED (12.0x) */
};
-static const int __cpuinitconst ezrat_eblcr[32] = {
+static const int ezrat_eblcr[32] = {
50, /* 0000 -> 5.0x */
30, /* 0001 -> 3.0x */
40, /* 0010 -> 4.0x */
@@ -235,7 +235,7 @@ static const int __cpuinitconst ezrat_eblcr[32] = {
/*
* VIA C3 Nehemiah */
-static const int __cpuinitconst nehemiah_mults[32] = {
+static const int nehemiah_mults[32] = {
100, /* 0000 -> 10.0x */
-1, /* 0001 -> 16.0x */
40, /* 0010 -> 4.0x */
@@ -270,7 +270,7 @@ static const int __cpuinitconst nehemiah_mults[32] = {
-1, /* 1111 -> 12.0x */
};
-static const int __cpuinitconst nehemiah_eblcr[32] = {
+static const int nehemiah_eblcr[32] = {
50, /* 0000 -> 5.0x */
160, /* 0001 -> 16.0x */
40, /* 0010 -> 4.0x */
@@ -315,7 +315,7 @@ struct mV_pos {
unsigned short pos;
};
-static const struct mV_pos __cpuinitconst vrm85_mV[32] = {
+static const struct mV_pos vrm85_mV[32] = {
{1250, 8}, {1200, 6}, {1150, 4}, {1100, 2},
{1050, 0}, {1800, 30}, {1750, 28}, {1700, 26},
{1650, 24}, {1600, 22}, {1550, 20}, {1500, 18},
@@ -326,14 +326,14 @@ static const struct mV_pos __cpuinitconst vrm85_mV[32] = {
{1475, 17}, {1425, 15}, {1375, 13}, {1325, 11}
};
-static const unsigned char __cpuinitconst mV_vrm85[32] = {
+static const unsigned char mV_vrm85[32] = {
0x04, 0x14, 0x03, 0x13, 0x02, 0x12, 0x01, 0x11,
0x00, 0x10, 0x0f, 0x1f, 0x0e, 0x1e, 0x0d, 0x1d,
0x0c, 0x1c, 0x0b, 0x1b, 0x0a, 0x1a, 0x09, 0x19,
0x08, 0x18, 0x07, 0x17, 0x06, 0x16, 0x05, 0x15
};
-static const struct mV_pos __cpuinitconst mobilevrm_mV[32] = {
+static const struct mV_pos mobilevrm_mV[32] = {
{1750, 31}, {1700, 30}, {1650, 29}, {1600, 28},
{1550, 27}, {1500, 26}, {1450, 25}, {1400, 24},
{1350, 23}, {1300, 22}, {1250, 21}, {1200, 20},
@@ -344,7 +344,7 @@ static const struct mV_pos __cpuinitconst mobilevrm_mV[32] = {
{675, 3}, {650, 2}, {625, 1}, {600, 0}
};
-static const unsigned char __cpuinitconst mV_mobilevrm[32] = {
+static const unsigned char mV_mobilevrm[32] = {
0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
diff --git a/drivers/cpufreq/longrun.c b/drivers/cpufreq/longrun.c
index 8bc9f5fbbaeb..074971b12635 100644
--- a/drivers/cpufreq/longrun.c
+++ b/drivers/cpufreq/longrun.c
@@ -33,7 +33,7 @@ static unsigned int longrun_low_freq, longrun_high_freq;
* Reads the current LongRun policy by access to MSR_TMTA_LONGRUN_FLAGS
* and MSR_TMTA_LONGRUN_CTRL
*/
-static void __cpuinit longrun_get_policy(struct cpufreq_policy *policy)
+static void longrun_get_policy(struct cpufreq_policy *policy)
{
u32 msr_lo, msr_hi;
@@ -129,9 +129,7 @@ static int longrun_verify_policy(struct cpufreq_policy *policy)
return -EINVAL;
policy->cpu = 0;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
if ((policy->policy != CPUFREQ_POLICY_POWERSAVE) &&
(policy->policy != CPUFREQ_POLICY_PERFORMANCE))
@@ -163,7 +161,7 @@ static unsigned int longrun_get(unsigned int cpu)
* TMTA rules:
* performance_pctg = (target_freq - low_freq)/(high_freq - low_freq)
*/
-static int __cpuinit longrun_determine_freqs(unsigned int *low_freq,
+static int longrun_determine_freqs(unsigned int *low_freq,
unsigned int *high_freq)
{
u32 msr_lo, msr_hi;
@@ -256,7 +254,7 @@ static int __cpuinit longrun_determine_freqs(unsigned int *low_freq,
}
-static int __cpuinit longrun_cpu_init(struct cpufreq_policy *policy)
+static int longrun_cpu_init(struct cpufreq_policy *policy)
{
int result = 0;
@@ -286,7 +284,6 @@ static struct cpufreq_driver longrun_driver = {
.get = longrun_get,
.init = longrun_cpu_init,
.name = "longrun",
- .owner = THIS_MODULE,
};
static const struct x86_cpu_id longrun_ids[] = {
diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c
index d53912768946..e65de91b534b 100644
--- a/drivers/cpufreq/loongson2_cpufreq.c
+++ b/drivers/cpufreq/loongson2_cpufreq.c
@@ -72,7 +72,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
freq =
((cpu_clock_freq / 1000) *
- loongson2_clockmod_table[newstate].index) / 8;
+ loongson2_clockmod_table[newstate].driver_data) / 8;
if (freq < policy->min || freq > policy->max)
return -EINVAL;
@@ -157,7 +157,6 @@ static struct freq_attr *loongson2_table_attr[] = {
};
static struct cpufreq_driver loongson2_cpufreq_driver = {
- .owner = THIS_MODULE,
.name = "loongson2",
.init = loongson2_cpufreq_cpu_init,
.verify = loongson2_cpufreq_verify,
diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c
index cdd62915efaf..41c601f4631e 100644
--- a/drivers/cpufreq/maple-cpufreq.c
+++ b/drivers/cpufreq/maple-cpufreq.c
@@ -190,7 +190,6 @@ static int maple_cpufreq_cpu_init(struct cpufreq_policy *policy)
static struct cpufreq_driver maple_cpufreq_driver = {
.name = "maple",
- .owner = THIS_MODULE,
.flags = CPUFREQ_CONST_LOOPS,
.init = maple_cpufreq_cpu_init,
.verify = maple_cpufreq_verify,
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 0279d18a57f9..1814318e1aaf 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -22,7 +22,7 @@
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/cpu.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -108,14 +108,14 @@ static int omap_target(struct cpufreq_policy *policy,
if (mpu_reg) {
rcu_read_lock();
- opp = opp_find_freq_ceil(mpu_dev, &freq);
+ opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq);
if (IS_ERR(opp)) {
rcu_read_unlock();
dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
__func__, freqs.new);
return -EINVAL;
}
- volt = opp_get_voltage(opp);
+ volt = dev_pm_opp_get_voltage(opp);
rcu_read_unlock();
tol = volt * OPP_TOLERANCE / 100;
volt_old = regulator_get_voltage(mpu_reg);
@@ -162,10 +162,10 @@ done:
static inline void freq_table_free(void)
{
if (atomic_dec_and_test(&freq_table_users))
- opp_free_cpufreq_table(mpu_dev, &freq_table);
+ dev_pm_opp_free_cpufreq_table(mpu_dev, &freq_table);
}
-static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
+static int omap_cpu_init(struct cpufreq_policy *policy)
{
int result = 0;
@@ -181,7 +181,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
policy->cur = omap_getspeed(policy->cpu);
if (!freq_table)
- result = opp_init_cpufreq_table(mpu_dev, &freq_table);
+ result = dev_pm_opp_init_cpufreq_table(mpu_dev, &freq_table);
if (result) {
dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n",
diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c
index 421ef37d0bb3..2f0a2a65c37f 100644
--- a/drivers/cpufreq/p4-clockmod.c
+++ b/drivers/cpufreq/p4-clockmod.c
@@ -118,7 +118,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
return -EINVAL;
freqs.old = cpufreq_p4_get(policy->cpu);
- freqs.new = stock_freq * p4clockmod_table[newstate].index / 8;
+ freqs.new = stock_freq * p4clockmod_table[newstate].driver_data / 8;
if (freqs.new == freqs.old)
return 0;
@@ -131,7 +131,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
* Developer's Manual, Volume 3
*/
for_each_cpu(i, policy->cpus)
- cpufreq_p4_setdc(i, p4clockmod_table[newstate].index);
+ cpufreq_p4_setdc(i, p4clockmod_table[newstate].driver_data);
/* notifiers */
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
@@ -279,7 +279,6 @@ static struct cpufreq_driver p4clockmod_driver = {
.exit = cpufreq_p4_cpu_exit,
.get = cpufreq_p4_get,
.name = "p4-clockmod",
- .owner = THIS_MODULE,
.attr = p4clockmod_attr,
};
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index 0de00081a81e..dce5533efd16 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -111,8 +111,7 @@ static struct pcc_cpu __percpu *pcc_cpu_info;
static int pcc_cpufreq_verify(struct cpufreq_policy *policy)
{
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
return 0;
}
@@ -585,7 +584,6 @@ static struct cpufreq_driver pcc_cpufreq_driver = {
.init = pcc_cpufreq_cpu_init,
.exit = pcc_cpufreq_cpu_exit,
.name = "pcc-cpufreq",
- .owner = THIS_MODULE,
};
static int __init pcc_cpufreq_init(void)
diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c
index ea0222a45b7b..85f1c8c25ddc 100644
--- a/drivers/cpufreq/powernow-k6.c
+++ b/drivers/cpufreq/powernow-k6.c
@@ -58,7 +58,7 @@ static int powernow_k6_get_cpu_multiplier(void)
msrval = POWERNOW_IOPORT + 0x0;
wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
- return clock_ratio[(invalue >> 5)&7].index;
+ return clock_ratio[(invalue >> 5)&7].driver_data;
}
@@ -75,13 +75,13 @@ static void powernow_k6_set_state(struct cpufreq_policy *policy,
unsigned long msrval;
struct cpufreq_freqs freqs;
- if (clock_ratio[best_i].index > max_multiplier) {
+ if (clock_ratio[best_i].driver_data > max_multiplier) {
printk(KERN_ERR PFX "invalid target frequency\n");
return;
}
freqs.old = busfreq * powernow_k6_get_cpu_multiplier();
- freqs.new = busfreq * clock_ratio[best_i].index;
+ freqs.new = busfreq * clock_ratio[best_i].driver_data;
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
@@ -156,7 +156,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
/* table init */
for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
- f = clock_ratio[i].index;
+ f = clock_ratio[i].driver_data;
if (f > max_multiplier)
clock_ratio[i].frequency = CPUFREQ_ENTRY_INVALID;
else
@@ -207,7 +207,6 @@ static struct cpufreq_driver powernow_k6_driver = {
.exit = powernow_k6_cpu_exit,
.get = powernow_k6_get,
.name = "powernow-k6",
- .owner = THIS_MODULE,
.attr = powernow_k6_attr,
};
diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c
index 53888dacbe58..14ce480be8ab 100644
--- a/drivers/cpufreq/powernow-k7.c
+++ b/drivers/cpufreq/powernow-k7.c
@@ -177,7 +177,7 @@ static int get_ranges(unsigned char *pst)
unsigned int speed;
u8 fid, vid;
- powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ powernow_table = kzalloc((sizeof(*powernow_table) *
(number_scales + 1)), GFP_KERNEL);
if (!powernow_table)
return -ENOMEM;
@@ -186,7 +186,7 @@ static int get_ranges(unsigned char *pst)
fid = *pst++;
powernow_table[j].frequency = (fsb * fid_codes[fid]) / 10;
- powernow_table[j].index = fid; /* lower 8 bits */
+ powernow_table[j].driver_data = fid; /* lower 8 bits */
speed = powernow_table[j].frequency;
@@ -203,7 +203,7 @@ static int get_ranges(unsigned char *pst)
maximum_speed = speed;
vid = *pst++;
- powernow_table[j].index |= (vid << 8); /* upper 8 bits */
+ powernow_table[j].driver_data |= (vid << 8); /* upper 8 bits */
pr_debug(" FID: 0x%x (%d.%dx [%dMHz]) "
"VID: 0x%x (%d.%03dV)\n", fid, fid_codes[fid] / 10,
@@ -212,7 +212,7 @@ static int get_ranges(unsigned char *pst)
mobile_vid_table[vid]%1000);
}
powernow_table[number_scales].frequency = CPUFREQ_TABLE_END;
- powernow_table[number_scales].index = 0;
+ powernow_table[number_scales].driver_data = 0;
return 0;
}
@@ -260,8 +260,8 @@ static void change_speed(struct cpufreq_policy *policy, unsigned int index)
* vid are the upper 8 bits.
*/
- fid = powernow_table[index].index & 0xFF;
- vid = (powernow_table[index].index & 0xFF00) >> 8;
+ fid = powernow_table[index].driver_data & 0xFF;
+ vid = (powernow_table[index].driver_data & 0xFF00) >> 8;
rdmsrl(MSR_K7_FID_VID_STATUS, fidvidstatus.val);
cfid = fidvidstatus.bits.CFID;
@@ -309,8 +309,7 @@ static int powernow_acpi_init(void)
goto err0;
}
- acpi_processor_perf = kzalloc(sizeof(struct acpi_processor_performance),
- GFP_KERNEL);
+ acpi_processor_perf = kzalloc(sizeof(*acpi_processor_perf), GFP_KERNEL);
if (!acpi_processor_perf) {
retval = -ENOMEM;
goto err0;
@@ -346,7 +345,7 @@ static int powernow_acpi_init(void)
goto err2;
}
- powernow_table = kzalloc((sizeof(struct cpufreq_frequency_table) *
+ powernow_table = kzalloc((sizeof(*powernow_table) *
(number_scales + 1)), GFP_KERNEL);
if (!powernow_table) {
retval = -ENOMEM;
@@ -373,8 +372,8 @@ static int powernow_acpi_init(void)
fid = pc.bits.fid;
powernow_table[i].frequency = fsb * fid_codes[fid] / 10;
- powernow_table[i].index = fid; /* lower 8 bits */
- powernow_table[i].index |= (vid << 8); /* upper 8 bits */
+ powernow_table[i].driver_data = fid; /* lower 8 bits */
+ powernow_table[i].driver_data |= (vid << 8); /* upper 8 bits */
speed = powernow_table[i].frequency;
speed_mhz = speed / 1000;
@@ -417,7 +416,7 @@ static int powernow_acpi_init(void)
}
powernow_table[i].frequency = CPUFREQ_TABLE_END;
- powernow_table[i].index = 0;
+ powernow_table[i].driver_data = 0;
/* notify BIOS that we exist */
acpi_processor_notify_smm(THIS_MODULE);
@@ -497,7 +496,7 @@ static int powernow_decode_bios(int maxfid, int startvid)
"relevant to this CPU).\n",
psb->numpst);
- p += sizeof(struct psb_s);
+ p += sizeof(*psb);
pst = (struct pst_s *) p;
@@ -510,12 +509,12 @@ static int powernow_decode_bios(int maxfid, int startvid)
(maxfid == pst->maxfid) &&
(startvid == pst->startvid)) {
print_pst_entry(pst, j);
- p = (char *)pst + sizeof(struct pst_s);
+ p = (char *)pst + sizeof(*pst);
ret = get_ranges(p);
return ret;
} else {
unsigned int k;
- p = (char *)pst + sizeof(struct pst_s);
+ p = (char *)pst + sizeof(*pst);
for (k = 0; k < number_scales; k++)
p += 2;
}
@@ -563,7 +562,7 @@ static int powernow_verify(struct cpufreq_policy *policy)
* We will then get the same kind of behaviour already tested under
* the "well-known" other OS.
*/
-static int __cpuinit fixup_sgtc(void)
+static int fixup_sgtc(void)
{
unsigned int sgtc;
unsigned int m;
@@ -597,7 +596,7 @@ static unsigned int powernow_get(unsigned int cpu)
}
-static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
+static int acer_cpufreq_pst(const struct dmi_system_id *d)
{
printk(KERN_WARNING PFX
"%s laptop with broken PST tables in BIOS detected.\n",
@@ -615,7 +614,7 @@ static int __cpuinit acer_cpufreq_pst(const struct dmi_system_id *d)
* A BIOS update is all that can save them.
* Mention this, and disable cpufreq.
*/
-static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
+static struct dmi_system_id powernow_dmi_table[] = {
{
.callback = acer_cpufreq_pst,
.ident = "Acer Aspire",
@@ -627,7 +626,7 @@ static struct dmi_system_id __cpuinitdata powernow_dmi_table[] = {
{ }
};
-static int __cpuinit powernow_cpu_init(struct cpufreq_policy *policy)
+static int powernow_cpu_init(struct cpufreq_policy *policy)
{
union msr_fidvidstatus fidvidstatus;
int result;
@@ -717,7 +716,6 @@ static struct cpufreq_driver powernow_driver = {
.init = powernow_cpu_init,
.exit = powernow_cpu_exit,
.name = "powernow-k7",
- .owner = THIS_MODULE,
.attr = powernow_table_attr,
};
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index b828efe4b2f8..9669cbc980f5 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -584,9 +584,9 @@ static void print_basics(struct powernow_k8_data *data)
CPUFREQ_ENTRY_INVALID) {
printk(KERN_INFO PFX
"fid 0x%x (%d MHz), vid 0x%x\n",
- data->powernow_table[j].index & 0xff,
+ data->powernow_table[j].driver_data & 0xff,
data->powernow_table[j].frequency/1000,
- data->powernow_table[j].index >> 8);
+ data->powernow_table[j].driver_data >> 8);
}
}
if (data->batps)
@@ -623,7 +623,7 @@ static int fill_powernow_table(struct powernow_k8_data *data,
if (check_pst_table(data, pst, maxvid))
return -EINVAL;
- powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ powernow_table = kmalloc((sizeof(*powernow_table)
* (data->numps + 1)), GFP_KERNEL);
if (!powernow_table) {
printk(KERN_ERR PFX "powernow_table memory alloc failure\n");
@@ -632,13 +632,13 @@ static int fill_powernow_table(struct powernow_k8_data *data,
for (j = 0; j < data->numps; j++) {
int freq;
- powernow_table[j].index = pst[j].fid; /* lower 8 bits */
- powernow_table[j].index |= (pst[j].vid << 8); /* upper 8 bits */
+ powernow_table[j].driver_data = pst[j].fid; /* lower 8 bits */
+ powernow_table[j].driver_data |= (pst[j].vid << 8); /* upper 8 bits */
freq = find_khz_freq_from_fid(pst[j].fid);
powernow_table[j].frequency = freq;
}
powernow_table[data->numps].frequency = CPUFREQ_TABLE_END;
- powernow_table[data->numps].index = 0;
+ powernow_table[data->numps].driver_data = 0;
if (query_current_values_with_pending_wait(data)) {
kfree(powernow_table);
@@ -793,7 +793,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
}
/* fill in data->powernow_table */
- powernow_table = kmalloc((sizeof(struct cpufreq_frequency_table)
+ powernow_table = kmalloc((sizeof(*powernow_table)
* (data->acpi_data.state_count + 1)), GFP_KERNEL);
if (!powernow_table) {
pr_debug("powernow_table memory alloc failure\n");
@@ -810,7 +810,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data)
powernow_table[data->acpi_data.state_count].frequency =
CPUFREQ_TABLE_END;
- powernow_table[data->acpi_data.state_count].index = 0;
+ powernow_table[data->acpi_data.state_count].driver_data = 0;
data->powernow_table = powernow_table;
if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu)
@@ -865,7 +865,7 @@ static int fill_powernow_table_fidvid(struct powernow_k8_data *data,
pr_debug(" %d : fid 0x%x, vid 0x%x\n", i, fid, vid);
index = fid | (vid<<8);
- powernow_table[i].index = index;
+ powernow_table[i].driver_data = index;
freq = find_khz_freq_from_fid(fid);
powernow_table[i].frequency = freq;
@@ -941,8 +941,8 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
* the cpufreq frequency table in find_psb_table, vid
* are the upper 8 bits.
*/
- fid = data->powernow_table[index].index & 0xFF;
- vid = (data->powernow_table[index].index & 0xFF00) >> 8;
+ fid = data->powernow_table[index].driver_data & 0xFF;
+ vid = (data->powernow_table[index].driver_data & 0xFF00) >> 8;
pr_debug("table matched fid 0x%x, giving vid 0x%x\n", fid, vid);
@@ -1069,7 +1069,7 @@ struct init_on_cpu {
int rc;
};
-static void __cpuinit powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
+static void powernowk8_cpu_init_on_cpu(void *_init_on_cpu)
{
struct init_on_cpu *init_on_cpu = _init_on_cpu;
@@ -1096,7 +1096,7 @@ static const char missing_pss_msg[] =
FW_BUG PFX "If that doesn't help, try upgrading your BIOS.\n";
/* per CPU init entry point to the driver */
-static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
+static int powernowk8_cpu_init(struct cpufreq_policy *pol)
{
struct powernow_k8_data *data;
struct init_on_cpu init_on_cpu;
@@ -1106,7 +1106,7 @@ static int __cpuinit powernowk8_cpu_init(struct cpufreq_policy *pol)
if (rc)
return -ENODEV;
- data = kzalloc(sizeof(struct powernow_k8_data), GFP_KERNEL);
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
printk(KERN_ERR PFX "unable to alloc powernow_k8_data");
return -ENOMEM;
@@ -1233,6 +1233,7 @@ static struct freq_attr *powernow_k8_attr[] = {
};
static struct cpufreq_driver cpufreq_amd64_driver = {
+ .flags = CPUFREQ_ASYNC_NOTIFICATION,
.verify = powernowk8_verify,
.target = powernowk8_target,
.bios_limit = acpi_processor_get_bios_limit,
@@ -1240,7 +1241,6 @@ static struct cpufreq_driver cpufreq_amd64_driver = {
.exit = powernowk8_cpu_exit,
.get = powernowk8_get,
.name = "powernow-k8",
- .owner = THIS_MODULE,
.attr = powernow_k8_attr,
};
@@ -1263,7 +1263,7 @@ static void __request_acpi_cpufreq(void)
}
/* driver entry point for init */
-static int __cpuinit powernowk8_init(void)
+static int powernowk8_init(void)
{
unsigned int i, supported_cpus = 0;
int ret;
diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c
index e577a1dbbfcd..2e448f0bbdc5 100644
--- a/drivers/cpufreq/ppc_cbe_cpufreq.c
+++ b/drivers/cpufreq/ppc_cbe_cpufreq.c
@@ -106,7 +106,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
/* initialize frequency table */
for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
- cbe_freqs[i].frequency = max_freq / cbe_freqs[i].index;
+ cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data;
pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
}
@@ -165,7 +165,7 @@ static int cbe_cpufreq_target(struct cpufreq_policy *policy,
"1/%d of max frequency\n",
policy->cpu,
cbe_freqs[cbe_pmode_new].frequency,
- cbe_freqs[cbe_pmode_new].index);
+ cbe_freqs[cbe_pmode_new].driver_data);
rc = set_pmode(policy->cpu, cbe_pmode_new);
@@ -181,7 +181,6 @@ static struct cpufreq_driver cbe_cpufreq_driver = {
.init = cbe_cpufreq_cpu_init,
.exit = cbe_cpufreq_cpu_exit,
.name = "cbe-cpufreq",
- .owner = THIS_MODULE,
.flags = CPUFREQ_CONST_LOOPS,
};
diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c
index 9e5bc8e388a0..fb3981ac829f 100644
--- a/drivers/cpufreq/pxa2xx-cpufreq.c
+++ b/drivers/cpufreq/pxa2xx-cpufreq.c
@@ -420,7 +420,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
/* Generate pxa25x the run cpufreq_frequency_table struct */
for (i = 0; i < NUM_PXA25x_RUN_FREQS; i++) {
pxa255_run_freq_table[i].frequency = pxa255_run_freqs[i].khz;
- pxa255_run_freq_table[i].index = i;
+ pxa255_run_freq_table[i].driver_data = i;
}
pxa255_run_freq_table[i].frequency = CPUFREQ_TABLE_END;
@@ -428,7 +428,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
for (i = 0; i < NUM_PXA25x_TURBO_FREQS; i++) {
pxa255_turbo_freq_table[i].frequency =
pxa255_turbo_freqs[i].khz;
- pxa255_turbo_freq_table[i].index = i;
+ pxa255_turbo_freq_table[i].driver_data = i;
}
pxa255_turbo_freq_table[i].frequency = CPUFREQ_TABLE_END;
@@ -440,9 +440,9 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
if (freq > pxa27x_maxfreq)
break;
pxa27x_freq_table[i].frequency = freq;
- pxa27x_freq_table[i].index = i;
+ pxa27x_freq_table[i].driver_data = i;
}
- pxa27x_freq_table[i].index = i;
+ pxa27x_freq_table[i].driver_data = i;
pxa27x_freq_table[i].frequency = CPUFREQ_TABLE_END;
/*
diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c
index 15d60f857ad5..9c92ef032a9e 100644
--- a/drivers/cpufreq/pxa3xx-cpufreq.c
+++ b/drivers/cpufreq/pxa3xx-cpufreq.c
@@ -98,10 +98,10 @@ static int setup_freqs_table(struct cpufreq_policy *policy,
return -ENOMEM;
for (i = 0; i < num; i++) {
- table[i].index = i;
+ table[i].driver_data = i;
table[i].frequency = freqs[i].cpufreq_mhz * 1000;
}
- table[num].index = i;
+ table[num].driver_data = i;
table[num].frequency = CPUFREQ_TABLE_END;
pxa3xx_freqs = freqs;
diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c
index 4f1881eee3f1..600c9ad07cd4 100644
--- a/drivers/cpufreq/s3c2416-cpufreq.c
+++ b/drivers/cpufreq/s3c2416-cpufreq.c
@@ -231,7 +231,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int relation)
{
struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
- struct cpufreq_freqs freqs;
+ unsigned int new_freq;
int idx, ret, to_dvs = 0;
unsigned int i;
@@ -244,7 +244,7 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
if (ret != 0)
goto out;
- idx = s3c_freq->freq_table[i].index;
+ idx = s3c_freq->freq_table[i].driver_data;
if (idx == SOURCE_HCLK)
to_dvs = 1;
@@ -256,25 +256,14 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
goto out;
}
- freqs.flags = 0;
- freqs.old = s3c_freq->is_dvs ? FREQ_DVS
- : clk_get_rate(s3c_freq->armclk) / 1000;
-
/* When leavin dvs mode, always switch the armdiv to the hclk rate
* The S3C2416 has stability issues when switching directly to
* higher frequencies.
*/
- freqs.new = (s3c_freq->is_dvs && !to_dvs)
+ new_freq = (s3c_freq->is_dvs && !to_dvs)
? clk_get_rate(s3c_freq->hclk) / 1000
: s3c_freq->freq_table[i].frequency;
- pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
-
- if (!to_dvs && freqs.old == freqs.new)
- goto out;
-
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
if (to_dvs) {
pr_debug("cpufreq: enter dvs\n");
ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
@@ -282,12 +271,10 @@ static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
pr_debug("cpufreq: leave dvs\n");
ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
} else {
- pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new);
- ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new);
+ pr_debug("cpufreq: change armdiv to %dkHz\n", new_freq);
+ ret = s3c2416_cpufreq_set_armdiv(s3c_freq, new_freq);
}
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
out:
mutex_unlock(&cpufreq_lock);
@@ -524,7 +511,6 @@ static struct freq_attr *s3c2416_cpufreq_attr[] = {
};
static struct cpufreq_driver s3c2416_cpufreq_driver = {
- .owner = THIS_MODULE,
.flags = 0,
.verify = s3c2416_cpufreq_verify_speed,
.target = s3c2416_cpufreq_set_target,
diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
index 27cacb524796..c53c6a6d5e97 100644
--- a/drivers/cpufreq/s3c64xx-cpufreq.c
+++ b/drivers/cpufreq/s3c64xx-cpufreq.c
@@ -87,7 +87,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
freqs.old = clk_get_rate(armclk) / 1000;
freqs.new = s3c64xx_freq_table[i].frequency;
freqs.flags = 0;
- dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].index];
+ dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[i].driver_data];
if (freqs.old == freqs.new)
return 0;
@@ -259,7 +259,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
}
static struct cpufreq_driver s3c64xx_cpufreq_driver = {
- .owner = THIS_MODULE,
.flags = 0,
.verify = s3c64xx_cpufreq_verify_speed,
.target = s3c64xx_cpufreq_set_target,
diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c
index f740b134d27b..d6f6c6f4efa7 100644
--- a/drivers/cpufreq/sc520_freq.c
+++ b/drivers/cpufreq/sc520_freq.c
@@ -71,7 +71,7 @@ static void sc520_freq_set_cpu_state(struct cpufreq_policy *policy,
local_irq_disable();
clockspeed_reg = *cpuctl & ~0x03;
- *cpuctl = clockspeed_reg | sc520_freq_table[state].index;
+ *cpuctl = clockspeed_reg | sc520_freq_table[state].driver_data;
local_irq_enable();
@@ -147,7 +147,6 @@ static struct cpufreq_driver sc520_freq_driver = {
.init = sc520_freq_cpu_init,
.exit = sc520_freq_cpu_exit,
.name = "sc520_freq",
- .owner = THIS_MODULE,
.attr = sc520_freq_attr,
};
diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c
index 73adb64651e8..f671aa1e0bcb 100644
--- a/drivers/cpufreq/sh-cpufreq.c
+++ b/drivers/cpufreq/sh-cpufreq.c
@@ -87,15 +87,12 @@ static int sh_cpufreq_verify(struct cpufreq_policy *policy)
if (freq_table)
return cpufreq_frequency_table_verify(policy, freq_table);
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
+ cpufreq_verify_within_cpu_limits(policy);
policy->min = (clk_round_rate(cpuclk, 1) + 500) / 1000;
policy->max = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
- cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
- policy->cpuinfo.max_freq);
-
+ cpufreq_verify_within_cpu_limits(policy);
return 0;
}
@@ -160,7 +157,6 @@ static struct freq_attr *sh_freq_attr[] = {
};
static struct cpufreq_driver sh_cpufreq_driver = {
- .owner = THIS_MODULE,
.name = "sh",
.get = sh_cpufreq_get,
.target = sh_cpufreq_target,
diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c
index 306ae462bba6..8ea3a1b9098e 100644
--- a/drivers/cpufreq/sparc-us2e-cpufreq.c
+++ b/drivers/cpufreq/sparc-us2e-cpufreq.c
@@ -252,7 +252,6 @@ static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy,
unsigned long new_bits, new_freq;
unsigned long clock_tick, divisor, old_divisor, estar;
cpumask_t cpus_allowed;
- struct cpufreq_freqs freqs;
cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
set_cpus_allowed_ptr(current, cpumask_of(cpu));
@@ -266,16 +265,10 @@ static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy,
old_divisor = estar_to_divisor(estar);
- freqs.old = clock_tick / old_divisor;
- freqs.new = new_freq;
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
if (old_divisor != divisor)
us2e_transition(estar, new_bits, clock_tick * 1000,
old_divisor, divisor);
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
set_cpus_allowed_ptr(current, &cpus_allowed);
}
@@ -308,17 +301,17 @@ static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
struct cpufreq_frequency_table *table =
&us2e_freq_table[cpu].table[0];
- table[0].index = 0;
+ table[0].driver_data = 0;
table[0].frequency = clock_tick / 1;
- table[1].index = 1;
+ table[1].driver_data = 1;
table[1].frequency = clock_tick / 2;
- table[2].index = 2;
+ table[2].driver_data = 2;
table[2].frequency = clock_tick / 4;
- table[2].index = 3;
+ table[2].driver_data = 3;
table[2].frequency = clock_tick / 6;
- table[2].index = 4;
+ table[2].driver_data = 4;
table[2].frequency = clock_tick / 8;
- table[2].index = 5;
+ table[2].driver_data = 5;
table[3].frequency = CPUFREQ_TABLE_END;
policy->cpuinfo.transition_latency = 0;
@@ -351,12 +344,11 @@ static int __init us2e_freq_init(void)
struct cpufreq_driver *driver;
ret = -ENOMEM;
- driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
if (!driver)
goto err_out;
- us2e_freq_table = kzalloc(
- (NR_CPUS * sizeof(struct us2e_freq_percpu_info)),
+ us2e_freq_table = kzalloc((NR_CPUS * sizeof(*us2e_freq_table)),
GFP_KERNEL);
if (!us2e_freq_table)
goto err_out;
@@ -366,7 +358,6 @@ static int __init us2e_freq_init(void)
driver->target = us2e_freq_target;
driver->get = us2e_freq_get;
driver->exit = us2e_freq_cpu_exit;
- driver->owner = THIS_MODULE,
strcpy(driver->name, "UltraSPARC-IIe");
cpufreq_us2e_driver = driver;
diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c
index c71ee142347a..241650ac95f2 100644
--- a/drivers/cpufreq/sparc-us3-cpufreq.c
+++ b/drivers/cpufreq/sparc-us3-cpufreq.c
@@ -99,7 +99,6 @@ static void us3_set_cpu_divider_index(struct cpufreq_policy *policy,
unsigned int cpu = policy->cpu;
unsigned long new_bits, new_freq, reg;
cpumask_t cpus_allowed;
- struct cpufreq_freqs freqs;
cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
set_cpus_allowed_ptr(current, cpumask_of(cpu));
@@ -125,16 +124,10 @@ static void us3_set_cpu_divider_index(struct cpufreq_policy *policy,
reg = read_safari_cfg();
- freqs.old = get_current_freq(cpu, reg);
- freqs.new = new_freq;
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
reg &= ~SAFARI_CFG_DIV_MASK;
reg |= new_bits;
write_safari_cfg(reg);
- cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
set_cpus_allowed_ptr(current, &cpus_allowed);
}
@@ -169,13 +162,13 @@ static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
struct cpufreq_frequency_table *table =
&us3_freq_table[cpu].table[0];
- table[0].index = 0;
+ table[0].driver_data = 0;
table[0].frequency = clock_tick / 1;
- table[1].index = 1;
+ table[1].driver_data = 1;
table[1].frequency = clock_tick / 2;
- table[2].index = 2;
+ table[2].driver_data = 2;
table[2].frequency = clock_tick / 32;
- table[3].index = 0;
+ table[3].driver_data = 0;
table[3].frequency = CPUFREQ_TABLE_END;
policy->cpuinfo.transition_latency = 0;
@@ -212,12 +205,11 @@ static int __init us3_freq_init(void)
struct cpufreq_driver *driver;
ret = -ENOMEM;
- driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
+ driver = kzalloc(sizeof(*driver), GFP_KERNEL);
if (!driver)
goto err_out;
- us3_freq_table = kzalloc(
- (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
+ us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
GFP_KERNEL);
if (!us3_freq_table)
goto err_out;
@@ -227,7 +219,6 @@ static int __init us3_freq_init(void)
driver->target = us3_freq_target;
driver->get = us3_freq_get;
driver->exit = us3_freq_cpu_exit;
- driver->owner = THIS_MODULE,
strcpy(driver->name, "UltraSPARC-III");
cpufreq_us3_driver = driver;
diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c
index 156829f4576d..c3efa7f2a908 100644
--- a/drivers/cpufreq/spear-cpufreq.c
+++ b/drivers/cpufreq/spear-cpufreq.c
@@ -250,11 +250,11 @@ static int spear_cpufreq_driver_init(void)
}
for (i = 0; i < cnt; i++) {
- freq_tbl[i].index = i;
+ freq_tbl[i].driver_data = i;
freq_tbl[i].frequency = be32_to_cpup(val++);
}
- freq_tbl[i].index = i;
+ freq_tbl[i].driver_data = i;
freq_tbl[i].frequency = CPUFREQ_TABLE_END;
spear_cpufreq.freq_tbl = freq_tbl;
diff --git a/drivers/cpufreq/speedstep-centrino.c b/drivers/cpufreq/speedstep-centrino.c
index 618e6f417b1c..f897d5105842 100644
--- a/drivers/cpufreq/speedstep-centrino.c
+++ b/drivers/cpufreq/speedstep-centrino.c
@@ -79,11 +79,11 @@ static struct cpufreq_driver centrino_driver;
/* Computes the correct form for IA32_PERF_CTL MSR for a particular
frequency/voltage operating point; frequency in MHz, volts in mV.
- This is stored as "index" in the structure. */
+ This is stored as "driver_data" in the structure. */
#define OP(mhz, mv) \
{ \
.frequency = (mhz) * 1000, \
- .index = (((mhz)/100) << 8) | ((mv - 700) / 16) \
+ .driver_data = (((mhz)/100) << 8) | ((mv - 700) / 16) \
}
/*
@@ -307,7 +307,7 @@ static unsigned extract_clock(unsigned msr, unsigned int cpu, int failsafe)
per_cpu(centrino_model, cpu)->op_points[i].frequency
!= CPUFREQ_TABLE_END;
i++) {
- if (msr == per_cpu(centrino_model, cpu)->op_points[i].index)
+ if (msr == per_cpu(centrino_model, cpu)->op_points[i].driver_data)
return per_cpu(centrino_model, cpu)->
op_points[i].frequency;
}
@@ -501,7 +501,7 @@ static int centrino_target (struct cpufreq_policy *policy,
break;
}
- msr = per_cpu(centrino_model, cpu)->op_points[newstate].index;
+ msr = per_cpu(centrino_model, cpu)->op_points[newstate].driver_data;
if (first_cpu) {
rdmsr_on_cpu(good_cpu, MSR_IA32_PERF_CTL, &oldmsr, &h);
@@ -575,7 +575,6 @@ static struct cpufreq_driver centrino_driver = {
.target = centrino_target,
.get = get_cur_freq,
.attr = centrino_attr,
- .owner = THIS_MODULE,
};
/*
diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c
index e2e5aa971452..5355abb69afc 100644
--- a/drivers/cpufreq/speedstep-ich.c
+++ b/drivers/cpufreq/speedstep-ich.c
@@ -378,7 +378,6 @@ static struct cpufreq_driver speedstep_driver = {
.init = speedstep_cpu_init,
.exit = speedstep_cpu_exit,
.get = speedstep_get,
- .owner = THIS_MODULE,
.attr = speedstep_attr,
};
diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c
index f5a6b70ee6c0..abfba4f731eb 100644
--- a/drivers/cpufreq/speedstep-smi.c
+++ b/drivers/cpufreq/speedstep-smi.c
@@ -375,7 +375,6 @@ static struct cpufreq_driver speedstep_driver = {
.exit = speedstep_cpu_exit,
.get = speedstep_get,
.resume = speedstep_resume,
- .owner = THIS_MODULE,
.attr = speedstep_attr,
};
diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c
index 12fc904d7dab..6e56ae0997e2 100644
--- a/drivers/cpufreq/unicore2-cpufreq.c
+++ b/drivers/cpufreq/unicore2-cpufreq.c
@@ -29,9 +29,7 @@ int ucv2_verify_speed(struct cpufreq_policy *policy)
if (policy->cpu)
return -EINVAL;
- cpufreq_verify_within_limits(policy,
- policy->cpuinfo.min_freq, policy->cpuinfo.max_freq);
-
+ cpufreq_verify_within_cpu_limits(policy);
return 0;
}
diff --git a/drivers/crypto/msm/qce50.c b/drivers/crypto/msm/qce50.c
index c2d3704f2960..e22a2874e542 100644
--- a/drivers/crypto/msm/qce50.c
+++ b/drivers/crypto/msm/qce50.c
@@ -2618,8 +2618,9 @@ static void qce_sps_release_bam(struct qce_device *pce_dev)
pr_debug("delete bam 0x%x\n", pbam->bam_mem);
list_del(&pbam->qlist);
kfree(pbam);
- pce_dev->pbam = NULL;
+
ret:
+ pce_dev->pbam = NULL;
mutex_unlock(&bam_register_lock);
}
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index b6e453ed7387..cb07ca58c1e1 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -380,10 +380,11 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
int ret = 0;
if (high_bw_req && pengine->high_bw_req == false) {
+ pm_stay_awake(&pengine->pdev->dev);
ret = qce_enable_clk(pengine->qce);
if (ret) {
pr_err("%s Unable enable clk\n", __func__);
- return;
+ goto clk_err;
}
ret = msm_bus_scale_client_update_request(
pengine->bus_scale_handle, 1);
@@ -391,7 +392,7 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
pr_err("%s Unable to set to high bandwidth\n",
__func__);
qce_disable_clk(pengine->qce);
- return;
+ goto clk_err;
}
pengine->high_bw_req = true;
} else if (high_bw_req == false && pengine->high_bw_req == true) {
@@ -400,7 +401,7 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
if (ret) {
pr_err("%s Unable to set to low bandwidth\n",
__func__);
- return;
+ goto clk_err;
}
ret = qce_disable_clk(pengine->qce);
if (ret) {
@@ -410,10 +411,16 @@ static void qcrypto_ce_set_bus(struct crypto_engine *pengine,
if (ret)
pr_err("%s Unable to set to high bandwidth\n",
__func__);
- return;
+ goto clk_err;
}
pengine->high_bw_req = false;
+ pm_relax(&pengine->pdev->dev);
}
+ return;
+clk_err:
+ pm_relax(&pengine->pdev->dev);
+ return;
+
}
static void qcrypto_bw_scale_down_timer_callback(unsigned long data)
@@ -804,6 +811,7 @@ static void _qcrypto_remove_engine(struct crypto_engine *pengine)
tasklet_kill(&pengine->done_tasklet);
cancel_work_sync(&pengine->low_bw_req_ws);
del_timer_sync(&pengine->bw_scale_down_timer);
+ device_init_wakeup(&pengine->pdev->dev, false);
if (pengine->bus_scale_handle != 0)
msm_bus_scale_unregister_client(pengine->bus_scale_handle);
@@ -3792,6 +3800,9 @@ static int _qcrypto_probe(struct platform_device *pdev)
INIT_WORK(&pengine->low_bw_req_ws, qcrypto_low_bw_req_work);
pengine->bw_scale_down_timer.function =
qcrypto_bw_scale_down_timer_callback;
+
+ device_init_wakeup(&pengine->pdev->dev, true);
+
tasklet_init(&pengine->done_tasklet, req_done, (unsigned long)pengine);
crypto_init_queue(&pengine->req_queue, 50);
diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
index 4b705c74be35..15fa143312fe 100644
--- a/drivers/devfreq/Kconfig
+++ b/drivers/devfreq/Kconfig
@@ -71,6 +71,14 @@ config DEVFREQ_GOV_MSM_ADRENO_TZ
Sets the frequency using a "on-demand" algorithm.
This governor is unlikely to be useful for other devices.
+config DEVFREQ_GOV_MSM_CPUFREQ
+ bool "MSM CPUfreq"
+ depends on CPU_FREQ_MSM
+ help
+ MSM CPUfreq based governor for CPU bandwidth voting. Sets the CPU
+ to DDR BW vote based on the current CPU frequency. This governor
+ is unlikely to be useful for non-MSM devices.
+
comment "DEVFREQ Drivers"
config ARM_EXYNOS4_BUS_DEVFREQ
diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
index 29b48ff42db4..3a960a4fbb38 100644
--- a/drivers/devfreq/Makefile
+++ b/drivers/devfreq/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o
obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o
obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o
obj-$(CONFIG_DEVFREQ_GOV_MSM_ADRENO_TZ) += governor_msm_adreno_tz.o
+obj-$(CONFIG_DEVFREQ_GOV_MSM_CPUFREQ) += governor_msm_cpufreq.o
# DEVFREQ Drivers
obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos4_bus.o
diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
index d5b30e3d5a5c..802f39f9a698 100644
--- a/drivers/devfreq/devfreq.c
+++ b/drivers/devfreq/devfreq.c
@@ -18,7 +18,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/stat.h>
-#include <linux/opp.h>
+#include <linux/pm_opp.h>
#include <linux/devfreq.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
@@ -936,7 +936,7 @@ static ssize_t show_available_freqs(struct device *d,
rcu_read_lock();
do {
- opp = opp_find_freq_ceil(dev, &freq);
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
if (IS_ERR(opp))
break;
@@ -1062,18 +1062,18 @@ struct opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq,
if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) {
/* The freq is an upper bound. opp should be lower */
- opp = opp_find_freq_floor(dev, freq);
+ opp = dev_pm_opp_find_freq_floor(dev, freq);
/* If not available, use the closest opp */
if (opp == ERR_PTR(-ERANGE))
- opp = opp_find_freq_ceil(dev, freq);
+ opp = dev_pm_opp_find_freq_ceil(dev, freq);
} else {
/* The freq is an lower bound. opp should be higher */
- opp = opp_find_freq_ceil(dev, freq);
+ opp = dev_pm_opp_find_freq_ceil(dev, freq);
/* If not available, use the closest opp */
if (opp == ERR_PTR(-ERANGE))
- opp = opp_find_freq_floor(dev, freq);
+ opp = dev_pm_opp_find_freq_floor(dev, freq);
}
return opp;
@@ -1092,7 +1092,7 @@ int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
int ret = 0;
rcu_read_lock();
- nh = opp_get_notifier(dev);
+ nh = dev_pm_opp_get_notifier(dev);
if (IS_ERR(nh))
ret = PTR_ERR(nh);
rcu_read_unlock();
@@ -1118,7 +1118,7 @@ int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
int ret = 0;
rcu_read_lock();
- nh = opp_get_notifier(dev);
+ nh = dev_pm_opp_get_notifier(dev);
if (IS_ERR(nh))
ret = PTR_ERR(nh);
rcu_read_unlock();
diff --git a/drivers/devfreq/governor_msm_cpufreq.c b/drivers/devfreq/governor_msm_cpufreq.c
new file mode 100644
index 000000000000..9b13e26a3c3e
--- /dev/null
+++ b/drivers/devfreq/governor_msm_cpufreq.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/devfreq.h>
+#include <mach/cpufreq.h>
+#include "governor.h"
+
+DEFINE_MUTEX(df_lock);
+static struct devfreq *df;
+
+static int devfreq_msm_cpufreq_get_freq(struct devfreq *df,
+ unsigned long *freq,
+ u32 *flag)
+{
+ *freq = msm_cpufreq_get_bw();
+ return 0;
+}
+
+int devfreq_msm_cpufreq_update_bw(void)
+{
+ int ret = 0;
+
+ mutex_lock(&df_lock);
+ if (df) {
+ mutex_lock(&df->lock);
+ ret = update_devfreq(df);
+ mutex_unlock(&df->lock);
+ }
+ mutex_unlock(&df_lock);
+ return ret;
+}
+
+static int devfreq_msm_cpufreq_ev_handler(struct devfreq *devfreq,
+ unsigned int event, void *data)
+{
+ int ret;
+
+ switch (event) {
+ case DEVFREQ_GOV_START:
+ mutex_lock(&df_lock);
+ df = devfreq;
+ mutex_unlock(&df_lock);
+
+ ret = devfreq_msm_cpufreq_update_bw();
+ if (ret) {
+ pr_err("Unable to update BW! Gov start failed!\n");
+ return ret;
+ }
+
+ devfreq_monitor_stop(df);
+ pr_debug("Enabled MSM CPUfreq governor\n");
+ break;
+
+ case DEVFREQ_GOV_STOP:
+ mutex_lock(&df_lock);
+ df = NULL;
+ mutex_unlock(&df_lock);
+
+ pr_debug("Disabled MSM CPUfreq governor\n");
+ break;
+ }
+
+ return 0;
+}
+
+static struct devfreq_governor devfreq_msm_cpufreq = {
+ .name = "msm_cpufreq",
+ .get_target_freq = devfreq_msm_cpufreq_get_freq,
+ .event_handler = devfreq_msm_cpufreq_ev_handler,
+};
+
+int register_devfreq_msm_cpufreq(void)
+{
+ return devfreq_add_governor(&devfreq_msm_cpufreq);
+}
diff --git a/drivers/esoc/Kconfig b/drivers/esoc/Kconfig
index 2d30aee4a102..f66d5576b22b 100644
--- a/drivers/esoc/Kconfig
+++ b/drivers/esoc/Kconfig
@@ -24,4 +24,20 @@ config ESOC_DEBUG
help
Say yes here to enable debugging support in the ESOC framework
and individual esoc drivers.
+
+config ESOC_MDM_4x
+ bool "Add support for external mdm9x25/mdm9x35"
+ help
+ In some qualcomm boards, an external modem such as mdm9x25 or mdm9x35
+ is connected to a primary msm. The primary soc can control/monitor
+ the modem via gpios. The data communication with such modems can
+ occur over PCIE or HSIC.
+
+config ESOC_MDM_DRV
+ tristate "Command engine for 4x series external modems"
+ help
+ Provides a command engine to control the behavior of an external modem
+ such as mdm9x25/mdm9x35/QSC. Allows the primary soc to put the
+ external modem in a specific mode. Also listens for events on the
+ external modem.
endif
diff --git a/drivers/esoc/Makefile b/drivers/esoc/Makefile
index 8720bda39527..394930d7195a 100644
--- a/drivers/esoc/Makefile
+++ b/drivers/esoc/Makefile
@@ -3,4 +3,5 @@
ccflags-$(CONFIG_ESOC_DEBUG) := -DDEBUG
obj-$(CONFIG_ESOC) += esoc_bus.o
obj-$(CONFIG_ESOC_DEV) += esoc_dev.o
-
+obj-$(CONFIG_ESOC_MDM_4x) += esoc-mdm-4x.o
+obj-$(CONFIG_ESOC_MDM_DRV) += esoc-mdm-drv.o
diff --git a/drivers/esoc/esoc-mdm-4x.c b/drivers/esoc/esoc-mdm-4x.c
new file mode 100644
index 000000000000..1fba10c2d3c6
--- /dev/null
+++ b/drivers/esoc/esoc-mdm-4x.c
@@ -0,0 +1,857 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/gpio.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <mach/gpiomux.h>
+#include <mach/sysmon.h>
+#include "esoc.h"
+
+#define MDM_PBLRDY_CNT 20
+#define INVALID_GPIO (-1)
+#define MDM_GPIO(mdm, i) (mdm->gpios[i])
+#define MDM9x25_LABEL "MDM9x25"
+#define MDM9x25_HSIC "HSIC"
+#define MDM2AP_STATUS_TIMEOUT_MS 120000L
+#define MDM_MODEM_TIMEOUT 6000
+#define MDM_MODEM_DELTA 100
+#define DEF_RAMDUMP_TIMEOUT 120000
+#define DEF_RAMDUMP_DELAY 2000
+#define RD_BUF_SIZE 100
+#define SFR_MAX_RETRIES 10
+#define SFR_RETRY_INTERVAL 1000
+
+enum mdm_gpio {
+ AP2MDM_WAKEUP = 0,
+ AP2MDM_STATUS,
+ AP2MDM_SOFT_RESET,
+ AP2MDM_VDD_MIN,
+ AP2MDM_CHNLRDY,
+ AP2MDM_ERRFATAL,
+ AP2MDM_VDDMIN,
+ AP2MDM_PMIC_PWR_EN,
+ MDM2AP_WAKEUP,
+ MDM2AP_ERRFATAL,
+ MDM2AP_PBLRDY,
+ MDM2AP_STATUS,
+ MDM2AP_VDDMIN,
+ NUM_GPIOS,
+};
+
+enum gpio_update_config {
+ GPIO_UPDATE_BOOTING_CONFIG = 1,
+ GPIO_UPDATE_RUNNING_CONFIG,
+};
+
+enum irq_mask {
+ IRQ_ERRFATAL = 0x1,
+ IRQ_STATUS = 0x2,
+ IRQ_PBLRDY = 0x4,
+};
+
+struct mdm_ctrl {
+ unsigned gpios[NUM_GPIOS];
+ spinlock_t status_lock;
+ struct workqueue_struct *mdm_queue;
+ struct delayed_work mdm2ap_status_check_work;
+ struct work_struct mdm_status_work;
+ struct work_struct restart_reason_work;
+ struct completion debug_done;
+ struct device *dev;
+ struct gpiomux_setting *mdm2ap_status_gpio_run_cfg;
+ struct gpiomux_setting mdm2ap_status_old_config;
+ int mdm2ap_status_valid_old_config;
+ int soft_reset_inverted;
+ int errfatal_irq;
+ int status_irq;
+ int pblrdy_irq;
+ int debug;
+ bool debug_fail;
+ unsigned int dump_timeout_ms;
+ unsigned int ramdump_delay_ms;
+ int sysmon_subsys_id;
+ struct esoc_clink *esoc;
+ bool get_restart_reason;
+ unsigned long irq_mask;
+ bool ready;
+ u32 status;
+};
+
+struct mdm_ops {
+ struct esoc_clink_ops *clink_ops;
+ int (*config_hw)(struct mdm_ctrl *mdm,
+ const struct esoc_clink_ops const *ops,
+ struct platform_device *pdev);
+};
+
+static struct gpio_map {
+ const char *name;
+ int index;
+} gpio_map[] = {
+ {"qcom,mdm2ap-errfatal-gpio", MDM2AP_ERRFATAL},
+ {"qcom,ap2mdm-errfatal-gpio", AP2MDM_ERRFATAL},
+ {"qcom,mdm2ap-status-gpio", MDM2AP_STATUS},
+ {"qcom,ap2mdm-status-gpio", AP2MDM_STATUS},
+ {"qcom,mdm2ap-pblrdy-gpio", MDM2AP_PBLRDY},
+ {"qcom,ap2mdm-wakeup-gpio", AP2MDM_WAKEUP},
+ {"qcom,ap2mdm-chnlrdy-gpio", AP2MDM_CHNLRDY},
+ {"qcom,mdm2ap-wakeup-gpio", MDM2AP_WAKEUP},
+ {"qcom,ap2mdm-vddmin-gpio", AP2MDM_VDDMIN},
+ {"qcom,mdm2ap-vddmin-gpio", MDM2AP_VDDMIN},
+ {"qcom,ap2mdm-pmic-pwr-en-gpio", AP2MDM_PMIC_PWR_EN},
+};
+
+/* Required gpios */
+static const int required_gpios[] = {
+ MDM2AP_ERRFATAL,
+ AP2MDM_ERRFATAL,
+ MDM2AP_STATUS,
+ AP2MDM_STATUS,
+ AP2MDM_SOFT_RESET
+};
+
+static void mdm_debug_gpio_show(struct mdm_ctrl *mdm)
+{
+ struct device *dev = mdm->dev;
+
+ dev_dbg(dev, "%s: MDM2AP_ERRFATAL gpio = %d\n",
+ __func__, MDM_GPIO(mdm, MDM2AP_ERRFATAL));
+ dev_dbg(dev, "%s: AP2MDM_ERRFATAL gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_ERRFATAL));
+ dev_dbg(dev, "%s: MDM2AP_STATUS gpio = %d\n",
+ __func__, MDM_GPIO(mdm, MDM2AP_STATUS));
+ dev_dbg(dev, "%s: AP2MDM_STATUS gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_STATUS));
+ dev_dbg(dev, "%s: AP2MDM_SOFT_RESET gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_SOFT_RESET));
+ dev_dbg(dev, "%s: MDM2AP_WAKEUP gpio = %d\n",
+ __func__, MDM_GPIO(mdm, MDM2AP_WAKEUP));
+ dev_dbg(dev, "%s: AP2MDM_WAKEUP gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_WAKEUP));
+ dev_dbg(dev, "%s: AP2MDM_PMIC_PWR_EN gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_PMIC_PWR_EN));
+ dev_dbg(dev, "%s: MDM2AP_PBLRDY gpio = %d\n",
+ __func__, MDM_GPIO(mdm, MDM2AP_PBLRDY));
+ dev_dbg(dev, "%s: AP2MDM_VDDMIN gpio = %d\n",
+ __func__, MDM_GPIO(mdm, AP2MDM_VDDMIN));
+ dev_dbg(dev, "%s: MDM2AP_VDDMIN gpio = %d\n",
+ __func__, MDM_GPIO(mdm, MDM2AP_VDDMIN));
+}
+
+static void mdm_enable_irqs(struct mdm_ctrl *mdm)
+{
+ if (!mdm)
+ return;
+ if (mdm->irq_mask & IRQ_ERRFATAL) {
+ enable_irq(mdm->errfatal_irq);
+ mdm->irq_mask &= ~IRQ_ERRFATAL;
+ }
+ if (mdm->irq_mask & IRQ_STATUS) {
+ enable_irq(mdm->status_irq);
+ mdm->irq_mask &= ~IRQ_STATUS;
+ }
+ if (mdm->irq_mask & IRQ_PBLRDY) {
+ enable_irq(mdm->pblrdy_irq);
+ mdm->irq_mask &= ~IRQ_PBLRDY;
+ }
+}
+
+static void mdm_disable_irqs(struct mdm_ctrl *mdm)
+{
+ if (!mdm)
+ return;
+ if (!(mdm->irq_mask & IRQ_ERRFATAL)) {
+ disable_irq_nosync(mdm->errfatal_irq);
+ mdm->irq_mask |= IRQ_ERRFATAL;
+ }
+ if (!(mdm->irq_mask & IRQ_STATUS)) {
+ disable_irq_nosync(mdm->status_irq);
+ mdm->irq_mask |= IRQ_STATUS;
+ }
+ if (!(mdm->irq_mask & IRQ_PBLRDY)) {
+ disable_irq_nosync(mdm->pblrdy_irq);
+ mdm->irq_mask |= IRQ_PBLRDY;
+ }
+}
+
+static void mdm_deconfigure_ipc(struct mdm_ctrl *mdm)
+{
+ int i;
+
+ for (i = 0; i < NUM_GPIOS; ++i) {
+ if (gpio_is_valid(MDM_GPIO(mdm, i)))
+ gpio_free(MDM_GPIO(mdm, i));
+ }
+ if (mdm->mdm_queue) {
+ destroy_workqueue(mdm->mdm_queue);
+ mdm->mdm_queue = NULL;
+ }
+}
+
+static void mdm_update_gpio_configs(struct mdm_ctrl *mdm,
+ enum gpio_update_config gpio_config)
+{
+ struct device *dev = mdm->dev;
+ /* Some gpio configuration may need updating after modem bootup.*/
+ switch (gpio_config) {
+ case GPIO_UPDATE_RUNNING_CONFIG:
+ if (mdm->mdm2ap_status_gpio_run_cfg) {
+ if (msm_gpiomux_write(MDM_GPIO(mdm, MDM2AP_STATUS),
+ GPIOMUX_ACTIVE,
+ mdm->mdm2ap_status_gpio_run_cfg,
+ &mdm->mdm2ap_status_old_config))
+ dev_err(dev, "switch to run failed\n");
+ else
+ mdm->mdm2ap_status_valid_old_config = 1;
+ }
+ break;
+ case GPIO_UPDATE_BOOTING_CONFIG:
+ if (mdm->mdm2ap_status_valid_old_config) {
+ msm_gpiomux_write(MDM_GPIO(mdm, MDM2AP_STATUS),
+ GPIOMUX_ACTIVE,
+ &mdm->mdm2ap_status_old_config,
+ NULL);
+ mdm->mdm2ap_status_valid_old_config = 0;
+ }
+ break;
+ default:
+ dev_err(dev, "%s: called with no config\n", __func__);
+ break;
+ }
+}
+
+/* This function can be called from atomic context. */
+static void mdm_toggle_soft_reset(struct mdm_ctrl *mdm)
+{
+ int soft_reset_direction_assert = 0,
+ soft_reset_direction_de_assert = 1;
+
+ if (mdm->soft_reset_inverted) {
+ soft_reset_direction_assert = 1;
+ soft_reset_direction_de_assert = 0;
+ }
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_assert);
+ /*
+ * Allow PS hold assert to be detected
+ */
+ usleep_range(8000, 9000);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction_de_assert);
+}
+
+static void mdm_do_first_power_on(struct mdm_ctrl *mdm)
+{
+ int i;
+ int pblrdy;
+ struct device *dev = mdm->dev;
+
+ dev_dbg(dev, "Powering on modem for the first time\n");
+ mdm_toggle_soft_reset(mdm);
+ /* Add a delay to allow PON sequence to complete*/
+ msleep(50);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 1);
+ for (i = 0; i < MDM_PBLRDY_CNT; i++) {
+ pblrdy = gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY));
+ if (pblrdy)
+ break;
+ usleep_range(5000, 6000);
+ }
+ dev_dbg(dev, "pblrdy i:%d\n", i);
+ msleep(200);
+}
+
+static void mdm_power_down(struct mdm_ctrl *mdm)
+{
+ struct device *dev = mdm->dev;
+ int soft_reset_direction = mdm->soft_reset_inverted ? 1 : 0;
+ /* Assert the soft reset line whether mdm2ap_status went low or not */
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction);
+ dev_dbg(dev, "Doing a hard reset\n");
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ soft_reset_direction);
+ /*
+ * Currently, there is a debounce timer on the charm PMIC. It is
+ * necessary to hold the PMIC RESET low for ~3.5 seconds
+ * for the reset to fully take place. Sleep here to ensure the
+ * reset has occured before the function exits.
+ */
+ msleep(4000);
+}
+
+static int mdm_cmd_exe(enum esoc_cmd cmd, struct esoc_clink *esoc)
+{
+ int ret;
+ unsigned long end_time;
+ bool status_down = false;
+ struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+ struct device *dev = mdm->dev;
+
+ switch (cmd) {
+ case ESOC_PWR_ON:
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
+ mdm_enable_irqs(mdm);
+ mdm_do_first_power_on(mdm);
+ break;
+ case ESOC_PWR_OFF:
+ mdm_disable_irqs(mdm);
+ mdm->debug = 0;
+ mdm->ready = false;
+ ret = sysmon_send_shutdown(mdm->sysmon_subsys_id);
+ if (ret)
+ dev_err(mdm->dev, "Graceful shutdown fail, ret = %d\n",
+ ret);
+ else {
+ dev_dbg(mdm->dev, "Waiting for status gpio go low\n");
+ status_down = false;
+ end_time = jiffies + msecs_to_jiffies(10000);
+ while (time_before(jiffies, end_time)) {
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS))
+ == 0) {
+ dev_dbg(dev, "Status went low\n");
+ status_down = true;
+ break;
+ }
+ msleep(100);
+ }
+ if (status_down) {
+ dev_err(dev, "forcing shutdown\n");
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+ mdm_update_gpio_configs(mdm,
+ GPIO_UPDATE_BOOTING_CONFIG);
+ dev_dbg(dev, "shutdown successful\n");
+ return 0;
+ } else
+ dev_err(mdm->dev, "graceful poff ipc fail\n");
+ }
+ mdm_power_down(mdm);
+ mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+ break;
+ case ESOC_RESET:
+ mdm_toggle_soft_reset(mdm);
+ break;
+ case ESOC_PREPARE_DEBUG:
+ /*
+ * disable all irqs except request irq (pblrdy)
+ * force a reset of the mdm by signaling
+ * an APQ crash, wait till mdm is ready for ramdumps.
+ * if mdm has not reset till then, force a reset
+ */
+ mdm->debug = 1;
+ mdm->ready = false;
+ cancel_delayed_work(&mdm->mdm2ap_status_check_work);
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ dev_dbg(mdm->dev, "set ap2mdm errfatal to force reset\n");
+ msleep(mdm->ramdump_delay_ms);
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) != 0)
+ mdm_toggle_soft_reset(mdm);
+ break;
+ case ESOC_EXE_DEBUG:
+ /*
+ * wait for ramdumps to be collected
+ * then power down the mdm and switch gpios to booting
+ * config
+ */
+ if (!wait_for_completion_timeout(&mdm->debug_done,
+ msecs_to_jiffies(mdm->dump_timeout_ms))) {
+ dev_err(mdm->dev, "ramdump collection timedout\n");
+ return -ETIMEDOUT;
+ }
+ if (mdm->debug_fail) {
+ dev_err(mdm->dev, "unable to collect ramdumps\n");
+ return -EIO;
+ }
+ dev_dbg(mdm->dev, "ramdump collection done\n");
+ init_completion(&mdm->debug_done);
+ break;
+ case ESOC_EXIT_DEBUG:
+ /*
+ * Deassert APQ to mdm err fatal
+ * Power on the mdm
+ */
+ mdm->debug = 0;
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
+ dev_dbg(mdm->dev, "exiting debug state after power on\n");
+ mdm->get_restart_reason = true;
+ break;
+ default:
+ return -EINVAL;
+ };
+ return 0;
+}
+
+static void mdm2ap_status_check(struct work_struct *work)
+{
+ struct mdm_ctrl *mdm =
+ container_of(work, struct mdm_ctrl,
+ mdm2ap_status_check_work.work);
+ struct device *dev = mdm->dev;
+ struct esoc_clink *esoc = mdm->esoc;
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0) {
+ dev_dbg(dev, "MDM2AP_STATUS did not go high\n");
+ esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
+ }
+}
+
+static void mdm_status_fn(struct work_struct *work)
+{
+ struct mdm_ctrl *mdm =
+ container_of(work, struct mdm_ctrl, mdm_status_work);
+ struct device *dev = mdm->dev;
+ int value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
+
+ dev_dbg(dev, "%s: status:%d\n", __func__, value);
+ /* Update gpio configuration to "running" config. */
+ mdm_update_gpio_configs(mdm, GPIO_UPDATE_RUNNING_CONFIG);
+}
+
+static void mdm_get_restart_reason(struct work_struct *work)
+{
+ int ret, ntries = 0;
+ char sfr_buf[RD_BUF_SIZE];
+ struct mdm_ctrl *mdm =
+ container_of(work, struct mdm_ctrl, restart_reason_work);
+ struct device *dev = mdm->dev;
+
+ do {
+ ret = sysmon_get_reason(mdm->sysmon_subsys_id, sfr_buf,
+ sizeof(sfr_buf));
+ if (!ret) {
+ dev_err(dev, "mdm restart reason is %s\n", sfr_buf);
+ break;
+ }
+ msleep(SFR_RETRY_INTERVAL);
+ } while (++ntries < SFR_MAX_RETRIES);
+ if (ntries == SFR_MAX_RETRIES)
+ dev_dbg(dev, "%s: Error retrieving restart reason: %d\n",
+ __func__, ret);
+ mdm->get_restart_reason = false;
+}
+
+static void mdm_notify(enum esoc_notify notify, struct esoc_clink *esoc)
+{
+ bool status_down;
+ unsigned long end_time;
+ struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+ struct device *dev = mdm->dev;
+
+ switch (notify) {
+ case ESOC_IMG_XFER_DONE:
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0)
+ schedule_delayed_work(&mdm->mdm2ap_status_check_work,
+ msecs_to_jiffies(MDM2AP_STATUS_TIMEOUT_MS));
+ break;
+ case ESOC_IMG_XFER_RETRY:
+ mdm_toggle_soft_reset(mdm);
+ break;
+ case ESOC_IMG_XFER_FAIL:
+ esoc_clink_evt_notify(ESOC_BOOT_FAIL, esoc);
+ break;
+ case ESOC_UPGRADE_AVAILABLE:
+ break;
+ case ESOC_DEBUG_DONE:
+ mdm->debug_fail = false;
+ mdm_update_gpio_configs(mdm, GPIO_UPDATE_BOOTING_CONFIG);
+ complete(&mdm->debug_done);
+ break;
+ case ESOC_DEBUG_FAIL:
+ mdm->debug_fail = true;
+ complete(&mdm->debug_done);
+ break;
+ case ESOC_PRIMARY_CRASH:
+ mdm_disable_irqs(mdm);
+ status_down = false;
+ dev_dbg(dev, "signal apq err fatal for graceful restart\n");
+ gpio_set_value(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 1);
+ end_time = jiffies + msecs_to_jiffies(MDM_MODEM_TIMEOUT);
+ while (time_before(jiffies, end_time)) {
+ msleep(MDM_MODEM_DELTA);
+ if (gpio_get_value(MDM_GPIO(mdm,
+ MDM2AP_STATUS)) == 0) {
+ status_down = true;
+ break;
+ }
+ }
+ if (!status_down) {
+ dev_err(mdm->dev, "%s MDM2AP status didnot go low\n",
+ __func__);
+ mdm_toggle_soft_reset(mdm);
+ }
+ break;
+ };
+ return;
+}
+
+static irqreturn_t mdm_errfatal(int irq, void *dev_id)
+{
+ struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id;
+ struct esoc_clink *esoc;
+ struct device *dev;
+
+ if (!mdm)
+ goto no_mdm_irq;
+ dev = mdm->dev;
+ if (!mdm->ready)
+ goto mdm_pwroff_irq;
+ esoc = mdm->esoc;
+ dev_err(dev, "%s: mdm sent errfatal interrupt\n",
+ __func__);
+ /* disable irq ?*/
+ esoc_clink_evt_notify(ESOC_ERR_FATAL, esoc);
+ return IRQ_HANDLED;
+mdm_pwroff_irq:
+ dev_info(dev, "errfatal irq when in pwroff\n");
+no_mdm_irq:
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mdm_status_change(int irq, void *dev_id)
+{
+ int value;
+ struct esoc_clink *esoc;
+ struct mdm_ctrl *mdm = (struct mdm_ctrl *)dev_id;
+ struct device *dev = mdm->dev;
+
+ if (!mdm)
+ return IRQ_HANDLED;
+ esoc = mdm->esoc;
+ value = gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS));
+ if (value == 0 && mdm->ready) {
+ dev_err(dev, "unexpected reset external modem\n");
+ esoc_clink_evt_notify(ESOC_UNEXPECTED_RESET, esoc);
+ } else if (value == 1) {
+ cancel_delayed_work(&mdm->mdm2ap_status_check_work);
+ dev_dbg(dev, "status = 1: mdm is now ready\n");
+ esoc_clink_evt_notify(ESOC_RUN_STATE, esoc);
+ mdm->ready = true;
+ queue_work(mdm->mdm_queue, &mdm->mdm_status_work);
+ if (mdm->get_restart_reason)
+ queue_work(mdm->mdm_queue, &mdm->restart_reason_work);
+ }
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mdm_pblrdy_change(int irq, void *dev_id)
+{
+ struct mdm_ctrl *mdm;
+ struct device *dev;
+ struct esoc_clink *esoc;
+
+ mdm = (struct mdm_ctrl *)dev_id;
+ if (!mdm)
+ return IRQ_HANDLED;
+ esoc = mdm->esoc;
+ dev = mdm->dev;
+ dev_dbg(dev, "pbl ready %d:\n",
+ gpio_get_value(MDM_GPIO(mdm, MDM2AP_PBLRDY)));
+ if (mdm->debug) {
+ esoc_clink_queue_request(ESOC_REQ_DEBUG, esoc);
+ return IRQ_HANDLED;
+ }
+ esoc_clink_queue_request(ESOC_REQ_IMG, esoc);
+ return IRQ_HANDLED;
+}
+
+static int mdm_get_status(u32 *status, struct esoc_clink *esoc)
+{
+ struct mdm_ctrl *mdm = get_esoc_clink_data(esoc);
+
+ if (gpio_get_value(MDM_GPIO(mdm, MDM2AP_STATUS)) == 0)
+ *status = 0;
+ else
+ *status = 1;
+ return 0;
+}
+
+/* Fail if any of the required gpios is absent. */
+static int mdm_dt_parse_gpios(struct mdm_ctrl *mdm)
+{
+ int i, val, rc = 0;
+ struct device_node *node = mdm->dev->of_node;
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+
+ for (i = 0; i < NUM_GPIOS; i++)
+ mdm->gpios[i] = INVALID_GPIO;
+
+ for (i = 0; i < ARRAY_SIZE(gpio_map); i++) {
+ val = of_get_named_gpio(node, gpio_map[i].name, 0);
+ if (val >= 0)
+ MDM_GPIO(mdm, gpio_map[i].index) = val;
+ }
+ /* These two are special because they can be inverted. */
+ val = of_get_named_gpio_flags(node, "qcom,ap2mdm-soft-reset-gpio",
+ 0, &flags);
+ if (val >= 0) {
+ MDM_GPIO(mdm, AP2MDM_SOFT_RESET) = val;
+ if (flags & OF_GPIO_ACTIVE_LOW)
+ mdm->soft_reset_inverted = 1;
+ }
+ /* Verify that the required gpios have valid values */
+ for (i = 0; i < ARRAY_SIZE(required_gpios); i++) {
+ if (MDM_GPIO(mdm, required_gpios[i]) == INVALID_GPIO) {
+ rc = -ENXIO;
+ break;
+ }
+ }
+ mdm_debug_gpio_show(mdm);
+ return rc;
+}
+
+static int mdm_configure_ipc(struct mdm_ctrl *mdm, struct platform_device *pdev)
+{
+ int ret = -1;
+ int irq;
+ struct device *dev = mdm->dev;
+ struct device_node *node = pdev->dev.of_node;
+
+ ret = of_property_read_u32(node, "qcom,ramdump-timeout-ms",
+ &mdm->dump_timeout_ms);
+ if (ret)
+ mdm->dump_timeout_ms = DEF_RAMDUMP_TIMEOUT;
+ ret = of_property_read_u32(node, "qcom,ramdump-delay-ms",
+ &mdm->ramdump_delay_ms);
+ if (ret)
+ mdm->ramdump_delay_ms = DEF_RAMDUMP_DELAY;
+ /* Multilple gpio_request calls are allowed */
+ if (gpio_request(MDM_GPIO(mdm, AP2MDM_STATUS), "AP2MDM_STATUS"))
+ dev_err(dev, "Failed to configure AP2MDM_STATUS gpio\n");
+ /* Multilple gpio_request calls are allowed */
+ if (gpio_request(MDM_GPIO(mdm, AP2MDM_ERRFATAL), "AP2MDM_ERRFATAL"))
+ dev_err(dev, "%s Failed to configure AP2MDM_ERRFATAL gpio\n",
+ __func__);
+ if (gpio_request(MDM_GPIO(mdm, MDM2AP_STATUS), "MDM2AP_STATUS")) {
+ dev_err(dev, "%s Failed to configure MDM2AP_STATUS gpio\n",
+ __func__);
+ goto fatal_err;
+ }
+ if (gpio_request(MDM_GPIO(mdm, MDM2AP_ERRFATAL), "MDM2AP_ERRFATAL")) {
+ dev_err(dev, "%s Failed to configure MDM2AP_ERRFATAL gpio\n",
+ __func__);
+ goto fatal_err;
+ }
+ if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) {
+ if (gpio_request(MDM_GPIO(mdm, MDM2AP_PBLRDY),
+ "MDM2AP_PBLRDY")) {
+ dev_err(dev, "Cannot configure MDM2AP_PBLRDY gpio\n");
+ goto fatal_err;
+ }
+ }
+ if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_SOFT_RESET))) {
+ if (gpio_request(MDM_GPIO(mdm, AP2MDM_SOFT_RESET),
+ "AP2MDM_SOFT_RESET")) {
+ dev_err(dev, "Cannot config AP2MDM_SOFT_RESET gpio\n");
+ goto fatal_err;
+ }
+ }
+ if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_WAKEUP))) {
+ if (gpio_request(MDM_GPIO(mdm, AP2MDM_WAKEUP),
+ "AP2MDM_WAKEUP")) {
+ dev_err(dev, "Cannot configure AP2MDM_WAKEUP gpio\n");
+ goto fatal_err;
+ }
+ }
+ if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY))) {
+ if (gpio_request(MDM_GPIO(mdm, AP2MDM_CHNLRDY),
+ "AP2MDM_CHNLRDY")) {
+ dev_err(dev, "Cannot configure AP2MDM_CHNLRDY gpio\n");
+ goto fatal_err;
+ }
+ }
+ ret = of_property_read_u32(node, "qcom,sysmon-subsys-id",
+ &mdm->sysmon_subsys_id);
+ if (ret < 0)
+ dev_dbg(dev, "sysmon_subsys_id not set.\n");
+
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_STATUS), 0);
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_ERRFATAL), 0);
+
+ if (gpio_is_valid(MDM_GPIO(mdm, AP2MDM_CHNLRDY)))
+ gpio_direction_output(MDM_GPIO(mdm, AP2MDM_CHNLRDY), 0);
+
+ gpio_direction_input(MDM_GPIO(mdm, MDM2AP_STATUS));
+ gpio_direction_input(MDM_GPIO(mdm, MDM2AP_ERRFATAL));
+
+ /* ERR_FATAL irq. */
+ irq = platform_get_irq_byname(pdev, "err_fatal_irq");
+ if (irq < 0) {
+ dev_err(dev, "bad MDM2AP_ERRFATAL IRQ resource\n");
+ goto errfatal_err;
+ }
+ ret = request_irq(irq, mdm_errfatal,
+ IRQF_TRIGGER_RISING , "mdm errfatal", mdm);
+
+ if (ret < 0) {
+ dev_err(dev, "%s: MDM2AP_ERRFATAL IRQ#%d request failed,\n",
+ __func__, irq);
+ goto errfatal_err;
+ }
+ mdm->errfatal_irq = irq;
+
+errfatal_err:
+ /* status irq */
+ irq = platform_get_irq_byname(pdev, "status_irq");
+ if (irq < 0) {
+ dev_err(dev, "%s: bad MDM2AP_STATUS IRQ resource, err = %d\n",
+ __func__, irq);
+ goto status_err;
+ }
+ ret = request_threaded_irq(irq, NULL, mdm_status_change,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "mdm status", mdm);
+ if (ret < 0) {
+ dev_err(dev, "%s: MDM2AP_STATUS IRQ#%d request failed, err=%d",
+ __func__, irq, ret);
+ goto status_err;
+ }
+ mdm->status_irq = irq;
+status_err:
+ if (gpio_is_valid(MDM_GPIO(mdm, MDM2AP_PBLRDY))) {
+ irq = platform_get_irq_byname(pdev, "plbrdy_irq");
+ if (irq < 0) {
+ dev_err(dev, "%s: MDM2AP_PBLRDY IRQ request failed\n",
+ __func__);
+ goto pblrdy_err;
+ }
+
+ ret = request_threaded_irq(irq, NULL, mdm_pblrdy_change,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "mdm pbl ready", mdm);
+ if (ret < 0) {
+ dev_err(dev, "MDM2AP_PBL IRQ#%d request failed %d\n",
+ irq, ret);
+ goto pblrdy_err;
+ }
+ mdm->pblrdy_irq = irq;
+ }
+ mdm_disable_irqs(mdm);
+pblrdy_err:
+ return 0;
+fatal_err:
+ mdm_deconfigure_ipc(mdm);
+ return ret;
+
+}
+
+static int mdm9x25_setup_hw(struct mdm_ctrl *mdm,
+ struct esoc_clink_ops const *ops,
+ struct platform_device *pdev)
+{
+ int ret;
+ struct esoc_clink *esoc;
+
+ mdm->dev = &pdev->dev;
+ esoc = devm_kzalloc(mdm->dev, sizeof(*esoc), GFP_KERNEL);
+ if (IS_ERR(esoc)) {
+ dev_err(mdm->dev, "cannot allocate esoc device\n");
+ return PTR_ERR(esoc);
+ }
+ mdm->mdm_queue = alloc_workqueue("mdm_queue", 0, 0);
+ if (!mdm->mdm_queue) {
+ dev_err(mdm->dev, "could not create mdm_queue\n");
+ return -ENOMEM;
+ }
+ mdm->irq_mask = 0;
+ mdm->ready = false;
+ ret = mdm_dt_parse_gpios(mdm);
+ if (ret)
+ return ret;
+ dev_err(mdm->dev, "parsing gpio done\n");
+ ret = mdm_configure_ipc(mdm, pdev);
+ if (ret)
+ return ret;
+ dev_err(mdm->dev, "ipc configure done\n");
+ esoc->name = MDM9x25_LABEL;
+ esoc->link_name = MDM9x25_HSIC;
+ esoc->clink_ops = ops;
+ esoc->parent = mdm->dev;
+ esoc->owner = THIS_MODULE;
+ set_esoc_clink_data(esoc, mdm);
+ ret = esoc_clink_register(esoc);
+ if (ret) {
+ dev_err(mdm->dev, "esoc registration failed\n");
+ return ret;
+ }
+ dev_dbg(mdm->dev, "esoc registration done\n");
+ init_completion(&mdm->debug_done);
+ INIT_WORK(&mdm->mdm_status_work, mdm_status_fn);
+ INIT_WORK(&mdm->restart_reason_work, mdm_get_restart_reason);
+ INIT_DELAYED_WORK(&mdm->mdm2ap_status_check_work, mdm2ap_status_check);
+ mdm->get_restart_reason = false;
+ mdm->debug_fail = false;
+ mdm->esoc = esoc;
+ return 0;
+}
+
+static struct esoc_clink_ops mdm_cops = {
+ .cmd_exe = mdm_cmd_exe,
+ .get_status = mdm_get_status,
+ .notify = mdm_notify,
+};
+
+static struct mdm_ops mdm9x25_ops = {
+ .clink_ops = &mdm_cops,
+ .config_hw = mdm9x25_setup_hw,
+};
+
+static const struct of_device_id mdm_dt_match[] = {
+ { .compatible = "qcom,ext-mdm9x25",
+ .data = &mdm9x25_ops, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mdm_dt_match);
+
+static int mdm_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ const struct mdm_ops *mdm_ops;
+ struct device_node *node = pdev->dev.of_node;
+ struct mdm_ctrl *mdm;
+
+ match = of_match_node(mdm_dt_match, node);
+ if (IS_ERR(match))
+ return PTR_ERR(match);
+ mdm_ops = match->data;
+ mdm = devm_kzalloc(&pdev->dev, sizeof(*mdm), GFP_KERNEL);
+ if (IS_ERR(mdm))
+ return PTR_ERR(mdm);
+ return mdm_ops->config_hw(mdm, mdm_ops->clink_ops, pdev);
+}
+
+static struct platform_driver mdm_driver = {
+ .probe = mdm_probe,
+ .driver = {
+ .name = "ext-mdm",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(mdm_dt_match),
+ },
+};
+
+static int __init mdm_register(void)
+{
+ return platform_driver_register(&mdm_driver);
+}
+module_init(mdm_register);
+
+static void __exit mdm_unregister(void)
+{
+ platform_driver_unregister(&mdm_driver);
+}
+module_exit(mdm_unregister);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/esoc/esoc-mdm-drv.c b/drivers/esoc/esoc-mdm-drv.c
new file mode 100644
index 000000000000..47f6a136272e
--- /dev/null
+++ b/drivers/esoc/esoc-mdm-drv.c
@@ -0,0 +1,250 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/delay.h>
+#include <linux/workqueue.h>
+#include "esoc.h"
+
+enum {
+ PWR_OFF = 0x1,
+ PWR_ON,
+ BOOT,
+ RUN,
+ CRASH,
+ IN_DEBUG,
+ SHUTDOWN,
+ RESET,
+ PEER_CRASH,
+};
+
+#define MDM_BOOT_TIMEOUT 60000L
+
+struct mdm_drv {
+ unsigned mode;
+ struct esoc_eng cmd_eng;
+ struct completion boot_done;
+ struct esoc_clink *esoc_clink;
+ bool boot_fail;
+ struct workqueue_struct *mdm_queue;
+ struct work_struct ssr_work;
+};
+
+#define to_mdm_drv(d) container_of(d, struct mdm_drv, cmd_eng)
+
+static void mdm_handle_clink_evt(enum esoc_evt evt,
+ struct esoc_eng *eng)
+{
+ struct mdm_drv *mdm_drv = to_mdm_drv(eng);
+ switch (evt) {
+ case ESOC_BOOT_FAIL:
+ mdm_drv->boot_fail = true;
+ complete(&mdm_drv->boot_done);
+ break;
+ case ESOC_RUN_STATE:
+ mdm_drv->boot_fail = false;
+ mdm_drv->mode = RUN,
+ complete(&mdm_drv->boot_done);
+ break;
+ case ESOC_UNEXPECTED_RESET:
+ case ESOC_ERR_FATAL:
+ if (mdm_drv->mode == CRASH)
+ return;
+ mdm_drv->mode = CRASH;
+ queue_work(mdm_drv->mdm_queue, &mdm_drv->ssr_work);
+ break;
+ default:
+ break;
+ }
+}
+
+static void mdm_ssr_fn(struct work_struct *work)
+{
+ int ret;
+ struct mdm_drv *mdm_drv = container_of(work, struct mdm_drv, ssr_work);
+ struct esoc_clink *esoc_clink = mdm_drv->esoc_clink;
+ const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+
+ /*
+ * If esoc bus cannot allow restart, then forcibly shut down the
+ * esoc
+ */
+ ret = esoc_clink_request_ssr(mdm_drv->esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "ssr request refused\n");
+ clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
+ }
+ return;
+}
+
+static void mdm_crash_shutdown(const struct subsys_desc *mdm_subsys)
+{
+ struct esoc_clink *esoc_clink =
+ container_of(mdm_subsys,
+ struct esoc_clink,
+ subsys);
+ const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+ clink_ops->notify(ESOC_PRIMARY_CRASH, esoc_clink);
+}
+
+static int mdm_subsys_shutdown(const struct subsys_desc *crashed_subsys,
+ bool force_stop)
+{
+ int ret;
+ struct esoc_clink *esoc_clink =
+ container_of(crashed_subsys, struct esoc_clink, subsys);
+ struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
+ const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+
+ if (mdm_drv->mode == CRASH || mdm_drv->mode == PEER_CRASH) {
+ ret = clink_ops->cmd_exe(ESOC_PREPARE_DEBUG,
+ esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "failed to enter debug\n");
+ return ret;
+ }
+ mdm_drv->mode = IN_DEBUG;
+ } else {
+ ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "failed to exe power off\n");
+ return ret;
+ }
+ mdm_drv->mode = PWR_OFF;
+ }
+ return 0;
+}
+
+static int mdm_subsys_powerup(const struct subsys_desc *crashed_subsys)
+{
+ int ret;
+ struct esoc_clink *esoc_clink =
+ container_of(crashed_subsys, struct esoc_clink,
+ subsys);
+ struct mdm_drv *mdm_drv = esoc_get_drv_data(esoc_clink);
+ const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+
+ if (mdm_drv->mode == PWR_OFF) {
+ ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "pwr on fail\n");
+ return ret;
+ }
+ } else if (mdm_drv->mode == IN_DEBUG) {
+ ret = clink_ops->cmd_exe(ESOC_EXIT_DEBUG, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "cannot exit debug mode\n");
+ return ret;
+ }
+ ret = clink_ops->cmd_exe(ESOC_PWR_OFF, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "pwr off fail\n");
+ return ret;
+ }
+ mdm_drv->mode = PWR_OFF;
+ ret = clink_ops->cmd_exe(ESOC_PWR_ON, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "pwr on fail\n");
+ return ret;
+ }
+ }
+ if (!wait_for_completion_timeout(&mdm_drv->boot_done,
+ msecs_to_jiffies(MDM_BOOT_TIMEOUT))) {
+ dev_err(&esoc_clink->dev, "Unable to boot\n");
+ return -ETIMEDOUT;
+ }
+ if (mdm_drv->boot_fail) {
+ dev_err(&esoc_clink->dev, "booting failed\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+static int mdm_subsys_ramdumps(int want_dumps,
+ const struct subsys_desc *crashed_subsys)
+{
+ int ret;
+ struct esoc_clink *esoc_clink =
+ container_of(crashed_subsys, struct esoc_clink,
+ subsys);
+ const struct esoc_clink_ops const *clink_ops = esoc_clink->clink_ops;
+
+ if (want_dumps) {
+ ret = clink_ops->cmd_exe(ESOC_EXE_DEBUG, esoc_clink);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "debugging failed\n");
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static int mdm_register_ssr(struct esoc_clink *esoc_clink)
+{
+ esoc_clink->subsys.shutdown = mdm_subsys_shutdown;
+ esoc_clink->subsys.ramdump = mdm_subsys_ramdumps;
+ esoc_clink->subsys.powerup = mdm_subsys_powerup;
+ esoc_clink->subsys.crash_shutdown = mdm_crash_shutdown;
+ return esoc_clink_register_ssr(esoc_clink);
+}
+
+int esoc_ssr_probe(struct esoc_clink *esoc_clink)
+{
+ int ret;
+ struct mdm_drv *mdm_drv;
+ struct esoc_eng *esoc_eng;
+
+ mdm_drv = devm_kzalloc(&esoc_clink->dev, sizeof(*mdm_drv), GFP_KERNEL);
+ if (IS_ERR(mdm_drv))
+ return PTR_ERR(mdm_drv);
+ esoc_eng = &mdm_drv->cmd_eng;
+ esoc_eng->handle_clink_evt = mdm_handle_clink_evt;
+ ret = esoc_clink_register_cmd_eng(esoc_clink, esoc_eng);
+ if (ret) {
+ dev_err(&esoc_clink->dev, "failed to register cmd engine\n");
+ return ret;
+ }
+ ret = mdm_register_ssr(esoc_clink);
+ if (ret)
+ goto ssr_err;
+ mdm_drv->mdm_queue = alloc_workqueue("mdm_drv_queue", 0, 0);
+ if (!mdm_drv->mdm_queue) {
+ dev_err(&esoc_clink->dev, "could not create mdm_queue\n");
+ goto queue_err;
+ }
+ esoc_set_drv_data(esoc_clink, mdm_drv);
+ init_completion(&mdm_drv->boot_done);
+ INIT_WORK(&mdm_drv->ssr_work, mdm_ssr_fn);
+ mdm_drv->esoc_clink = esoc_clink;
+ mdm_drv->mode = PWR_OFF;
+ mdm_drv->boot_fail = false;
+ return 0;
+queue_err:
+ esoc_clink_unregister_ssr(esoc_clink);
+ssr_err:
+ esoc_clink_unregister_cmd_eng(esoc_clink, esoc_eng);
+ return ret;
+}
+
+static struct esoc_drv esoc_ssr_drv = {
+ .owner = THIS_MODULE,
+ .probe = esoc_ssr_probe,
+ .driver = {
+ .name = "MDM9x25",
+ },
+};
+
+int __init esoc_ssr_init(void)
+{
+ return esoc_drv_register(&esoc_ssr_drv);
+}
+module_init(esoc_ssr_init);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 7fab6eef36eb..8b3ae1959422 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -155,22 +155,6 @@ config GPIO_MPC8XXX
Say Y here if you're going to use hardware that connects to the
MPC512x/831x/834x/837x/8572/8610 GPIOs.
-config GPIO_MSM_V1
- tristate "Qualcomm MSM GPIO v1"
- depends on GPIOLIB && ARCH_MSM && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
- help
- Say yes here to support the GPIO interface on ARM v6 based
- Qualcomm MSM chips. Most of the pins on the MSM can be
- selected for GPIO, and are controlled by this driver.
-
-config GPIO_MSM_V2
- tristate "Qualcomm MSM GPIO v2"
- depends on GPIOLIB && ARCH_MSM
- help
- Say yes here to support the GPIO interface on ARM v7 based
- Qualcomm MSM chips. Most of the pins on the MSM can be
- selected for GPIO, and are controlled by this driver.
-
config GPIO_MSM_V3
tristate "Qualcomm MSM GPIO v3"
depends on GPIOLIB && ARCH_MSM
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index dae4d3cd3fcb..ac1f7e725311 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -45,8 +45,6 @@ obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o
obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o
obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o
-obj-$(CONFIG_GPIO_MSM_V1) += gpio-msm-v1.o
-obj-$(CONFIG_GPIO_MSM_V2) += gpio-msm-common.o gpio-msm-v2.o
obj-$(CONFIG_GPIO_MSM_V3) += gpio-msm-common.o gpio-msm-v3.o
obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o
obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o
diff --git a/drivers/gpio/gpio-msm-common.c b/drivers/gpio/gpio-msm-common.c
index 3c56260c497d..3a20533f097c 100644
--- a/drivers/gpio/gpio-msm-common.c
+++ b/drivers/gpio/gpio-msm-common.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
+#include <linux/irqchip/msm-mpm-irq.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -29,7 +30,6 @@
#include <mach/msm_iomap.h>
#include <mach/gpiomux.h>
-#include <mach/mpm.h>
#include "gpio-msm-common.h"
#ifdef CONFIG_GPIO_MSM_V3
diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c
deleted file mode 100644
index 064d56f60933..000000000000
--- a/drivers/gpio/gpio-msm-v1.c
+++ /dev/null
@@ -1,840 +0,0 @@
-/* linux/arch/arm/mach-msm/gpio.c
- *
- * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
- *
- * 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/bitops.h>
-#include <linux/gpio.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <asm/mach/irq.h>
-#include <mach/gpiomux.h>
-#include <mach/msm_iomap.h>
-#include <mach/msm_smem.h>
-#include <mach/proc_comm.h>
-
-
-/* see 80-VA736-2 Rev C pp 695-751
-**
-** These are actually the *shadow* gpio registers, since the
-** real ones (which allow full access) are only available to the
-** ARM9 side of the world.
-**
-** Since the _BASE need to be page-aligned when we're mapping them
-** to virtual addresses, adjust for the additional offset in these
-** macros.
-*/
-
-#if defined(CONFIG_ARCH_MSM7X30)
-#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off))
-#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off))
-#else
-#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + 0x800 + (off))
-#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off))
-#endif
-
-#if defined(CONFIG_ARCH_MSM7X00A) || defined(CONFIG_ARCH_MSM7X25) ||\
- defined(CONFIG_ARCH_MSM7X27)
-
-/* output value */
-#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
-#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 42-16 */
-#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-43 */
-#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
-#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
-#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 107-121 */
-
-/* same pin map as above, output enable */
-#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10)
-#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08)
-#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14)
-#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18)
-#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
-#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54)
-
-/* same pin map as above, input read */
-#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34)
-#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20)
-#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38)
-#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
-#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40)
-#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
-#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
-#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
-#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
-#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
-#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
-#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
-#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
-#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
-#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
-#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
-
-/* same pin map as above, interrupt enable */
-#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
-#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
-#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
-#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
-#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
-#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
-#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
-#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
-#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
-#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
-#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
-#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
-#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
-#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
-#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
-#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
-
-#endif
-
-#if defined(CONFIG_ARCH_MSM7X30)
-
-/* output value */
-#define MSM_GPIO_OUT_0 MSM_GPIO1_REG(0x00) /* gpio 15-0 */
-#define MSM_GPIO_OUT_1 MSM_GPIO2_REG(0x00) /* gpio 43-16 */
-#define MSM_GPIO_OUT_2 MSM_GPIO1_REG(0x04) /* gpio 67-44 */
-#define MSM_GPIO_OUT_3 MSM_GPIO1_REG(0x08) /* gpio 94-68 */
-#define MSM_GPIO_OUT_4 MSM_GPIO1_REG(0x0C) /* gpio 106-95 */
-#define MSM_GPIO_OUT_5 MSM_GPIO1_REG(0x50) /* gpio 133-107 */
-#define MSM_GPIO_OUT_6 MSM_GPIO1_REG(0xC4) /* gpio 150-134 */
-#define MSM_GPIO_OUT_7 MSM_GPIO1_REG(0x214) /* gpio 181-151 */
-
-/* same pin map as above, output enable */
-#define MSM_GPIO_OE_0 MSM_GPIO1_REG(0x10)
-#define MSM_GPIO_OE_1 MSM_GPIO2_REG(0x08)
-#define MSM_GPIO_OE_2 MSM_GPIO1_REG(0x14)
-#define MSM_GPIO_OE_3 MSM_GPIO1_REG(0x18)
-#define MSM_GPIO_OE_4 MSM_GPIO1_REG(0x1C)
-#define MSM_GPIO_OE_5 MSM_GPIO1_REG(0x54)
-#define MSM_GPIO_OE_6 MSM_GPIO1_REG(0xC8)
-#define MSM_GPIO_OE_7 MSM_GPIO1_REG(0x218)
-
-/* same pin map as above, input read */
-#define MSM_GPIO_IN_0 MSM_GPIO1_REG(0x34)
-#define MSM_GPIO_IN_1 MSM_GPIO2_REG(0x20)
-#define MSM_GPIO_IN_2 MSM_GPIO1_REG(0x38)
-#define MSM_GPIO_IN_3 MSM_GPIO1_REG(0x3C)
-#define MSM_GPIO_IN_4 MSM_GPIO1_REG(0x40)
-#define MSM_GPIO_IN_5 MSM_GPIO1_REG(0x44)
-#define MSM_GPIO_IN_6 MSM_GPIO1_REG(0xCC)
-#define MSM_GPIO_IN_7 MSM_GPIO1_REG(0x21C)
-
-/* same pin map as above, 1=edge 0=level interrup */
-#define MSM_GPIO_INT_EDGE_0 MSM_GPIO1_REG(0x60)
-#define MSM_GPIO_INT_EDGE_1 MSM_GPIO2_REG(0x50)
-#define MSM_GPIO_INT_EDGE_2 MSM_GPIO1_REG(0x64)
-#define MSM_GPIO_INT_EDGE_3 MSM_GPIO1_REG(0x68)
-#define MSM_GPIO_INT_EDGE_4 MSM_GPIO1_REG(0x6C)
-#define MSM_GPIO_INT_EDGE_5 MSM_GPIO1_REG(0xC0)
-#define MSM_GPIO_INT_EDGE_6 MSM_GPIO1_REG(0xD0)
-#define MSM_GPIO_INT_EDGE_7 MSM_GPIO1_REG(0x240)
-
-/* same pin map as above, 1=positive 0=negative */
-#define MSM_GPIO_INT_POS_0 MSM_GPIO1_REG(0x70)
-#define MSM_GPIO_INT_POS_1 MSM_GPIO2_REG(0x58)
-#define MSM_GPIO_INT_POS_2 MSM_GPIO1_REG(0x74)
-#define MSM_GPIO_INT_POS_3 MSM_GPIO1_REG(0x78)
-#define MSM_GPIO_INT_POS_4 MSM_GPIO1_REG(0x7C)
-#define MSM_GPIO_INT_POS_5 MSM_GPIO1_REG(0xBC)
-#define MSM_GPIO_INT_POS_6 MSM_GPIO1_REG(0xD4)
-#define MSM_GPIO_INT_POS_7 MSM_GPIO1_REG(0x228)
-
-/* same pin map as above, interrupt enable */
-#define MSM_GPIO_INT_EN_0 MSM_GPIO1_REG(0x80)
-#define MSM_GPIO_INT_EN_1 MSM_GPIO2_REG(0x60)
-#define MSM_GPIO_INT_EN_2 MSM_GPIO1_REG(0x84)
-#define MSM_GPIO_INT_EN_3 MSM_GPIO1_REG(0x88)
-#define MSM_GPIO_INT_EN_4 MSM_GPIO1_REG(0x8C)
-#define MSM_GPIO_INT_EN_5 MSM_GPIO1_REG(0xB8)
-#define MSM_GPIO_INT_EN_6 MSM_GPIO1_REG(0xD8)
-#define MSM_GPIO_INT_EN_7 MSM_GPIO1_REG(0x22C)
-
-/* same pin map as above, write 1 to clear interrupt */
-#define MSM_GPIO_INT_CLEAR_0 MSM_GPIO1_REG(0x90)
-#define MSM_GPIO_INT_CLEAR_1 MSM_GPIO2_REG(0x68)
-#define MSM_GPIO_INT_CLEAR_2 MSM_GPIO1_REG(0x94)
-#define MSM_GPIO_INT_CLEAR_3 MSM_GPIO1_REG(0x98)
-#define MSM_GPIO_INT_CLEAR_4 MSM_GPIO1_REG(0x9C)
-#define MSM_GPIO_INT_CLEAR_5 MSM_GPIO1_REG(0xB4)
-#define MSM_GPIO_INT_CLEAR_6 MSM_GPIO1_REG(0xDC)
-#define MSM_GPIO_INT_CLEAR_7 MSM_GPIO1_REG(0x230)
-
-/* same pin map as above, 1=interrupt pending */
-#define MSM_GPIO_INT_STATUS_0 MSM_GPIO1_REG(0xA0)
-#define MSM_GPIO_INT_STATUS_1 MSM_GPIO2_REG(0x70)
-#define MSM_GPIO_INT_STATUS_2 MSM_GPIO1_REG(0xA4)
-#define MSM_GPIO_INT_STATUS_3 MSM_GPIO1_REG(0xA8)
-#define MSM_GPIO_INT_STATUS_4 MSM_GPIO1_REG(0xAC)
-#define MSM_GPIO_INT_STATUS_5 MSM_GPIO1_REG(0xB0)
-#define MSM_GPIO_INT_STATUS_6 MSM_GPIO1_REG(0xE0)
-#define MSM_GPIO_INT_STATUS_7 MSM_GPIO1_REG(0x234)
-
-#endif
-
-enum {
- GPIO_DEBUG_SLEEP = 1U << 0,
-};
-static int msm_gpio_debug_mask;
-module_param_named(debug_mask, msm_gpio_debug_mask, int,
- S_IRUGO | S_IWUSR | S_IWGRP);
-
-#define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0)
-
-#define MSM_GPIO_BANK(bank, first, last) \
- { \
- .regs = { \
- .out = MSM_GPIO_OUT_##bank, \
- .in = MSM_GPIO_IN_##bank, \
- .int_status = MSM_GPIO_INT_STATUS_##bank, \
- .int_clear = MSM_GPIO_INT_CLEAR_##bank, \
- .int_en = MSM_GPIO_INT_EN_##bank, \
- .int_edge = MSM_GPIO_INT_EDGE_##bank, \
- .int_pos = MSM_GPIO_INT_POS_##bank, \
- .oe = MSM_GPIO_OE_##bank, \
- }, \
- .chip = { \
- .base = (first), \
- .ngpio = (last) - (first) + 1, \
- .get = msm_gpio_get, \
- .set = msm_gpio_set, \
- .direction_input = msm_gpio_direction_input, \
- .direction_output = msm_gpio_direction_output, \
- .to_irq = msm_gpio_to_irq, \
- .request = msm_gpio_request, \
- .free = msm_gpio_free, \
- } \
- }
-
-#define MSM_GPIO_BROKEN_INT_CLEAR 1
-
-struct msm_gpio_regs {
- void __iomem *out;
- void __iomem *in;
- void __iomem *int_status;
- void __iomem *int_clear;
- void __iomem *int_en;
- void __iomem *int_edge;
- void __iomem *int_pos;
- void __iomem *oe;
-};
-
-struct msm_gpio_chip {
- spinlock_t lock;
- struct gpio_chip chip;
- struct msm_gpio_regs regs;
-#if MSM_GPIO_BROKEN_INT_CLEAR
- unsigned int_status_copy;
-#endif
- unsigned int both_edge_detect;
- unsigned int int_enable[2]; /* 0: awake, 1: sleep */
-};
-
-static int msm_gpio_write(struct msm_gpio_chip *msm_chip,
- unsigned offset, unsigned on)
-{
- unsigned mask = BIT(offset);
- unsigned val;
-
- val = __raw_readl(msm_chip->regs.out);
- if (on)
- __raw_writel(val | mask, msm_chip->regs.out);
- else
- __raw_writel(val & ~mask, msm_chip->regs.out);
- return 0;
-}
-
-static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip)
-{
- int loop_limit = 100;
- unsigned pol, val, val2, intstat;
- do {
- val = __raw_readl(msm_chip->regs.in);
- pol = __raw_readl(msm_chip->regs.int_pos);
- pol = (pol & ~msm_chip->both_edge_detect) |
- (~val & msm_chip->both_edge_detect);
- __raw_writel(pol, msm_chip->regs.int_pos);
- intstat = __raw_readl(msm_chip->regs.int_status);
- val2 = __raw_readl(msm_chip->regs.in);
- if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0)
- return;
- } while (loop_limit-- > 0);
- printk(KERN_ERR "msm_gpio_update_both_edge_detect, "
- "failed to reach stable state %x != %x\n", val, val2);
-}
-
-static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip,
- unsigned offset)
-{
- unsigned bit = BIT(offset);
-
-#if MSM_GPIO_BROKEN_INT_CLEAR
- /* Save interrupts that already triggered before we loose them. */
- /* Any interrupt that triggers between the read of int_status */
- /* and the write to int_clear will still be lost though. */
- msm_chip->int_status_copy |= __raw_readl(msm_chip->regs.int_status);
- msm_chip->int_status_copy &= ~bit;
-#endif
- __raw_writel(bit, msm_chip->regs.int_clear);
- msm_gpio_update_both_edge_detect(msm_chip);
- return 0;
-}
-
-static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- __raw_writel(__raw_readl(msm_chip->regs.oe) & ~BIT(offset),
- msm_chip->regs.oe);
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int
-msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_write(msm_chip, offset, value);
- __raw_writel(__raw_readl(msm_chip->regs.oe) | BIT(offset),
- msm_chip->regs.oe);
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct msm_gpio_chip *msm_chip;
- int rc;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- rc = (__raw_readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0;
- mb();
- return rc;
-}
-
-static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct msm_gpio_chip *msm_chip;
- unsigned long irq_flags;
-
- msm_chip = container_of(chip, struct msm_gpio_chip, chip);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_write(msm_chip, offset, value);
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- return MSM_GPIO_TO_INT(chip->base + offset);
-}
-
-#ifdef CONFIG_MSM_GPIOMUX
-static int msm_gpio_request(struct gpio_chip *chip, unsigned offset)
-{
- return msm_gpiomux_get(chip->base + offset);
-}
-
-static void msm_gpio_free(struct gpio_chip *chip, unsigned offset)
-{
- msm_gpiomux_put(chip->base + offset);
-}
-#else
-#define msm_gpio_request NULL
-#define msm_gpio_free NULL
-#endif
-
-struct msm_gpio_chip msm_gpio_chips[] = {
-#if defined(CONFIG_ARCH_MSM7X00A)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 121),
-#elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 132),
-#elif defined(CONFIG_ARCH_MSM7X30)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 43),
- MSM_GPIO_BANK(2, 44, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 106),
- MSM_GPIO_BANK(5, 107, 133),
- MSM_GPIO_BANK(6, 134, 150),
- MSM_GPIO_BANK(7, 151, 181),
-#elif defined(CONFIG_ARCH_QSD8X50)
- MSM_GPIO_BANK(0, 0, 15),
- MSM_GPIO_BANK(1, 16, 42),
- MSM_GPIO_BANK(2, 43, 67),
- MSM_GPIO_BANK(3, 68, 94),
- MSM_GPIO_BANK(4, 95, 103),
- MSM_GPIO_BANK(5, 104, 121),
- MSM_GPIO_BANK(6, 122, 152),
- MSM_GPIO_BANK(7, 153, 164),
-#endif
-};
-
-static void msm_gpio_irq_ack(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- msm_gpio_clear_detect_status(msm_chip,
- d->irq - gpio_to_irq(msm_chip->chip.base));
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static void msm_gpio_irq_mask(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- /* level triggered interrupts are also latched */
- if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
- msm_gpio_clear_detect_status(msm_chip, offset);
- msm_chip->int_enable[0] &= ~BIT(offset);
- __raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static void msm_gpio_irq_unmask(struct irq_data *d)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- /* level triggered interrupts are also latched */
- if (!(__raw_readl(msm_chip->regs.int_edge) & BIT(offset)))
- msm_gpio_clear_detect_status(msm_chip, offset);
- msm_chip->int_enable[0] |= BIT(offset);
- __raw_writel(msm_chip->int_enable[0], msm_chip->regs.int_en);
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
-}
-
-static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
-
- if (on)
- msm_chip->int_enable[1] |= BIT(offset);
- else
- msm_chip->int_enable[1] &= ~BIT(offset);
-
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type)
-{
- unsigned long irq_flags;
- struct msm_gpio_chip *msm_chip = irq_get_chip_data(d->irq);
- unsigned offset = d->irq - gpio_to_irq(msm_chip->chip.base);
- unsigned val, mask = BIT(offset);
-
- spin_lock_irqsave(&msm_chip->lock, irq_flags);
- val = __raw_readl(msm_chip->regs.int_edge);
- if (flow_type & IRQ_TYPE_EDGE_BOTH) {
- __raw_writel(val | mask, msm_chip->regs.int_edge);
- __irq_set_handler_locked(d->irq, handle_edge_irq);
- } else {
- __raw_writel(val & ~mask, msm_chip->regs.int_edge);
- __irq_set_handler_locked(d->irq, handle_level_irq);
- }
- if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
- msm_chip->both_edge_detect |= mask;
- msm_gpio_update_both_edge_detect(msm_chip);
- } else {
- msm_chip->both_edge_detect &= ~mask;
- val = __raw_readl(msm_chip->regs.int_pos);
- if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH))
- __raw_writel(val | mask, msm_chip->regs.int_pos);
- else
- __raw_writel(val & ~mask, msm_chip->regs.int_pos);
- }
- mb();
- spin_unlock_irqrestore(&msm_chip->lock, irq_flags);
- return 0;
-}
-
-static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
-{
- int i, j, mask;
- unsigned val;
- struct irq_chip *chip = irq_desc_get_chip(desc);
-
- chained_irq_enter(chip, desc);
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i];
- val = __raw_readl(msm_chip->regs.int_status);
- val &= msm_chip->int_enable[0];
- while (val) {
- mask = val & -val;
- j = fls(mask) - 1;
- /* printk("%s %08x %08x bit %d gpio %d irq %d\n",
- __func__, v, m, j, msm_chip->chip.start + j,
- FIRST_GPIO_IRQ + msm_chip->chip.start + j); */
- val &= ~mask;
- generic_handle_irq(FIRST_GPIO_IRQ +
- msm_chip->chip.base + j);
- }
- }
-
- chained_irq_exit(chip, desc);
-}
-
-static struct irq_chip msm_gpio_irq_chip = {
- .name = "msmgpio",
- .irq_ack = msm_gpio_irq_ack,
- .irq_mask = msm_gpio_irq_mask,
- .irq_unmask = msm_gpio_irq_unmask,
- .irq_set_wake = msm_gpio_irq_set_wake,
- .irq_set_type = msm_gpio_irq_set_type,
-};
-
-#define NUM_GPIO_SMEM_BANKS 6
-#define GPIO_SMEM_NUM_GROUPS 2
-#define GPIO_SMEM_MAX_PC_INTERRUPTS 8
-struct tramp_gpio_smem {
- uint16_t num_fired[GPIO_SMEM_NUM_GROUPS];
- uint16_t fired[GPIO_SMEM_NUM_GROUPS][GPIO_SMEM_MAX_PC_INTERRUPTS];
- uint32_t enabled[NUM_GPIO_SMEM_BANKS];
- uint32_t detection[NUM_GPIO_SMEM_BANKS];
- uint32_t polarity[NUM_GPIO_SMEM_BANKS];
-};
-
-static void msm_gpio_sleep_int(unsigned long arg)
-{
- int i, j;
- struct tramp_gpio_smem *smem_gpio;
-
- BUILD_BUG_ON(NR_GPIO_IRQS > NUM_GPIO_SMEM_BANKS * 32);
-
- smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0,
- SMEM_ANY_HOST_FLAG);
- if (smem_gpio == NULL)
- return;
-
- local_irq_disable();
- for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++) {
- int count = smem_gpio->num_fired[i];
- for (j = 0; j < count; j++) {
- /* TODO: Check mask */
- generic_handle_irq(
- MSM_GPIO_TO_INT(smem_gpio->fired[i][j]));
- }
- }
- local_irq_enable();
-}
-
-static DECLARE_TASKLET(msm_gpio_sleep_int_tasklet, msm_gpio_sleep_int, 0);
-
-void msm_gpio_enter_sleep(int from_idle)
-{
- int i;
- struct tramp_gpio_smem *smem_gpio;
-
- smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0,
- SMEM_ANY_HOST_FLAG);
-
- if (smem_gpio) {
- for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
- smem_gpio->enabled[i] = 0;
- smem_gpio->detection[i] = 0;
- smem_gpio->polarity[i] = 0;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- __raw_writel(msm_gpio_chips[i].int_enable[!from_idle],
- msm_gpio_chips[i].regs.int_en);
- if (smem_gpio) {
- uint32_t tmp;
- int start, index, shiftl, shiftr;
- start = msm_gpio_chips[i].chip.base;
- index = start / 32;
- shiftl = start % 32;
- shiftr = 32 - shiftl;
- tmp = msm_gpio_chips[i].int_enable[!from_idle];
- smem_gpio->enabled[index] |= tmp << shiftl;
- smem_gpio->enabled[index+1] |= tmp >> shiftr;
- smem_gpio->detection[index] |=
- __raw_readl(msm_gpio_chips[i].regs.int_edge) <<
- shiftl;
- smem_gpio->detection[index+1] |=
- __raw_readl(msm_gpio_chips[i].regs.int_edge) >>
- shiftr;
- smem_gpio->polarity[index] |=
- __raw_readl(msm_gpio_chips[i].regs.int_pos) <<
- shiftl;
- smem_gpio->polarity[index+1] |=
- __raw_readl(msm_gpio_chips[i].regs.int_pos) >>
- shiftr;
- }
- }
- mb();
-
- if (smem_gpio) {
- if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
- for (i = 0; i < ARRAY_SIZE(smem_gpio->enabled); i++) {
- printk("msm_gpio_enter_sleep gpio %d-%d: enable"
- " %08x, edge %08x, polarity %08x\n",
- i * 32, i * 32 + 31,
- smem_gpio->enabled[i],
- smem_gpio->detection[i],
- smem_gpio->polarity[i]);
- }
- for (i = 0; i < GPIO_SMEM_NUM_GROUPS; i++)
- smem_gpio->num_fired[i] = 0;
- }
-}
-
-void msm_gpio_exit_sleep(void)
-{
- int i;
- struct tramp_gpio_smem *smem_gpio;
-
- smem_gpio = smem_find(SMEM_GPIO_INT, sizeof(*smem_gpio), 0,
- SMEM_ANY_HOST_FLAG);
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- __raw_writel(msm_gpio_chips[i].int_enable[0],
- msm_gpio_chips[i].regs.int_en);
- }
- mb();
-
- if (smem_gpio && (smem_gpio->num_fired[0] || smem_gpio->num_fired[1])) {
- if (msm_gpio_debug_mask & GPIO_DEBUG_SLEEP)
- printk(KERN_INFO "gpio: fired %x %x\n",
- smem_gpio->num_fired[0], smem_gpio->num_fired[1]);
- tasklet_schedule(&msm_gpio_sleep_int_tasklet);
- }
-}
-
-
-int gpio_tlmm_config(unsigned config, unsigned disable)
-{
- return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable);
-}
-EXPORT_SYMBOL(gpio_tlmm_config);
-
-int msm_gpios_request_enable(const struct msm_gpio *table, int size)
-{
- int rc = msm_gpios_request(table, size);
- if (rc)
- return rc;
- rc = msm_gpios_enable(table, size);
- if (rc)
- msm_gpios_free(table, size);
- return rc;
-}
-EXPORT_SYMBOL(msm_gpios_request_enable);
-
-void msm_gpios_disable_free(const struct msm_gpio *table, int size)
-{
- msm_gpios_disable(table, size);
- msm_gpios_free(table, size);
-}
-EXPORT_SYMBOL(msm_gpios_disable_free);
-
-int msm_gpios_request(const struct msm_gpio *table, int size)
-{
- int rc;
- int i;
- const struct msm_gpio *g;
- for (i = 0; i < size; i++) {
- g = table + i;
- rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
- if (rc) {
- pr_err("gpio_request(%d) <%s> failed: %d\n",
- GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
- goto err;
- }
- }
- return 0;
-err:
- msm_gpios_free(table, i);
- return rc;
-}
-EXPORT_SYMBOL(msm_gpios_request);
-
-void msm_gpios_free(const struct msm_gpio *table, int size)
-{
- int i;
- const struct msm_gpio *g;
- for (i = size-1; i >= 0; i--) {
- g = table + i;
- gpio_free(GPIO_PIN(g->gpio_cfg));
- }
-}
-EXPORT_SYMBOL(msm_gpios_free);
-
-int msm_gpios_enable(const struct msm_gpio *table, int size)
-{
- int rc;
- int i;
- const struct msm_gpio *g;
- for (i = 0; i < size; i++) {
- g = table + i;
- rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
- if (rc) {
- pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
- " <%s> failed: %d\n",
- g->gpio_cfg, g->label ?: "?", rc);
- pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
- GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
- GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
- GPIO_DRVSTR(g->gpio_cfg));
- goto err;
- }
- }
- return 0;
-err:
- msm_gpios_disable(table, i);
- return rc;
-}
-EXPORT_SYMBOL(msm_gpios_enable);
-
-int msm_gpios_disable(const struct msm_gpio *table, int size)
-{
- int rc = 0;
- int i;
- const struct msm_gpio *g;
- for (i = size-1; i >= 0; i--) {
- int tmp;
- g = table + i;
- tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
- if (tmp) {
- pr_err("gpio_tlmm_config(0x%08x, GPIO_CFG_DISABLE)"
- " <%s> failed: %d\n",
- g->gpio_cfg, g->label ?: "?", rc);
- pr_err("pin %d func %d dir %d pull %d drvstr %d\n",
- GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
- GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
- GPIO_DRVSTR(g->gpio_cfg));
- if (!rc)
- rc = tmp;
- }
- }
-
- return rc;
-}
-EXPORT_SYMBOL(msm_gpios_disable);
-
-/* Locate the GPIO_OUT register for the given GPIO and return its address
- * and the bit position of the gpio's bit within the register.
- *
- * This function is used by gpiomux-v1 in order to support output transitions.
- */
-void msm_gpio_find_out(const unsigned gpio, void __iomem **out,
- unsigned *offset)
-{
- struct msm_gpio_chip *msm_chip = msm_gpio_chips;
-
- while (gpio >= msm_chip->chip.base + msm_chip->chip.ngpio)
- ++msm_chip;
-
- *out = msm_chip->regs.out;
- *offset = gpio - msm_chip->chip.base;
-}
-
-static int msm_gpio_probe(struct platform_device *dev)
-{
- int i, j = 0;
- int grp_irq;
-
- for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
- if (i - FIRST_GPIO_IRQ >=
- msm_gpio_chips[j].chip.base +
- msm_gpio_chips[j].chip.ngpio)
- j++;
- irq_set_chip_data(i, &msm_gpio_chips[j]);
- irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
- handle_edge_irq);
- set_irq_flags(i, IRQF_VALID);
- }
-
- for (i = 0; i < dev->num_resources; i++) {
- grp_irq = platform_get_irq(dev, i);
- if (grp_irq < 0)
- return -ENXIO;
-
- irq_set_chained_handler(grp_irq, msm_gpio_irq_handler);
- irq_set_irq_wake(grp_irq, (i + 1));
- }
-
- for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
- spin_lock_init(&msm_gpio_chips[i].lock);
- __raw_writel(0, msm_gpio_chips[i].regs.int_en);
- gpiochip_add(&msm_gpio_chips[i].chip);
- }
-
- mb();
- return 0;
-}
-
-static struct platform_driver msm_gpio_driver = {
- .probe = msm_gpio_probe,
- .driver = {
- .name = "msmgpio",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init msm_gpio_init(void)
-{
- return platform_driver_register(&msm_gpio_driver);
-}
-postcore_initcall(msm_gpio_init);
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
deleted file mode 100644
index 8c42bd61872b..000000000000
--- a/drivers/gpio/gpio-msm-v2.c
+++ /dev/null
@@ -1,225 +0,0 @@
-/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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/bitmap.h>
-#include <linux/bitops.h>
-#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-
-#include <mach/msm_iomap.h>
-#include <mach/gpiomux.h>
-#include "gpio-msm-common.h"
-
-/* Bits of interest in the GPIO_IN_OUT register.
- */
-enum {
- GPIO_IN_BIT = 0,
- GPIO_OUT_BIT = 1
-};
-
-/* Bits of interest in the GPIO_INTR_STATUS register.
- */
-enum {
- INTR_STATUS_BIT = 0,
-};
-
-/* Bits of interest in the GPIO_CFG register.
- */
-enum {
- GPIO_OE_BIT = 9,
-};
-
-/* Bits of interest in the GPIO_INTR_CFG register.
- */
-enum {
- INTR_ENABLE_BIT = 0,
- INTR_POL_CTL_BIT = 1,
- INTR_DECT_CTL_BIT = 2,
- INTR_RAW_STATUS_EN_BIT = 3,
-};
-
-/* Codes of interest in GPIO_INTR_CFG_SU.
- */
-enum {
- TARGET_PROC_SCORPION = 4,
- TARGET_PROC_NONE = 7,
-};
-
-/*
- * There is no 'DC_POLARITY_LO' because the GIC is incapable
- * of asserting on falling edge or level-low conditions. Even though
- * the registers allow for low-polarity inputs, the case can never arise.
- */
-enum {
- DC_POLARITY_HI = BIT(11),
- DC_IRQ_ENABLE = BIT(3),
-};
-
-/*
- * When a GPIO triggers, two separate decisions are made, controlled
- * by two separate flags.
- *
- * - First, INTR_RAW_STATUS_EN controls whether or not the GPIO_INTR_STATUS
- * register for that GPIO will be updated to reflect the triggering of that
- * gpio. If this bit is 0, this register will not be updated.
- * - Second, INTR_ENABLE controls whether an interrupt is triggered.
- *
- * If INTR_ENABLE is set and INTR_RAW_STATUS_EN is NOT set, an interrupt
- * can be triggered but the status register will not reflect it.
- */
-#define INTR_RAW_STATUS_EN BIT(INTR_RAW_STATUS_EN_BIT)
-#define INTR_ENABLE BIT(INTR_ENABLE_BIT)
-#define INTR_DECT_CTL_EDGE BIT(INTR_DECT_CTL_BIT)
-#define INTR_POL_CTL_HI BIT(INTR_POL_CTL_BIT)
-
-#define GPIO_INTR_CFG_SU(gpio) (MSM_TLMM_BASE + 0x0400 + (0x04 * (gpio)))
-#define DIR_CONN_INTR_CFG_SU(irq) (MSM_TLMM_BASE + 0x0700 + (0x04 * (irq)))
-#define GPIO_CONFIG(gpio) (MSM_TLMM_BASE + 0x1000 + (0x10 * (gpio)))
-#define GPIO_IN_OUT(gpio) (MSM_TLMM_BASE + 0x1004 + (0x10 * (gpio)))
-#define GPIO_INTR_CFG(gpio) (MSM_TLMM_BASE + 0x1008 + (0x10 * (gpio)))
-#define GPIO_INTR_STATUS(gpio) (MSM_TLMM_BASE + 0x100c + (0x10 * (gpio)))
-
-static inline void set_gpio_bits(unsigned n, void __iomem *reg)
-{
- __raw_writel(__raw_readl(reg) | n, reg);
-}
-
-static inline void clr_gpio_bits(unsigned n, void __iomem *reg)
-{
- __raw_writel(__raw_readl(reg) & ~n, reg);
-}
-
-unsigned __msm_gpio_get_inout(unsigned gpio)
-{
- return __raw_readl(GPIO_IN_OUT(gpio)) & BIT(GPIO_IN_BIT);
-}
-
-void __msm_gpio_set_inout(unsigned gpio, unsigned val)
-{
- __raw_writel(val ? BIT(GPIO_OUT_BIT) : 0, GPIO_IN_OUT(gpio));
-}
-
-void __msm_gpio_set_config_direction(unsigned gpio, int input, int val)
-{
- if (input)
- clr_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio));
- else {
- __msm_gpio_set_inout(gpio, val);
- set_gpio_bits(BIT(GPIO_OE_BIT), GPIO_CONFIG(gpio));
- }
-}
-
-void __msm_gpio_set_polarity(unsigned gpio, unsigned val)
-{
- if (val)
- clr_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
- else
- set_gpio_bits(INTR_POL_CTL_HI, GPIO_INTR_CFG(gpio));
-}
-
-unsigned __msm_gpio_get_intr_status(unsigned gpio)
-{
- return __raw_readl(GPIO_INTR_STATUS(gpio)) &
- BIT(INTR_STATUS_BIT);
-}
-
-void __msm_gpio_set_intr_status(unsigned gpio)
-{
- __raw_writel(BIT(INTR_STATUS_BIT), GPIO_INTR_STATUS(gpio));
-}
-
-unsigned __msm_gpio_get_intr_config(unsigned gpio)
-{
- return __raw_readl(GPIO_INTR_CFG(gpio));
-}
-
-void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val)
-{
- if (val) {
- set_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
-
- } else {
- clr_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
- }
-}
-
-unsigned __msm_gpio_get_intr_cfg_enable(unsigned gpio)
-{
- return __msm_gpio_get_intr_config(gpio) & INTR_ENABLE;
-}
-
-void __msm_gpio_set_intr_cfg_type(unsigned gpio, unsigned type)
-{
- unsigned cfg;
-
- /* RAW_STATUS_EN is left on for all gpio irqs. Due to the
- * internal circuitry of TLMM, toggling the RAW_STATUS
- * could cause the INTR_STATUS to be set for EDGE interrupts.
- */
- cfg = __msm_gpio_get_intr_config(gpio);
- cfg |= INTR_RAW_STATUS_EN;
- __raw_writel(cfg, GPIO_INTR_CFG(gpio));
- __raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
-
- cfg = __msm_gpio_get_intr_config(gpio);
- if (type & IRQ_TYPE_EDGE_BOTH)
- cfg |= INTR_DECT_CTL_EDGE;
- else
- cfg &= ~INTR_DECT_CTL_EDGE;
-
- if (type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH))
- cfg |= INTR_POL_CTL_HI;
- else
- cfg &= ~INTR_POL_CTL_HI;
-
- __raw_writel(cfg, GPIO_INTR_CFG(gpio));
- /* Sometimes it might take a little while to update
- * the interrupt status after the RAW_STATUS is enabled
- * We clear the interrupt status before enabling the
- * interrupt in the unmask call-back.
- */
- udelay(5);
-}
-
-void __gpio_tlmm_config(unsigned config)
-{
- uint32_t flags;
- unsigned gpio = GPIO_PIN(config);
-
- flags = ((GPIO_DIR(config) << 9) & (0x1 << 9)) |
- ((GPIO_DRVSTR(config) << 6) & (0x7 << 6)) |
- ((GPIO_FUNC(config) << 2) & (0xf << 2)) |
- ((GPIO_PULL(config) & 0x3));
- __raw_writel(flags, GPIO_CONFIG(gpio));
-}
-
-void __msm_gpio_install_direct_irq(unsigned gpio, unsigned irq,
- unsigned int input_polarity)
-{
- uint32_t bits;
-
- __raw_writel(__raw_readl(GPIO_CONFIG(gpio)) | BIT(GPIO_OE_BIT),
- GPIO_CONFIG(gpio));
- __raw_writel(__raw_readl(GPIO_INTR_CFG(gpio)) &
- ~(INTR_RAW_STATUS_EN | INTR_ENABLE),
- GPIO_INTR_CFG(gpio));
- __raw_writel(DC_IRQ_ENABLE | TARGET_PROC_NONE,
- GPIO_INTR_CFG_SU(gpio));
-
- bits = TARGET_PROC_SCORPION | (gpio << 3);
- if (input_polarity)
- bits |= DC_POLARITY_HI;
- __raw_writel(bits, DIR_CONN_INTR_CFG_SU(irq));
-}
diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c
index 27af202c5d86..9379fb0dfff6 100644
--- a/drivers/gpio/gpio-sx150x.c
+++ b/drivers/gpio/gpio-sx150x.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010, 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -18,7 +18,10 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/of_gpio.h>
#include <linux/i2c/sx150x.h>
+#include <linux/of.h>
+#include <linux/delay.h>
#define NO_UPDATE_PENDING -1
@@ -53,8 +56,11 @@ struct sx150x_chip {
struct mutex lock;
};
+#define SX1508Q_ID 0
+#define SX1509Q_ID 1
+
static const struct sx150x_device_data sx150x_devices[] = {
- [0] = { /* sx1508q */
+ [SX1508Q_ID] = { /* sx1508q */
.reg_pullup = 0x03,
.reg_pulldn = 0x04,
.reg_drain = 0x05,
@@ -69,7 +75,7 @@ static const struct sx150x_device_data sx150x_devices[] = {
.reg_reset = 0x7d,
.ngpios = 8
},
- [1] = { /* sx1509q */
+ [SX1509Q_ID] = { /* sx1509q */
.reg_pullup = 0x07,
.reg_pulldn = 0x09,
.reg_drain = 0x0b,
@@ -86,9 +92,24 @@ static const struct sx150x_device_data sx150x_devices[] = {
},
};
+#ifdef CONFIG_OF
+static struct of_device_id sx150x_match_table[] = {
+ {
+ .compatible = "sx1508q", .data = NULL,
+ },
+ {
+ .compatible = "sx1509q", .data = NULL,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sx150x_match_table);
+#else
+#define sx150x_match_table NULL
+#endif
+
static const struct i2c_device_id sx150x_id[] = {
- {"sx1508q", 0},
- {"sx1509q", 1},
+ {"sx1508q", SX1508Q_ID},
+ {"sx1509q", SX1509Q_ID},
{}
};
MODULE_DEVICE_TABLE(i2c, sx150x_id);
@@ -424,6 +445,13 @@ static void sx150x_init_chip(struct sx150x_chip *chip,
{
mutex_init(&chip->lock);
+ if (client->dev.of_node) {
+ if (of_device_is_compatible(client->dev.of_node, "sx1508q"))
+ driver_data = SX1508Q_ID;
+ else
+ driver_data = SX1509Q_ID;
+ }
+
chip->client = client;
chip->dev_cfg = &sx150x_devices[driver_data];
chip->gpio_chip.label = client->name;
@@ -438,6 +466,10 @@ static void sx150x_init_chip(struct sx150x_chip *chip,
if (pdata->oscio_is_gpo)
++chip->gpio_chip.ngpio;
+ if (client && client->dev.of_node)
+ chip->gpio_chip.of_node =
+ of_node_get(client->dev.of_node);
+
chip->irq_chip.name = client->name;
chip->irq_chip.irq_mask = sx150x_irq_mask;
chip->irq_chip.irq_unmask = sx150x_irq_unmask;
@@ -466,6 +498,29 @@ static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg)
static int sx150x_reset(struct sx150x_chip *chip)
{
int err;
+ struct device_node *np = chip->gpio_chip.of_node;
+ enum of_gpio_flags flags = OF_GPIO_ACTIVE_LOW;
+ int rst_gpio;
+
+ if (np) {
+ rst_gpio = of_get_named_gpio_flags(np, "sx150x,reset_gpio",
+ 0, &flags);
+ if (rst_gpio < 0)
+ pr_debug("%s: No Reset GPIO found %d\n",
+ __func__, rst_gpio);
+ else {
+ err = gpio_request(rst_gpio, "sx150x_rst");
+ if (err) {
+ pr_err("Failed to get reset GPIO %d\n", err);
+ return err;
+ }
+
+ /* Max. time for NRESET low is 2.5ms */
+ gpio_direction_output(rst_gpio, 0x0);
+ usleep(2500);
+ gpio_direction_output(rst_gpio, 0x1);
+ }
+ }
err = i2c_smbus_write_byte_data(chip->client,
chip->dev_cfg->reg_reset,
@@ -572,16 +627,85 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip)
}
}
+#ifdef CONFIG_OF
+static int sx150x_parse_dt(struct device *dev,
+ struct sx150x_platform_data *pdata)
+{
+ int rc;
+ unsigned int temp;
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ pdata->oscio_is_gpo = of_property_read_bool(np, "sx150x,oscio_is_gpo");
+ pdata->reset_during_probe =
+ of_property_read_bool(np, "sx150x,reset_onprobe");
+
+ rc = of_property_read_u32(np, "sx150x,pullup_ena", &temp);
+ if (rc) {
+ pr_err("%s: Failed to find pullup_ena %d\n", __func__, rc);
+ return rc;
+ }
+ pdata->io_pullup_ena = temp;
+
+ rc = of_property_read_u32(np, "sx150x,pulldn_ena", &temp);
+ if (rc) {
+ pr_err("%s: Failed to find pulldn_ena %d\n", __func__, rc);
+ return rc;
+ }
+ pdata->io_pulldn_ena = temp;
+
+ rc = of_property_read_u32(np, "sx150x,float_ena", &temp);
+ if (rc) {
+ pr_err("%s: Failed to find float_ena %d\n", __func__, rc);
+ return rc;
+ }
+ pdata->io_open_drain_ena = temp;
+
+ rc = of_property_read_u32(np, "sx150x,polarity", &temp);
+ if (rc) {
+ pr_err("%s: Failed to find polarity %d\n", __func__, rc);
+ return rc;
+ }
+ pdata->io_polarity = temp;
+
+ /* TODO: Add support for Interrupts */
+ pdata->irq_summary = -1;
+ pdata->gpio_base = -1;
+
+ return 0;
+}
+#else
+static int sx150x_parse_dt(struct device *dev,
+ struct sx150x_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int sx150x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
static const u32 i2c_funcs = I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WRITE_WORD_DATA;
+ I2C_FUNC_SMBUS_WRITE_WORD_DATA;
struct sx150x_platform_data *pdata;
struct sx150x_chip *chip;
int rc;
- pdata = client->dev.platform_data;
+ if (client->dev.of_node) {
+ pdata = devm_kzalloc(&client->dev,
+ sizeof(struct sx150x_platform_data), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ rc = sx150x_parse_dt(&client->dev, pdata);
+ if (rc)
+ return rc;
+ } else
+ pdata = client->dev.platform_data;
if (!pdata)
return -EINVAL;
@@ -640,7 +764,8 @@ static int sx150x_remove(struct i2c_client *client)
static struct i2c_driver sx150x_driver = {
.driver = {
.name = "sx150x",
- .owner = THIS_MODULE
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sx150x_match_table)
},
.probe = sx150x_probe,
.remove = sx150x_remove,
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index 2b6f83d4d196..5fa2514628a6 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -32,8 +32,8 @@
#include "../ion_priv.h"
#include "ion_cp_common.h"
-#define ION_COMPAT_STR "qti,msm-ion"
-#define ION_COMPAT_MEM_RESERVE_STR "qti,msm-ion-reserve"
+#define ION_COMPAT_STR "qcom,msm-ion"
+#define ION_COMPAT_MEM_RESERVE_STR "qcom,msm-ion-reserve"
static struct ion_device *idev;
static int num_heaps;
@@ -445,7 +445,7 @@ static int msm_init_extra_data(struct device_node *node,
unsigned int val;
ret = of_property_read_u32(node,
- "qti,default-prefetch-size", &val);
+ "qcom,default-prefetch-size", &val);
if (!ret) {
heap->extra_data = kzalloc(sizeof(struct ion_cma_pdata),
@@ -491,7 +491,7 @@ static int msm_ion_get_heap_type_from_dt_node(struct device_node *node,
{
const char *name;
int i, ret = -EINVAL;
- ret = of_property_read_string(node, "qti,ion-heap-type", &name);
+ ret = of_property_read_string(node, "qcom,ion-heap-type", &name);
if (ret)
goto out;
for (i = 0; i < ARRAY_SIZE(heap_types_info); ++i) {
@@ -545,7 +545,7 @@ static void msm_ion_get_heap_align(struct device_node *node,
{
unsigned int val;
- int ret = of_property_read_u32(node, "qti,heap-align", &val);
+ int ret = of_property_read_u32(node, "qcom,heap-align", &val);
if (!ret) {
switch ((int) heap->type) {
case ION_HEAP_TYPE_CP:
@@ -578,11 +578,11 @@ static int msm_ion_get_heap_size(struct device_node *node,
u32 out_values[2];
struct device_node *pnode;
- ret = of_property_read_u32(node, "qti,memory-reservation-size", &val);
+ ret = of_property_read_u32(node, "qcom,memory-reservation-size", &val);
if (!ret)
heap->size = val;
- ret = of_property_read_u32_array(node, "qti,memory-fixed",
+ ret = of_property_read_u32_array(node, "qcom,memory-fixed",
out_values, 2);
if (!ret) {
heap->size = out_values[1];
@@ -617,7 +617,7 @@ static void msm_ion_get_heap_base(struct device_node *node,
int ret = 0;
struct device_node *pnode;
- ret = of_property_read_u32_array(node, "qti,memory-fixed",
+ ret = of_property_read_u32_array(node, "qcom,memory-fixed",
out_values, 2);
if (!ret)
heap->base = out_values[0];
@@ -635,7 +635,7 @@ static void msm_ion_get_heap_adjacent(struct device_node *node,
struct ion_platform_heap *heap)
{
unsigned int val;
- int ret = of_property_read_u32(node, "qti,heap-adjacent", &val);
+ int ret = of_property_read_u32(node, "qcom,heap-adjacent", &val);
if (!ret) {
switch (heap->type) {
case ION_HEAP_TYPE_CARVEOUT:
@@ -805,7 +805,7 @@ static long msm_ion_custom_ioctl(struct ion_client *client,
sizeof(struct ion_flush_data)))
return -EFAULT;
- if (data.handle >= 0) {
+ if (data.handle > 0) {
handle = ion_handle_get_by_id(client, (int)data.handle);
if (IS_ERR(handle)) {
pr_info("%s: Could not find handle: %d\n",
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index b5f50d03a958..b2d8c1c178d2 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -30,6 +30,7 @@
#include "kgsl_cffdump.h"
#include "kgsl_sharedmem.h"
#include "kgsl_iommu.h"
+#include "kgsl_trace.h"
#include "adreno.h"
#include "adreno_pm4types.h"
@@ -1407,7 +1408,7 @@ static int adreno_of_get_iommu(struct device_node *parent,
data->physstart = reg_val[0];
data->physend = data->physstart + reg_val[1] - 1;
data->iommu_halt_enable = of_property_read_bool(node,
- "qti,iommu-enable-halt");
+ "qcom,iommu-enable-halt");
data->iommu_ctx_count = 0;
@@ -1861,6 +1862,8 @@ static int _adreno_start(struct adreno_device *adreno_dev)
device->reset_counter++;
+ set_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
+
return 0;
error_rb_stop:
@@ -1898,7 +1901,25 @@ static void adreno_start_work(struct work_struct *work)
set_user_nice(current, _wake_nice);
mutex_lock(&device->mutex);
- _status = _adreno_start(adreno_dev);
+ /*
+ * If adreno start is already called, no need to call it again
+ * it can lead to unpredictable behavior if we try to start
+ * the device that is already started.
+ * Below is the sequence of events that can go bad without the check
+ * 1) thread 1 calls adreno_start to be scheduled on high priority wq
+ * 2) thread 2 calls adreno_start with normal priority
+ * 3) thread 1 after checking the device to be in slumber state gives
+ * up mutex to be scheduled on high priority wq
+ * 4) thread 2 after checking the device to be in slumber state gets
+ * the mutex and finishes adreno_start before thread 1 is scheduled
+ * on high priority wq.
+ * 5) thread 1 gets scheduled on high priority wq and executes
+ * adreno_start again. This leads to unpredictable behavior.
+ */
+ if (!test_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv))
+ _status = _adreno_start(adreno_dev);
+ else
+ _status = 0;
mutex_unlock(&device->mutex);
}
@@ -1962,6 +1983,8 @@ static int adreno_stop(struct kgsl_device *device)
kgsl_cffdump_close(device);
+ clear_bit(ADRENO_DEVICE_STARTED, &adreno_dev->priv);
+
return 0;
}
@@ -2807,7 +2830,7 @@ static void adreno_regwrite(struct kgsl_device *device,
if (!in_interrupt())
kgsl_pre_hwaccess(device);
- kgsl_trace_regwrite(device, offsetwords, value);
+ trace_kgsl_regwrite(device, offsetwords, value);
kgsl_cffdump_regwrite(device, offsetwords << 2, value);
reg = (unsigned int *)(device->reg_virt + (offsetwords << 2));
@@ -2847,7 +2870,7 @@ static int adreno_waittimestamp(struct kgsl_device *device,
return -EINVAL;
ret = adreno_drawctxt_wait(ADRENO_DEVICE(device), context,
- timestamp, msecs_to_jiffies(msecs));
+ timestamp, msecs);
/* If the context got invalidated then return a specific error */
drawctxt = ADRENO_CONTEXT(context);
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 204cc807d70c..8b48ba0d634c 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -56,8 +56,6 @@
#define KGSL_END_OF_PROFILE_IDENTIFIER 0x2DEFADE2
#define KGSL_PWRON_FIXUP_IDENTIFIER 0x2AFAFAFA
-void adreno_debugfs_init(struct kgsl_device *device);
-
#define ADRENO_ISTORE_START 0x5000 /* Istore offset */
#define ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW 50
@@ -206,6 +204,7 @@ enum adreno_device_flags {
ADRENO_DEVICE_INITIALIZED = 2,
ADRENO_DEVICE_CORESIGHT = 3,
ADRENO_DEVICE_HANG_INTR = 4,
+ ADRENO_DEVICE_STARTED = 5,
};
#define PERFCOUNTER_FLAG_NONE 0x0
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 17c3c8a0a8a8..a8ab126959ed 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1947,6 +1947,7 @@ void a3xx_perfcounter_close(struct adreno_device *adreno_dev)
int a3xx_perfcounter_init(struct adreno_device *adreno_dev)
{
int ret;
+ struct kgsl_device *device = &adreno_dev->dev;
/* SP[3] counter is broken on a330 so disable it if a330 device */
if (adreno_is_a330(adreno_dev))
a3xx_perfcounters_sp[3].countable = KGSL_PERFCOUNTER_BROKEN;
@@ -1959,16 +1960,19 @@ int a3xx_perfcounter_init(struct adreno_device *adreno_dev)
ret = adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_PWR, 1,
NULL, NULL, PERFCOUNTER_FLAG_KERNEL);
- /* VBIF waiting for RAM */
- ret |= adreno_perfcounter_get(adreno_dev,
+ if (device->pwrctrl.bus_control) {
+ /* VBIF waiting for RAM */
+ ret |= adreno_perfcounter_get(adreno_dev,
KGSL_PERFCOUNTER_GROUP_VBIF_PWR, 0,
NULL, NULL, PERFCOUNTER_FLAG_KERNEL);
- /* VBIF DDR cycles */
- ret |= adreno_perfcounter_get(adreno_dev, KGSL_PERFCOUNTER_GROUP_VBIF,
+ /* VBIF DDR cycles */
+ ret |= adreno_perfcounter_get(adreno_dev,
+ KGSL_PERFCOUNTER_GROUP_VBIF,
VBIF_AXI_TOTAL_BEATS,
&adreno_dev->ram_cycles_lo, NULL,
PERFCOUNTER_FLAG_KERNEL);
+ }
/* Default performance counter profiling to false */
adreno_dev->profile.enabled = false;
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index 82c1146e5036..33a1df25cf25 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -392,6 +392,10 @@ static void a4xx_start(struct adreno_device *adreno_dev)
/* Turn on the GPU busy counter and let it run free */
memset(&adreno_dev->busy_data, 0, sizeof(adreno_dev->busy_data));
+ /* Disable L2 bypass to avoid UCHE out of bounds errors */
+ kgsl_regwrite(device, UCHE_TRAP_BASE_LO, 0xffff0000);
+ kgsl_regwrite(device, UCHE_TRAP_BASE_HI, 0xffff0000);
+
/* On A420 cores turn on SKIP_IB2_DISABLE in addition to the default */
kgsl_regwrite(device, A4XX_CP_DEBUG, A4XX_CP_DEBUG_DEFAULT |
(adreno_is_a420(adreno_dev) ? (1 << 29) : 0));
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index 1fd4a5a02aab..301aaa5fafb8 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -20,6 +20,7 @@
#include "kgsl.h"
#include "kgsl_sharedmem.h"
#include "kgsl_cffdump.h"
+#include "kgsl_trace.h"
#include "adreno.h"
#include "adreno_pm4types.h"
@@ -1223,7 +1224,7 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
done:
device->pwrctrl.irq_last = 0;
- kgsl_trace_issueibcmds(device, context->id, cmdbatch,
+ trace_kgsl_issueibcmds(device, context->id, cmdbatch,
cmdbatch->timestamp, cmdbatch->flags, ret,
drawctxt->type);
diff --git a/drivers/gpu/msm/adreno_trace.h b/drivers/gpu/msm/adreno_trace.h
index d0b98b0776a4..6301ea479efd 100644
--- a/drivers/gpu/msm/adreno_trace.h
+++ b/drivers/gpu/msm/adreno_trace.h
@@ -18,6 +18,7 @@
#define TRACE_SYSTEM kgsl
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE adreno_trace
#include <linux/tracepoint.h>
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index fe9a8e7ac9e9..81b290b33ddd 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -65,44 +65,6 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry);
static void
kgsl_put_process_private(struct kgsl_device *device,
struct kgsl_process_private *private);
-/**
- * kgsl_trace_issueibcmds() - Call trace_issueibcmds by proxy
- * device: KGSL device
- * id: ID of the context submitting the command
- * cmdbatch: Pointer to kgsl_cmdbatch describing these commands
- * timestamp: Timestamp assigned to the command batch
- * flags: Flags sent by the user
- * result: Result of the submission attempt
- * type: Type of context issuing the command
- *
- * Wrap the issueibcmds ftrace hook into a function that can be called from the
- * GPU specific modules.
- */
-void kgsl_trace_issueibcmds(struct kgsl_device *device, int id,
- struct kgsl_cmdbatch *cmdbatch,
- unsigned int timestamp, unsigned int flags,
- int result, unsigned int type)
-{
- trace_kgsl_issueibcmds(device, id, cmdbatch,
- timestamp, flags, result, type);
-}
-EXPORT_SYMBOL(kgsl_trace_issueibcmds);
-
-/**
- * kgsl_trace_regwrite - call regwrite ftrace function by proxy
- * device: KGSL device
- * offset: dword offset of the register being written
- * value: Value of the register being written
- *
- * Wrap the regwrite ftrace hook into a function that can be called from the
- * GPU specific modules.
- */
-void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset,
- unsigned int value)
-{
- trace_kgsl_regwrite(device, offset, value);
-}
-EXPORT_SYMBOL(kgsl_trace_regwrite);
static int kgsl_memfree_hist_init(void)
{
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index 60fade0e6c47..ebadca4bf1d1 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -291,14 +291,6 @@ extern const struct dev_pm_ops kgsl_pm_ops;
int kgsl_suspend_driver(struct platform_device *pdev, pm_message_t state);
int kgsl_resume_driver(struct platform_device *pdev);
-void kgsl_trace_regwrite(struct kgsl_device *device, unsigned int offset,
- unsigned int value);
-
-void kgsl_trace_issueibcmds(struct kgsl_device *device, int id,
- struct kgsl_cmdbatch *cmdbatch,
- unsigned int timestamp, unsigned int flags,
- int result, unsigned int type);
-
#ifdef CONFIG_MSM_KGSL_DRM
extern int kgsl_drm_init(struct platform_device *dev);
extern void kgsl_drm_exit(void);
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index 600d9b25b185..0cd5e36c98cf 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -210,7 +210,7 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
kgsl_event_func func, void *priv, void *owner)
{
struct kgsl_event *event;
- unsigned int queued, cur_ts;
+ unsigned int queued = 0, cur_ts;
struct kgsl_context *context = NULL;
BUG_ON(!mutex_is_locked(&device->mutex));
@@ -223,12 +223,20 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
if (context == NULL)
return -EINVAL;
}
+ /*
+ * If the caller is creating their own timestamps, let them schedule
+ * events in the future. Otherwise only allow timestamps that have been
+ * queued.
+ */
+ if (context == NULL ||
+ ((context->flags & KGSL_CONTEXT_USER_GENERATED_TS) == 0)) {
+ kgsl_readtimestamp(device, context,
+ KGSL_TIMESTAMP_QUEUED, &queued);
- kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_QUEUED, &queued);
-
- if (timestamp_cmp(ts, queued) > 0) {
- kgsl_context_put(context);
- return -EINVAL;
+ if (timestamp_cmp(ts, queued) > 0) {
+ kgsl_context_put(context);
+ return -EINVAL;
+ }
}
kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED, &cur_ts);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 0262896e1d73..a03809a6b1ec 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -130,6 +130,9 @@ void kgsl_pwrctrl_buslevel_update(struct kgsl_device *device,
int buslevel = 0;
if (!pwr->pcl)
return;
+ /* the bus should be ON to update the active frequency */
+ if (on && !(test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)))
+ return;
/*
* If the bus should remain on calculate our request and submit it,
* otherwise request bus level 0, off.
@@ -173,25 +176,12 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
pwr->bus_mod = 0;
pwrlevel = &pwr->pwrlevels[pwr->active_pwrlevel];
- if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) {
- kgsl_pwrctrl_buslevel_update(device, true);
- if (pwr->ebi1_clk)
- clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq);
- }
+ kgsl_pwrctrl_buslevel_update(device, true);
if (test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags) ||
(device->state == KGSL_STATE_NAP)) {
/*
- * On some platforms, instability is caused on
- * changing clock freq when the core is busy.
- * Idle the gpu core before changing the clock freq.
- */
-
- if (pwr->idle_needed == true)
- device->ftbl->idle(device);
-
- /*
* Don't shift by more than one level at a time to
* avoid glitches.
*/
@@ -949,22 +939,12 @@ static void kgsl_pwrctrl_axi(struct kgsl_device *device, int state)
if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON,
&pwr->power_flags)) {
trace_kgsl_bus(device, state);
- if (pwr->ebi1_clk) {
- clk_set_rate(pwr->ebi1_clk, 0);
- clk_disable_unprepare(pwr->ebi1_clk);
- }
kgsl_pwrctrl_buslevel_update(device, false);
}
} else if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON,
&pwr->power_flags)) {
trace_kgsl_bus(device, state);
- if (pwr->ebi1_clk) {
- clk_prepare_enable(pwr->ebi1_clk);
- clk_set_rate(pwr->ebi1_clk,
- pwr->pwrlevels[pwr->active_pwrlevel].
- bus_freq);
- }
kgsl_pwrctrl_buslevel_update(device, true);
}
}
@@ -1103,16 +1083,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
pwr->power_flags = 0;
- pwr->idle_needed = pdata->idle_needed;
pwr->interval_timeout = pdata->idle_timeout;
pwr->strtstp_sleepwake = pdata->strtstp_sleepwake;
- pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk");
- if (IS_ERR(pwr->ebi1_clk))
- pwr->ebi1_clk = NULL;
- else
- clk_set_rate(pwr->ebi1_clk,
- pwr->pwrlevels[pwr->active_pwrlevel].
- bus_freq);
/* Set the CPU latency to 501usec to allow low latency PC modes */
pwr->pm_qos_latency = 501;
@@ -1183,8 +1155,6 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
pm_runtime_disable(device->parentdev);
- clk_put(pwr->ebi1_clk);
-
if (pwr->pcl)
msm_bus_scale_unregister_client(pwr->pcl);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index a5416a11c208..9362840a7279 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -43,7 +43,6 @@ struct kgsl_clk_stats {
/**
* struct kgsl_pwrctrl - Power control settings for a KGSL device
* @interrupt_num - The interrupt number for the device
- * @ebi1_clk - Pointer to the EBI clock structure
* @grp_clks - Array of clocks structures that we control
* @power_flags - Control flags for power
* @pwrlevels - List of supported power levels
@@ -59,7 +58,6 @@ struct kgsl_clk_stats {
* @gpu_reg - pointer to the regulator structure for gpu_reg
* @gpu_cx - pointer to the regulator structure for gpu_cx
* @pcl - bus scale identifier
- * @idle_needed - true if the device needs a idle before clock change
* @irq_name - resource name for the IRQ
* @clk_stats - structure of clock statistics
* @pm_qos_req_dma - the power management quality of service structure
@@ -72,7 +70,6 @@ struct kgsl_clk_stats {
struct kgsl_pwrctrl {
int interrupt_num;
- struct clk *ebi1_clk;
struct clk *grp_clks[KGSL_MAX_CLKS];
unsigned long power_flags;
unsigned long ctrl_flags;
@@ -90,7 +87,6 @@ struct kgsl_pwrctrl {
struct regulator *gpu_reg;
struct regulator *gpu_cx;
uint32_t pcl;
- unsigned int idle_needed;
const char *irq_name;
struct kgsl_clk_stats clk_stats;
struct pm_qos_request pm_qos_req_dma;
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 9b71ae69f4b4..8e6715183af2 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -131,7 +131,7 @@ static inline unsigned int kgsl_get_sg_pa(struct scatterlist *sg)
static inline void *kgsl_sg_alloc(unsigned int sglen)
{
- if (sglen >= ULONG_MAX / sizeof(struct scatterlist))
+ if ((sglen == 0) || (sglen >= ULONG_MAX / sizeof(struct scatterlist)))
return NULL;
if ((sglen * sizeof(struct scatterlist)) < PAGE_SIZE)
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index b35cebdc6846..d59342eece87 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -187,7 +187,6 @@ static int snapshot_os(struct kgsl_device *device,
header->power_level = pwr->active_pwrlevel;
header->power_interval_timeout = pwr->interval_timeout;
header->grpclk = kgsl_get_clkrate(pwr->grp_clks[0]);
- header->busclk = kgsl_get_clkrate(pwr->ebi1_clk);
/* Save the last active context */
kgsl_sharedmem_readl(&device->memstore, &header->current_context,
diff --git a/drivers/gpu/msm/kgsl_trace.c b/drivers/gpu/msm/kgsl_trace.c
index e432729fb353..253034e2a4fa 100644
--- a/drivers/gpu/msm/kgsl_trace.c
+++ b/drivers/gpu/msm/kgsl_trace.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -11,9 +11,14 @@
*
*/
+#include <linux/module.h>
+
#include "kgsl.h"
#include "kgsl_device.h"
/* Instantiate tracepoints */
#define CREATE_TRACE_POINTS
#include "kgsl_trace.h"
+
+EXPORT_TRACEPOINT_SYMBOL(kgsl_regwrite);
+EXPORT_TRACEPOINT_SYMBOL(kgsl_issueibcmds);
diff --git a/drivers/hwmon/epm_adc.c b/drivers/hwmon/epm_adc.c
index e271beb718b1..f62093ad3773 100644
--- a/drivers/hwmon/epm_adc.c
+++ b/drivers/hwmon/epm_adc.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/hwmon.h>
#include <linux/delay.h>
#include <linux/epm_adc.h>
@@ -133,6 +134,7 @@ struct epm_adc_drv {
uint32_t bus_id;
struct miscdevice misc;
uint32_t channel_mask;
+ uint32_t epm_global_en_gpio;
struct epm_chan_properties epm_psoc_ch_prop[0];
};
@@ -705,22 +707,24 @@ conv_err:
return rc;
}
-static int epm_adc_psoc_gpio_init(bool enable)
+static int epm_adc_psoc_gpio_init(struct epm_adc_drv *epm_adc,
+ bool enable)
{
int rc = 0;
if (enable) {
- rc = gpio_request(EPM_PSOC_GLOBAL_ENABLE, "EPM_PSOC_GLOBAL_EN");
+ rc = gpio_request(epm_adc->epm_global_en_gpio,
+ "EPM_PSOC_GLOBAL_EN");
if (!rc) {
- gpio_direction_output(EPM_PSOC_GLOBAL_ENABLE, 1);
+ gpio_direction_output(epm_adc->epm_global_en_gpio, 1);
} else {
pr_err("%s: Configure EPM_GLOBAL_EN Failed\n",
__func__);
return rc;
}
} else {
- gpio_direction_output(EPM_PSOC_GLOBAL_ENABLE, 0);
- gpio_free(EPM_PSOC_GLOBAL_ENABLE);
+ gpio_direction_output(epm_adc->epm_global_en_gpio, 0);
+ gpio_free(epm_adc->epm_global_en_gpio);
}
return 0;
@@ -899,6 +903,12 @@ static int epm_psoc_init(struct epm_adc_drv *epm_adc,
init_resp->num_dev = rx_buf[6];
init_resp->num_channel = rx_buf[7];
+ pr_debug("EPM PSOC response for hello command: resp_cmd:0x%x\n",
+ rx_buf[0]);
+ pr_debug("EPM PSOC version:0x%x\n", rx_buf[1]);
+ pr_debug("EPM PSOC firmware version:0x%x\n",
+ rx_buf[6] | rx_buf[5] | rx_buf[4] | rx_buf[3]);
+
return rc;
}
@@ -1694,7 +1704,7 @@ static long epm_adc_ioctl(struct file *file, unsigned int cmd,
}
if (!rc) {
- rc = epm_adc_psoc_gpio_init(true);
+ rc = epm_adc_psoc_gpio_init(epm_adc, true);
if (rc) {
pr_err("GPIO init failed\n");
return -EINVAL;
@@ -1709,7 +1719,7 @@ static long epm_adc_ioctl(struct file *file, unsigned int cmd,
case EPM_PSOC_ADC_DEINIT:
{
uint32_t result;
- result = epm_adc_psoc_gpio_init(false);
+ result = epm_adc_psoc_gpio_init(epm_adc, false);
if (copy_to_user((void __user *)arg, &result,
sizeof(uint32_t)))
@@ -2071,7 +2081,7 @@ static ssize_t epm_adc_psoc_show_in(struct device *dev,
struct epm_psoc_get_data psoc_get_meas;
int rc = 0;
- rc = epm_adc_psoc_gpio_init(true);
+ rc = epm_adc_psoc_gpio_init(epm_adc, true);
if (rc) {
pr_err("GPIO init failed\n");
return 0;
@@ -2111,7 +2121,7 @@ static ssize_t epm_adc_psoc_show_in(struct device *dev,
psoc_get_meas.reading_value,
attr->index);
- rc = epm_adc_psoc_gpio_init(false);
+ rc = epm_adc_psoc_gpio_init(epm_adc, false);
if (rc) {
pr_err("GPIO de-init failed\n");
return 0;
@@ -2177,7 +2187,7 @@ static int get_device_tree_data(struct spi_device *spi)
const struct device_node *node = spi->dev.of_node;
struct epm_adc_drv *epm_adc;
u32 *epm_ch_gain, *epm_ch_rsense;
- u32 rc = 0, epm_num_channels, i, channel_mask;
+ u32 rc = 0, epm_num_channels, i, channel_mask, epm_gpio_num;
if (!node)
return -EINVAL;
@@ -2224,6 +2234,13 @@ static int get_device_tree_data(struct spi_device *spi)
return -ENODEV;
}
+ epm_gpio_num = of_get_named_gpio(spi->dev.of_node,
+ "qcom,epm-enable-gpio", 0);
+ if (epm_gpio_num < 0) {
+ dev_err(&spi->dev, "missing global en gpio num\n");
+ return -ENODEV;
+ }
+
epm_adc = devm_kzalloc(&spi->dev,
sizeof(struct epm_adc_drv) +
(epm_num_channels *
@@ -2242,6 +2259,7 @@ static int get_device_tree_data(struct spi_device *spi)
}
epm_adc->channel_mask = channel_mask;
+ epm_adc->epm_global_en_gpio = epm_gpio_num;
epm_adc_drv = epm_adc;
return 0;
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
index ffb8dc7486a2..9c58718d56c2 100644
--- a/drivers/hwmon/qpnp-adc-common.c
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -238,6 +238,60 @@ static const struct qpnp_vadc_map_pt adcmap_qrd_skuaa_btm_threshold[] = {
{800, 549},
};
+static const struct qpnp_vadc_map_pt adcmap_qrd_skug_btm_threshold[] = {
+ {-200, 1338},
+ {-180, 1307},
+ {-160, 1276},
+ {-140, 1244},
+ {-120, 1213},
+ {-100, 1182},
+ {-80, 1151},
+ {-60, 1121},
+ {-40, 1092},
+ {-20, 1063},
+ {0, 1035},
+ {20, 1008},
+ {40, 982},
+ {60, 957},
+ {80, 933},
+ {100, 910},
+ {120, 889},
+ {140, 868},
+ {160, 848},
+ {180, 830},
+ {200, 812},
+ {220, 795},
+ {240, 780},
+ {260, 765},
+ {280, 751},
+ {300, 738},
+ {320, 726},
+ {340, 714},
+ {360, 704},
+ {380, 694},
+ {400, 684},
+ {420, 675},
+ {440, 667},
+ {460, 659},
+ {480, 652},
+ {500, 645},
+ {520, 639},
+ {540, 633},
+ {560, 627},
+ {580, 622},
+ {600, 617},
+ {620, 613},
+ {640, 608},
+ {660, 604},
+ {680, 600},
+ {700, 597},
+ {720, 593},
+ {740, 590},
+ {760, 587},
+ {780, 585},
+ {800, 582},
+};
+
/* Voltage to temperature */
static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
@@ -528,7 +582,8 @@ int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc,
if (!chan_properties || !chan_properties->offset_gain_numerator ||
!chan_properties->offset_gain_denominator || !adc_properties
- || !adc_chan_result)
+ || !adc_chan_result
+ || !chan_properties->adc_graph[CALIB_ABSOLUTE].dy)
return -EINVAL;
pmic_voltage = (adc_code -
@@ -698,6 +753,25 @@ int32_t qpnp_adc_scale_qrd_skuaa_batt_therm(struct qpnp_vadc_chip *chip,
}
EXPORT_SYMBOL(qpnp_adc_scale_qrd_skuaa_batt_therm);
+int32_t qpnp_adc_scale_qrd_skug_batt_therm(struct qpnp_vadc_chip *chip,
+ int32_t adc_code,
+ const struct qpnp_adc_properties *adc_properties,
+ const struct qpnp_vadc_chan_properties *chan_properties,
+ struct qpnp_vadc_result *adc_chan_result)
+{
+ int64_t bat_voltage = 0;
+
+ bat_voltage = qpnp_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+
+ return qpnp_adc_map_temp_voltage(
+ adcmap_qrd_skug_btm_threshold,
+ ARRAY_SIZE(adcmap_qrd_skug_btm_threshold),
+ bat_voltage,
+ &adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_skug_batt_therm);
+
int32_t qpnp_adc_scale_smb_batt_therm(struct qpnp_vadc_chip *chip,
int32_t adc_code,
const struct qpnp_adc_properties *adc_properties,
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
index dc8b991d5660..40734a2c3f88 100644
--- a/drivers/hwmon/qpnp-adc-current.c
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -1060,6 +1060,11 @@ int32_t qpnp_iadc_read(struct qpnp_iadc_chip *iadc,
if (qpnp_iadc_is_valid(iadc) < 0)
return -EPROBE_DEFER;
+ if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+ pr_err("raw offset errors! run iadc calibration again\n");
+ return -EINVAL;
+ }
+
rc = qpnp_check_pmic_temp(iadc);
if (rc) {
pr_err("Error checking pmic therm temp\n");
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
index c3cd522b0795..511dbc6d8b79 100644
--- a/drivers/hwmon/qpnp-adc-voltage.c
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -130,6 +130,7 @@ static struct qpnp_vadc_scale_fn vadc_scale_fn[] = {
[SCALE_QRD_BATT_THERM] = {qpnp_adc_scale_qrd_batt_therm},
[SCALE_QRD_SKUAA_BATT_THERM] = {qpnp_adc_scale_qrd_skuaa_batt_therm},
[SCALE_SMB_BATT_THERM] = {qpnp_adc_scale_smb_batt_therm},
+ [SCALE_QRD_SKUG_BATT_THERM] = {qpnp_adc_scale_qrd_skug_batt_therm},
};
static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
@@ -854,7 +855,14 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
}
pr_debug("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
- calib_read_1, calib_read_2);
+ calib_read_2, calib_read_1);
+
+ if (calib_read_1 == calib_read_2) {
+ pr_err("absolute reference raw: 625mV:0x%x 1.25V:0x%x\n",
+ calib_read_2, calib_read_1);
+ rc = -EINVAL;
+ goto calib_fail;
+ }
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_ABSOLUTE].dy =
(calib_read_1 - calib_read_2);
@@ -934,6 +942,14 @@ static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
pr_debug("ratiometric reference raw: VDD:0x%x GND:0x%x\n",
calib_read_1, calib_read_2);
+
+ if (calib_read_1 == calib_read_2) {
+ pr_err("ratiometric reference raw: VDD:0x%x GND:0x%x\n",
+ calib_read_1, calib_read_2);
+ rc = -EINVAL;
+ goto calib_fail;
+ }
+
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy =
(calib_read_1 - calib_read_2);
vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx =
diff --git a/drivers/hwspinlock/msm_remote_spinlock.c b/drivers/hwspinlock/msm_remote_spinlock.c
index e541f1f30884..c0ac7b0eae9f 100644
--- a/drivers/hwspinlock/msm_remote_spinlock.c
+++ b/drivers/hwspinlock/msm_remote_spinlock.c
@@ -19,7 +19,7 @@
#include <linux/of_address.h>
#include <linux/msm_remote_spinlock.h>
-#include <mach/msm_smem.h>
+#include <soc/qcom/smem.h>
#define SPINLOCK_PID_APPS 1
diff --git a/drivers/input/misc/bmp18x-core.c b/drivers/input/misc/bmp18x-core.c
index 6c0f2f2e0784..5821a5987ac9 100644
--- a/drivers/input/misc/bmp18x-core.c
+++ b/drivers/input/misc/bmp18x-core.c
@@ -88,6 +88,7 @@ struct bmp18x_data {
struct device *dev;
struct mutex lock;
struct bmp18x_calibration_data calibration;
+ struct sensors_classdev cdev;
u8 oversampling_setting;
u8 sw_oversampling_setting;
u32 raw_temperature;
@@ -113,9 +114,13 @@ static struct sensors_classdev sensors_cdev = {
.max_range = "1100.0",
.resolution = "0.01",
.sensor_power = "0.67",
- .min_delay = 20000,
+ .min_delay = 20000, /* microsecond */
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 200, /* millisecond */
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
};
#ifdef CONFIG_HAS_EARLYSUSPEND
@@ -403,6 +408,19 @@ static ssize_t show_sw_oversampling(struct device *dev,
static DEVICE_ATTR(sw_oversampling, S_IWUSR | S_IRUGO,
show_sw_oversampling, set_sw_oversampling);
+static ssize_t bmp18x_poll_delay_set(struct sensors_classdev *sensors_cdev,
+ unsigned int delay_msec)
+{
+ struct bmp18x_data *data = container_of(sensors_cdev,
+ struct bmp18x_data, cdev);
+ mutex_lock(&data->lock);
+ data->delay = delay_msec;
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+
static ssize_t show_delay(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -416,17 +434,43 @@ static ssize_t set_delay(struct device *dev,
{
struct bmp18x_data *data = dev_get_drvdata(dev);
unsigned long delay;
- int success = kstrtoul(buf, 10, &delay);
- if (success == 0) {
- mutex_lock(&data->lock);
- data->delay = delay;
- mutex_unlock(&data->lock);
- }
- return success;
+ int err = kstrtoul(buf, 10, &delay);
+ if (err < 0)
+ return err;
+
+ err = bmp18x_poll_delay_set(&data->cdev, delay);
+ if (err < 0)
+ return err;
+
+ return count;
}
-static DEVICE_ATTR(delay, S_IWUSR | S_IRUGO,
+
+static DEVICE_ATTR(poll_delay, S_IWUSR | S_IRUGO,
show_delay, set_delay);
+static ssize_t bmp18x_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enabled)
+{
+ struct bmp18x_data *data = container_of(sensors_cdev,
+ struct bmp18x_data, cdev);
+ struct device *dev = data->dev;
+
+ mutex_lock(&data->lock);
+ data->enable = enabled ? 1 : 0;
+
+ if (data->enable) {
+ bmp18x_enable(dev);
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(data->delay));
+ } else {
+ cancel_delayed_work_sync(&data->work);
+ bmp18x_disable(dev);
+ }
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
static ssize_t show_enable(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -440,24 +484,17 @@ static ssize_t set_enable(struct device *dev,
{
struct bmp18x_data *data = dev_get_drvdata(dev);
unsigned long enable;
- int success = kstrtoul(buf, 10, &enable);
- if (success == 0) {
- mutex_lock(&data->lock);
- data->enable = enable ? 1 : 0;
-
- if (data->enable) {
- bmp18x_enable(dev);
- schedule_delayed_work(&data->work,
- msecs_to_jiffies(data->delay));
- } else {
- cancel_delayed_work_sync(&data->work);
- bmp18x_disable(dev);
- }
- mutex_unlock(&data->lock);
+ int err = kstrtoul(buf, 10, &enable);
+ if (err < 0)
+ return err;
+
+ err = bmp18x_enable_set(&data->cdev, enable);
+ if (err < 0)
+ return err;
- }
return count;
}
+
static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO,
show_enable, set_enable);
@@ -499,7 +536,7 @@ static struct attribute *bmp18x_attributes[] = {
&dev_attr_pressure0_input.attr,
&dev_attr_oversampling.attr,
&dev_attr_sw_oversampling.attr,
- &dev_attr_delay.attr,
+ &dev_attr_poll_delay.attr,
&dev_attr_enable.attr,
NULL
};
@@ -628,7 +665,10 @@ int bmp18x_probe(struct device *dev, struct bmp18x_data_bus *data_bus)
if (err)
goto error_sysfs;
- err = sensors_classdev_register(&data->input->dev, &sensors_cdev);
+ data->cdev = sensors_cdev;
+ data->cdev.sensors_enable = bmp18x_enable_set;
+ data->cdev.sensors_poll_delay = bmp18x_poll_delay_set;
+ err = sensors_classdev_register(&data->input->dev, &data->cdev);
if (err) {
pr_err("class device create failed: %d\n", err);
goto error_class_sysfs;
diff --git a/drivers/input/misc/cm36283.c b/drivers/input/misc/cm36283.c
index c8a276b9d19c..76162ac6f5a5 100644
--- a/drivers/input/misc/cm36283.c
+++ b/drivers/input/misc/cm36283.c
@@ -639,12 +639,6 @@ static const struct file_operations psensor_fops = {
.unlocked_ioctl = psensor_ioctl
};
-struct miscdevice psensor_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "proximity",
- .fops = &psensor_fops
-};
-
void lightsensor_set_kvalue(struct cm36283_info *lpi)
{
if (!lpi) {
@@ -792,13 +786,6 @@ static const struct file_operations lightsensor_fops = {
.unlocked_ioctl = lightsensor_ioctl
};
-static struct miscdevice lightsensor_misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = "lightsensor",
- .fops = &lightsensor_fops
-};
-
-
static ssize_t ps_adc_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -843,7 +830,6 @@ static ssize_t ps_enable_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_adc, 0664, ps_adc_show, ps_enable_store);
static ssize_t ps_parameters_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -887,10 +873,6 @@ static ssize_t ps_parameters_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_parameters, 0664,
- ps_parameters_show, ps_parameters_store);
-
-
static ssize_t ps_conf_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -916,7 +898,6 @@ static ssize_t ps_conf_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_conf, 0664, ps_conf_show, ps_conf_store);
static ssize_t ps_thd_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -945,7 +926,6 @@ static ssize_t ps_thd_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_thd, 0664, ps_thd_show, ps_thd_store);
static ssize_t ps_hw_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -968,7 +948,6 @@ static ssize_t ps_hw_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_hw, 0664, ps_hw_show, ps_hw_store);
static ssize_t ls_adc_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -982,8 +961,6 @@ static ssize_t ls_adc_show(struct device *dev,
return ret;
}
-static DEVICE_ATTR(ls_adc, 0664, ls_adc_show, NULL);
-
static ssize_t ls_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1032,8 +1009,6 @@ static ssize_t ls_enable_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ls_auto, 0664,
- ls_enable_show, ls_enable_store);
static ssize_t ls_kadc_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1076,7 +1051,6 @@ static ssize_t ls_kadc_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ls_kadc, 0664, ls_kadc_show, ls_kadc_store);
static ssize_t ls_gadc_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1116,7 +1090,6 @@ static ssize_t ls_gadc_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ls_gadc, 0664, ls_gadc_show, ls_gadc_store);
static ssize_t ls_adc_table_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1167,9 +1140,6 @@ static ssize_t ls_adc_table_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ls_adc_table, 0664,
- ls_adc_table_show, ls_adc_table_store);
-
static ssize_t ls_conf_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1191,7 +1161,6 @@ static ssize_t ls_conf_store(struct device *dev,
_cm36283_I2C_Write_Word(lpi->slave_addr, ALS_CONF, lpi->ls_cmd);
return count;
}
-static DEVICE_ATTR(ls_conf, 0664, ls_conf_show, ls_conf_store);
static ssize_t ls_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -1218,9 +1187,6 @@ static ssize_t ls_poll_delay_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ls_poll_delay, 0664, ls_poll_delay_show,
- ls_poll_delay_store);
-
static ssize_t ps_poll_delay_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1246,9 +1212,6 @@ static ssize_t ps_poll_delay_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(ps_poll_delay, 0664, ps_poll_delay_show,
- ps_poll_delay_store);
-
static ssize_t ls_fLevel_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1270,7 +1233,6 @@ static ssize_t ls_fLevel_store(struct device *dev,
fLevel=-1;
return count;
}
-static DEVICE_ATTR(ls_flevel, 0664, ls_fLevel_show, ls_fLevel_store);
static int lightsensor_setup(struct cm36283_info *lpi)
{
@@ -1285,6 +1247,7 @@ static int lightsensor_setup(struct cm36283_info *lpi)
return -ENOMEM;
}
lpi->ls_input_dev->name = "cm36283-ls";
+ lpi->ls_input_dev->id.bustype = BUS_I2C;
set_bit(EV_ABS, lpi->ls_input_dev->evbit);
range = get_als_range();
@@ -1297,17 +1260,8 @@ static int lightsensor_setup(struct cm36283_info *lpi)
goto err_free_ls_input_device;
}
- ret = misc_register(&lightsensor_misc);
- if (ret < 0) {
- pr_err("[LS][CM36283 error]%s: can not register ls misc device\n",
- __func__);
- goto err_unregister_ls_input_device;
- }
-
return ret;
-err_unregister_ls_input_device:
- input_unregister_device(lpi->ls_input_dev);
err_free_ls_input_device:
input_free_device(lpi->ls_input_dev);
return ret;
@@ -1325,6 +1279,7 @@ static int psensor_setup(struct cm36283_info *lpi)
return -ENOMEM;
}
lpi->ps_input_dev->name = "cm36283-ps";
+ lpi->ps_input_dev->id.bustype = BUS_I2C;
set_bit(EV_ABS, lpi->ps_input_dev->evbit);
input_set_abs_params(lpi->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
@@ -1336,18 +1291,8 @@ static int psensor_setup(struct cm36283_info *lpi)
goto err_free_ps_input_device;
}
- ret = misc_register(&psensor_misc);
- if (ret < 0) {
- pr_err(
- "[PS][CM36283 error]%s: could not register ps misc device\n",
- __func__);
- goto err_unregister_ps_input_device;
- }
-
return ret;
-err_unregister_ps_input_device:
- input_unregister_device(lpi->ps_input_dev);
err_free_ps_input_device:
input_free_device(lpi->ps_input_dev);
return ret;
@@ -1495,6 +1440,59 @@ static int cm36283_parse_dt(struct device *dev,
return 0;
}
+static int create_sysfs_interfaces(struct device *dev,
+ struct device_attribute *attributes, int len)
+{
+ int i;
+ int err;
+ for (i = 0; i < len; i++) {
+ err = device_create_file(dev, attributes + i);
+ if (err)
+ goto error;
+ }
+ return 0;
+
+error:
+ for (; i >= 0; i--)
+ device_remove_file(dev, attributes + i);
+ dev_err(dev, "%s:Unable to create interface\n", __func__);
+ return err;
+}
+
+static int remove_sysfs_interfaces(struct device *dev,
+ struct device_attribute *attributes, int len)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ device_remove_file(dev, attributes + i);
+ return 0;
+}
+
+static struct device_attribute light_attr[] = {
+ __ATTR(ls_adc, 0664, ls_adc_show, NULL),
+ __ATTR(ls_kadc, 0664, ls_kadc_show, ls_kadc_store),
+ __ATTR(ls_gadc, 0664, ls_gadc_show, ls_gadc_store),
+ __ATTR(ls_conf, 0664, ls_conf_show, ls_conf_store),
+ __ATTR(ls_adc_table, 0664,
+ ls_adc_table_show, ls_adc_table_store),
+ __ATTR(poll_delay, 0664, ls_poll_delay_show,
+ ls_poll_delay_store),
+ __ATTR(enable, 0664,
+ ls_enable_show, ls_enable_store),
+};
+
+static struct device_attribute proximity_attr[] = {
+ __ATTR(enable, 0664, ps_adc_show, ps_enable_store),
+ __ATTR(ps_parameters, 0664,
+ ps_parameters_show, ps_parameters_store),
+ __ATTR(ps_conf, 0664, ps_conf_show, ps_conf_store),
+ __ATTR(ps_hw, 0664, ps_hw_show, ps_hw_store),
+ __ATTR(ps_thd, 0664, ps_thd_show, ps_thd_store),
+ __ATTR(poll_delay, 0664, ps_poll_delay_show,
+ ps_poll_delay_store),
+ __ATTR(ls_flevel, 0664, ls_fLevel_show, ls_fLevel_store),
+};
+
static int cm36283_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1632,95 +1630,24 @@ static int cm36283_probe(struct i2c_client *client,
goto err_psensor_setup;
}
- lpi->cm36283_class = class_create(THIS_MODULE, "optical_sensors");
- if (IS_ERR(lpi->cm36283_class)) {
- ret = PTR_ERR(lpi->cm36283_class);
- lpi->cm36283_class = NULL;
- goto err_create_class;
- }
-
- lpi->ls_dev = device_create(lpi->cm36283_class,
- NULL, 0, "%s", "lightsensor");
- if (unlikely(IS_ERR(lpi->ls_dev))) {
- ret = PTR_ERR(lpi->ls_dev);
- lpi->ls_dev = NULL;
- goto err_create_ls_device;
+ ret = create_sysfs_interfaces(&lpi->ls_input_dev->dev, light_attr,
+ ARRAY_SIZE(light_attr));
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to create sysfs\n");
+ goto err_input_cleanup;
}
- /* register the attributes */
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc);
- if (ret)
- goto err_create_ls_device_file;
-
- /* register the attributes */
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_auto);
- if (ret)
- goto err_create_ls_device_file;
-
- /* register the attributes */
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_kadc);
- if (ret)
- goto err_create_ls_device_file;
-
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_gadc);
- if (ret)
- goto err_create_ls_device_file;
-
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_adc_table);
- if (ret)
- goto err_create_ls_device_file;
-
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_conf);
- if (ret)
- goto err_create_ls_device_file;
-
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_flevel);
- if (ret)
- goto err_create_ls_device_file;
-
- ret = device_create_file(lpi->ls_dev, &dev_attr_ls_poll_delay);
- if (ret)
- goto err_create_ls_device_file;
-
- lpi->ps_dev = device_create(lpi->cm36283_class,
- NULL, 0, "%s", "proximity");
- if (unlikely(IS_ERR(lpi->ps_dev))) {
- ret = PTR_ERR(lpi->ps_dev);
- lpi->ps_dev = NULL;
- goto err_create_ps_device;
+ ret = create_sysfs_interfaces(&lpi->ps_input_dev->dev, proximity_attr,
+ ARRAY_SIZE(proximity_attr));
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to create sysfs\n");
+ goto err_light_sysfs_cleanup;
}
- /* register the attributes */
- ret = device_create_file(lpi->ps_dev, &dev_attr_ps_adc);
- if (ret)
- goto err_create_ps_device_file;
-
- ret = device_create_file(lpi->ps_dev,
- &dev_attr_ps_parameters);
- if (ret)
- goto err_create_ps_device_file;
-
- /* register the attributes */
- ret = device_create_file(lpi->ps_dev, &dev_attr_ps_conf);
- if (ret)
- goto err_create_ps_device_file;
-
- /* register the attributes */
- ret = device_create_file(lpi->ps_dev, &dev_attr_ps_thd);
- if (ret)
- goto err_create_ps_device_file;
-
- ret = device_create_file(lpi->ps_dev, &dev_attr_ps_hw);
- if (ret)
- goto err_create_ps_device_file;
-
- ret = device_create_file(lpi->ps_dev, &dev_attr_ps_poll_delay);
- if (ret)
- goto err_create_ps_device_file;
ret = sensors_classdev_register(&client->dev, &sensors_light_cdev);
if (ret)
- goto err_create_ps_device_file;
+ goto err_proximity_sysfs_cleanup;
ret = sensors_classdev_register(&client->dev, &sensors_proximity_cdev);
if (ret)
@@ -1734,19 +1661,16 @@ static int cm36283_probe(struct i2c_client *client,
return ret;
err_create_class_sysfs:
sensors_classdev_unregister(&sensors_light_cdev);
-err_create_ps_device_file:
- device_unregister(lpi->ps_dev);
-err_create_ps_device:
-err_create_ls_device_file:
- device_unregister(lpi->ls_dev);
-err_create_ls_device:
- class_destroy(lpi->cm36283_class);
-err_create_class:
- misc_deregister(&psensor_misc);
+err_proximity_sysfs_cleanup:
+ remove_sysfs_interfaces(&lpi->ps_input_dev->dev, proximity_attr,
+ ARRAY_SIZE(proximity_attr));
+err_light_sysfs_cleanup:
+ remove_sysfs_interfaces(&lpi->ls_input_dev->dev, light_attr,
+ ARRAY_SIZE(light_attr));
+err_input_cleanup:
input_unregister_device(lpi->ps_input_dev);
input_free_device(lpi->ps_input_dev);
err_psensor_setup:
- misc_deregister(&lightsensor_misc);
input_unregister_device(lpi->ls_input_dev);
input_free_device(lpi->ls_input_dev);
err_lightsensor_setup:
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index 8120e6306dfa..225805401a01 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -87,9 +87,13 @@ static struct sensors_classdev sensors_cdev = {
.max_range = "19.6",
.resolution = "0.01",
.sensor_power = "0.2",
- .min_delay = 2000,
+ .min_delay = 2000, /* microsecond */
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 200, /* millisecond */
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
};
static const struct {
@@ -121,6 +125,7 @@ struct kxtj9_data {
bool power_enabled;
struct regulator *vdd;
struct regulator *vio;
+ struct sensors_classdev cdev;
};
static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
@@ -489,6 +494,36 @@ static int kxtj9_setup_input_device(struct kxtj9_data *tj9)
return 0;
}
+static int kxtj9_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enabled)
+{
+ struct kxtj9_data *tj9 = container_of(sensors_cdev,
+ struct kxtj9_data, cdev);
+ struct input_dev *input_dev = tj9->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (enabled == 0) {
+ disable_irq(tj9->client->irq);
+ kxtj9_disable(tj9);
+ tj9->enable = false;
+ } else if (enabled == 1) {
+ if (!kxtj9_enable(tj9)) {
+ enable_irq(tj9->client->irq);
+ tj9->enable = true;
+ }
+ } else {
+ dev_err(&tj9->client->dev,
+ "Invalid value of input, input=%d\n", enabled);
+ mutex_unlock(&input_dev->mutex);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
static ssize_t kxtj9_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -504,31 +539,16 @@ static ssize_t kxtj9_enable_store(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
- struct input_dev *input_dev = tj9->input_dev;
unsigned long data;
int error;
error = kstrtoul(buf, 10, &data);
- if (error)
+ if (error < 0)
return error;
- mutex_lock(&input_dev->mutex);
-
- if (data == 0) {
- disable_irq(client->irq);
- kxtj9_disable(tj9);
- tj9->enable = false;
- } else if (data == 1) {
- if (!kxtj9_enable(tj9)) {
- enable_irq(client->irq);
- tj9->enable = true;
- }
- } else {
- dev_err(&tj9->client->dev,
- "Invalid value of input, input=%ld\n", data);
- }
-
- mutex_unlock(&input_dev->mutex);
+ error = kxtj9_enable_set(&tj9->cdev, data);
+ if (error < 0)
+ return error;
return count;
}
@@ -545,6 +565,29 @@ static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP,
* will be responsible for retrieving data from the input node at the desired
* interval.
*/
+static int kxtj9_poll_delay_set(struct sensors_classdev *sensors_cdev,
+ unsigned int delay_msec)
+{
+ struct kxtj9_data *tj9 = container_of(sensors_cdev,
+ struct kxtj9_data, cdev);
+ struct input_dev *input_dev = tj9->input_dev;
+
+ /* Lock the device to prevent races with open/close (and itself) */
+ mutex_lock(&input_dev->mutex);
+
+ if (tj9->enable)
+ disable_irq(tj9->client->irq);
+
+ tj9->last_poll_interval = max(delay_msec, tj9->pdata.min_interval);
+
+ if (tj9->enable) {
+ kxtj9_update_odr(tj9, tj9->last_poll_interval);
+ enable_irq(tj9->client->irq);
+ }
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
/* Returns currently selected poll interval (in ms) */
static ssize_t kxtj9_get_poll_delay(struct device *dev,
@@ -563,7 +606,6 @@ static ssize_t kxtj9_set_poll_delay(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct kxtj9_data *tj9 = i2c_get_clientdata(client);
- struct input_dev *input_dev = tj9->input_dev;
unsigned int interval;
int error;
@@ -571,24 +613,9 @@ static ssize_t kxtj9_set_poll_delay(struct device *dev,
if (error < 0)
return error;
- /* Lock the device to prevent races with open/close (and itself) */
- mutex_lock(&input_dev->mutex);
-
- if (tj9->enable)
- disable_irq(client->irq);
-
- /*
- * Set current interval to the greater of the minimum interval or
- * the requested interval
- */
- tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
-
- if (tj9->enable) {
- kxtj9_update_odr(tj9, tj9->last_poll_interval);
- enable_irq(client->irq);
- }
- mutex_unlock(&input_dev->mutex);
-
+ error = kxtj9_poll_delay_set(&tj9->cdev, interval);
+ if (error < 0)
+ return error;
return count;
}
@@ -605,7 +632,6 @@ static struct attribute_group kxtj9_attribute_group = {
.attrs = kxtj9_attributes
};
-
#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
static void kxtj9_poll(struct input_polled_dev *dev)
{
@@ -861,6 +887,18 @@ static int kxtj9_probe(struct i2c_client *client,
tj9->ctrl_reg1 = tj9->pdata.res_ctl | tj9->pdata.g_range;
tj9->last_poll_interval = tj9->pdata.init_interval;
+ tj9->cdev = sensors_cdev;
+ /* The min_delay is used by userspace and the unit is microsecond. */
+ tj9->cdev.min_delay = tj9->pdata.min_interval * 1000;
+ tj9->cdev.delay_msec = tj9->pdata.init_interval;
+ tj9->cdev.sensors_enable = kxtj9_enable_set;
+ tj9->cdev.sensors_poll_delay = kxtj9_poll_delay_set;
+ err = sensors_classdev_register(&client->dev, &tj9->cdev);
+ if (err) {
+ dev_err(&client->dev, "class device create failed: %d\n", err);
+ goto err_power_off;
+ }
+
if (client->irq) {
/* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
@@ -892,11 +930,6 @@ static int kxtj9_probe(struct i2c_client *client,
goto err_power_off;
}
- err = sensors_classdev_register(&client->dev, &sensors_cdev);
- if (err) {
- dev_err(&client->dev, "class device create failed: %d\n", err);
- goto err_free_irq;
- }
dev_dbg(&client->dev, "%s: kxtj9_probe OK.\n", __func__);
kxtj9_device_power_off(tj9);
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index de9ddc86d0f6..2a14a28d98a2 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -122,6 +122,7 @@ struct mpu3050_sensor {
struct input_dev *idev;
struct mpu3050_gyro_platform_data *platform_data;
struct delayed_work input_work;
+ struct sensors_classdev cdev;
u32 use_poll;
u32 poll_interval;
u32 dlpf_index;
@@ -141,6 +142,10 @@ static struct sensors_classdev sensors_cdev = {
.min_delay = 2000,
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = MPU3050_DEFAULT_POLL_INTERVAL,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
};
struct sensor_regulator {
@@ -258,6 +263,40 @@ error_vdd:
return rc;
}
+static int mpu3050_poll_delay_set(struct sensors_classdev *sensors_cdev,
+ unsigned int delay_msec)
+{
+ struct mpu3050_sensor *sensor = container_of(sensors_cdev,
+ struct mpu3050_sensor, cdev);
+ unsigned int dlpf_index;
+ u8 divider, reg;
+ int ret;
+
+ dlpf_index = interval_to_dlpf_cfg(delay_msec);
+ divider = delay_msec * dlpf_table[dlpf_index].sample_rate - 1;
+
+ if (sensor->dlpf_index != dlpf_index) {
+ /* Set low pass filter and full scale */
+ reg = dlpf_table[dlpf_index].cfg;
+ reg |= MPU3050_DEFAULT_FS_RANGE << 3;
+ reg |= MPU3050_EXT_SYNC_NONE << 5;
+ ret = i2c_smbus_write_byte_data(sensor->client,
+ MPU3050_DLPF_FS_SYNC, reg);
+ if (!ret)
+ sensor->dlpf_index = dlpf_index;
+ }
+
+ if (sensor->poll_interval != delay_msec) {
+ /* Output frequency divider. The poll interval */
+ ret = i2c_smbus_write_byte_data(sensor->client,
+ MPU3050_SMPLRT_DIV, divider);
+ if (!ret)
+ sensor->poll_interval = delay_msec;
+ }
+
+ return 0;
+}
+
/**
* mpu3050_attr_get_polling_rate - get the sampling rate
*/
@@ -280,8 +319,6 @@ static ssize_t mpu3050_attr_set_polling_rate(struct device *dev,
{
struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
unsigned long interval_ms;
- unsigned int dlpf_index;
- u8 divider, reg;
int ret;
if (kstrtoul(buf, 10, &interval_ms))
@@ -290,29 +327,39 @@ static ssize_t mpu3050_attr_set_polling_rate(struct device *dev,
(interval_ms > MPU3050_MAX_POLL_INTERVAL))
return -EINVAL;
- dlpf_index = interval_to_dlpf_cfg(interval_ms);
- divider = interval_ms * dlpf_table[dlpf_index].sample_rate - 1;
+ ret = mpu3050_poll_delay_set(&sensor->cdev, interval_ms);
- if (sensor->dlpf_index != dlpf_index) {
- /* Set low pass filter and full scale */
- reg = dlpf_table[dlpf_index].cfg;
- reg |= MPU3050_DEFAULT_FS_RANGE << 3;
- reg |= MPU3050_EXT_SYNC_NONE << 5;
- ret = i2c_smbus_write_byte_data(sensor->client,
- MPU3050_DLPF_FS_SYNC, reg);
- if (ret == 0)
- sensor->dlpf_index = dlpf_index;
- }
+ return ret < 0 ? ret : size;
+}
+static int mpu3050_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enabled)
+{
+ struct mpu3050_sensor *sensor = container_of(sensors_cdev,
+ struct mpu3050_sensor, cdev);
- if (sensor->poll_interval != interval_ms) {
- /* Output frequency divider. The poll interval */
- ret = i2c_smbus_write_byte_data(sensor->client,
- MPU3050_SMPLRT_DIV, divider);
- if (ret == 0)
- sensor->poll_interval = interval_ms;
+
+ if (enabled && (!sensor->enable)) {
+ sensor->enable = enabled;
+ pm_runtime_get_sync(sensor->dev);
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(sensor->poll_interval));
+ else
+ enable_irq(sensor->client->irq);
+ } else if (!enabled && sensor->enable) {
+ if (sensor->use_poll)
+ cancel_delayed_work_sync(&sensor->input_work);
+ else
+ disable_irq(sensor->client->irq);
+ pm_runtime_put_sync(sensor->dev);
+ sensor->enable = enabled;
+ } else {
+ dev_warn(&sensor->client->dev,
+ "ignore enable state change from %d to %d\n",
+ sensor->enable, enabled);
}
- return size;
+ return 0;
}
/**
@@ -325,32 +372,14 @@ static ssize_t mpu3050_attr_set_enable(struct device *dev,
{
struct mpu3050_sensor *sensor = dev_get_drvdata(dev);
unsigned long val;
+ int err;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
- sensor->enable = (u32)val == 0 ? 0 : 1;
- if (sensor->enable) {
- pm_runtime_get_sync(sensor->dev);
- gpio_set_value(sensor->enable_gpio, 1);
- if (sensor->use_poll)
- schedule_delayed_work(&sensor->input_work,
- msecs_to_jiffies(sensor->poll_interval));
- else {
- i2c_smbus_write_byte_data(sensor->client,
- MPU3050_INT_CFG,
- MPU3050_ACTIVE_LOW |
- MPU3050_OPEN_DRAIN |
- MPU3050_RAW_RDY_EN);
- enable_irq(sensor->client->irq);
- }
- } else {
- if (sensor->use_poll)
- cancel_delayed_work_sync(&sensor->input_work);
- else
- disable_irq(sensor->client->irq);
- gpio_set_value(sensor->enable_gpio, 0);
- pm_runtime_put(sensor->dev);
- }
+ err = mpu3050_enable_set(&sensor->cdev, val);
+ if (err < 0)
+ return err;
+
return count;
}
@@ -546,12 +575,6 @@ static int mpu3050_hw_init(struct mpu3050_sensor *sensor)
int ret;
u8 reg;
- /* Reset */
- ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,
- MPU3050_PWR_MGM_RESET);
- if (ret < 0)
- return ret;
-
ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
if (ret < 0)
return ret;
@@ -576,6 +599,16 @@ static int mpu3050_hw_init(struct mpu3050_sensor *sensor)
if (ret < 0)
return ret;
+ /* Enable interrupts */
+ if (!sensor->use_poll) {
+ reg = MPU3050_ACTIVE_LOW;
+ reg |= MPU3050_OPEN_DRAIN;
+ reg |= MPU3050_RAW_RDY_EN;
+ ret = i2c_smbus_write_byte_data(client, MPU3050_INT_CFG, reg);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
#ifdef CONFIG_OF
@@ -675,6 +708,19 @@ static int mpu3050_probe(struct i2c_client *client,
sensor->enable_gpio = -EINVAL;
}
+ sensor->cdev = sensors_cdev;
+ sensor->cdev.min_delay = MPU3050_MIN_POLL_INTERVAL;
+ sensor->cdev.delay_msec = sensor->poll_interval;
+ sensor->cdev.sensors_enable = mpu3050_enable_set;
+ sensor->cdev.sensors_poll_delay = mpu3050_poll_delay_set;
+ ret = sensors_classdev_register(&client->dev, &sensor->cdev);
+
+ if (ret) {
+ dev_err(&client->dev, "class device create failed: %d\n", ret);
+ error = -EINVAL;
+ goto err_free_mem;
+ }
+
if (gpio_is_valid(sensor->enable_gpio)) {
ret = gpio_request(sensor->enable_gpio, "GYRO_EN_PM");
gpio_direction_output(sensor->enable_gpio, 1);
@@ -686,7 +732,7 @@ static int mpu3050_probe(struct i2c_client *client,
if (ret < 0) {
dev_err(&client->dev, "failed to detect device\n");
error = -ENXIO;
- goto err_free_mem;
+ goto err_class_sysfs;
}
for (i = 0; i < ARRAY_SIZE(mpu3050_chip_ids); i++)
@@ -696,7 +742,7 @@ static int mpu3050_probe(struct i2c_client *client,
if (i == ARRAY_SIZE(mpu3050_chip_ids)) {
dev_err(&client->dev, "unsupported chip id\n");
error = -ENXIO;
- goto err_free_mem;
+ goto err_class_sysfs;
}
idev->name = "MPU3050";
@@ -762,22 +808,19 @@ static int mpu3050_probe(struct i2c_client *client,
disable_irq(client->irq);
}
+ sensor->enable = 0;
+ mpu3050_set_power_mode(client, 0);
+
error = input_register_device(idev);
if (error) {
dev_err(&client->dev, "failed to register input device\n");
goto err_free_irq;
}
- error = sensors_classdev_register(&client->dev, &sensors_cdev);
- if (error < 0) {
- dev_err(&client->dev, "failed to create class device\n");
- goto err_input_cleanup;
- }
-
error = create_sysfs_interfaces(&idev->dev);
if (error < 0) {
dev_err(&client->dev, "failed to create sysfs\n");
- goto err_class_sysfs;
+ goto err_input_cleanup;
}
pm_runtime_enable(&client->dev);
@@ -785,8 +828,6 @@ static int mpu3050_probe(struct i2c_client *client,
return 0;
-err_class_sysfs:
- sensors_classdev_unregister(&sensors_cdev);
err_input_cleanup:
input_unregister_device(idev);
err_free_irq:
@@ -798,6 +839,8 @@ err_free_gpio:
gpio_free(sensor->platform_data->gpio_int);
err_pm_set_suspended:
pm_runtime_set_suspended(&client->dev);
+err_class_sysfs:
+ sensors_classdev_unregister(&sensor->cdev);
err_free_mem:
input_free_device(idev);
kfree(sensor);
@@ -842,10 +885,11 @@ static int mpu3050_suspend(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
- if (!sensor->use_poll)
- disable_irq(client->irq);
-
- mpu3050_set_power_mode(client, 0);
+ if (sensor->enable) {
+ if (!sensor->use_poll)
+ disable_irq(client->irq);
+ mpu3050_set_power_mode(client, 0);
+ }
return 0;
}
@@ -861,16 +905,64 @@ static int mpu3050_resume(struct device *dev)
struct i2c_client *client = to_i2c_client(dev);
struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
- mpu3050_set_power_mode(client, 1);
+ if (sensor->enable) {
+ mpu3050_set_power_mode(client, 1);
+ mpu3050_hw_init(sensor);
+ if (!sensor->use_poll)
+ enable_irq(client->irq);
+ }
+
+ return 0;
+}
+
+/**
+ * mpu3050_runtime_suspend - called on device enters runtime suspend
+ * @dev: device being suspended
+ *
+ * Put the device into sleep mode.
+ */
+static int mpu3050_runtime_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
- if (!sensor->use_poll)
- enable_irq(client->irq);
+ if (sensor->enable)
+ mpu3050_set_power_mode(client, 0);
+
+ return 0;
+}
+
+/**
+ * mpu3050_runtime_resume - called on device enters runtime resume
+ * @dev: device being resumed
+ *
+ * Put the device into powered mode.
+ */
+static int mpu3050_runtime_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+
+ if (sensor->enable) {
+ mpu3050_set_power_mode(client, 1);
+ mpu3050_hw_init(sensor);
+ }
return 0;
}
#endif
-static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);
+static const struct dev_pm_ops mpu3050_pm = {
+ .runtime_suspend = mpu3050_runtime_suspend,
+ .runtime_resume = mpu3050_runtime_resume,
+ .runtime_idle = NULL,
+ .suspend = mpu3050_suspend,
+ .resume = mpu3050_resume,
+ .freeze = mpu3050_suspend,
+ .thaw = mpu3050_resume,
+ .poweroff = mpu3050_suspend,
+ .restore = mpu3050_resume,
+};
static const struct i2c_device_id mpu3050_ids[] = {
{ "mpu3050", 0 },
diff --git a/drivers/input/misc/stk3x1x.c b/drivers/input/misc/stk3x1x.c
index 8fde72d773ae..8750858a39a2 100644
--- a/drivers/input/misc/stk3x1x.c
+++ b/drivers/input/misc/stk3x1x.c
@@ -64,8 +64,6 @@
#define STK_POLL_PS
#define STK_POLL_ALS /* ALS interrupt is valid only when STK_PS_INT_MODE = 1 or 4*/
-#define STK_DEBUG_PRINTF
-
/* Define Register Map */
#define STK_STATE_REG 0x00
#define STK_PSCTRL_REG 0x01
@@ -192,9 +190,13 @@ static struct sensors_classdev sensors_light_cdev = {
.max_range = "6500",
.resolution = "0.0625",
.sensor_power = "0.09",
- .min_delay = 0,
+ .min_delay = (MIN_ALS_POLL_DELAY_NS / 1000), /* us */
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 200,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
};
static struct sensors_classdev sensors_proximity_cdev = {
@@ -209,6 +211,10 @@ static struct sensors_classdev sensors_proximity_cdev = {
.min_delay = 0,
.fifo_reserved_event_count = 0,
.fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 200,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
};
struct data_filter {
@@ -221,6 +227,8 @@ struct data_filter {
struct stk3x1x_data {
struct i2c_client *client;
struct stk3x1x_platform_data *pdata;
+ struct sensors_classdev als_cdev;
+ struct sensors_classdev ps_cdev;
#if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS))
int32_t irq;
struct work_struct stk_work;
@@ -238,7 +246,7 @@ struct stk3x1x_data {
int32_t ps_distance_last;
bool ps_enabled;
struct wake_lock ps_wakelock;
- struct work_struct stk_ps_work;
+ struct work_struct stk_ps_work;
struct workqueue_struct *stk_ps_wq;
#ifdef STK_POLL_PS
struct wake_lock ps_nosuspend_wl;
@@ -321,11 +329,11 @@ static void stk_init_code_threshold_table(struct stk3x1x_data *ps_data)
for (i=1,j=0;i<LUX_THD_TABLE_SIZE;i++,j++)
{
alscode = stk_lux2alscode(ps_data, lux_threshold_table[j]);
- printk(KERN_INFO "alscode[%d]=%d\n",i,alscode);
+ dev_dbg(&ps_data->client->dev, "alscode[%d]=%d\n", i, alscode);
code_threshold_table[i] = (uint16_t)(alscode);
}
code_threshold_table[i] = 0xffff;
- printk(KERN_INFO "alscode[%d]=%d\n",i,alscode);
+ dev_dbg(&ps_data->client->dev, "alscode[%d]=%d\n", i, alscode);
}
static uint32_t stk_get_lux_interval_index(uint16_t alscode)
@@ -458,7 +466,6 @@ static int32_t stk3x1x_check_pid(struct stk3x1x_data *ps_data)
printk(KERN_ERR "%s: read i2c error, err=%d\n", __func__, err2);
return -1;
}
- printk(KERN_INFO "%s: PID=0x%x, RID=0x%x\n", __func__, err1, err2);
if(err2 == 0xC0)
printk(KERN_INFO "%s: RID=0xC0!!!!!!!!!!!!!\n", __func__);
@@ -685,7 +692,9 @@ static int32_t stk3x1x_enable_ps(struct stk3x1x_data *ps_data, uint8_t enable)
input_sync(ps_data->ps_input_dev);
wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
reading = stk3x1x_get_ps_reading(ps_data);
- printk(KERN_INFO "%s: ps input event=%d, ps code = %d\n",__func__, near_far_state, reading);
+ dev_dbg(&ps_data->client->dev,
+ "%s: ps input event=%d, ps code = %d\n",
+ __func__, near_far_state, reading);
#endif /* #ifndef STK_POLL_PS */
}
else
@@ -904,6 +913,21 @@ static ssize_t stk_als_code_show(struct device *dev, struct device_attribute *at
return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
}
+static ssize_t stk_als_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enabled)
+{
+ struct stk3x1x_data *als_data = container_of(sensors_cdev,
+ struct stk3x1x_data, als_cdev);
+ int err;
+
+ mutex_lock(&als_data->io_lock);
+ err = stk3x1x_enable_als(als_data, enabled);
+ mutex_unlock(&als_data->io_lock);
+
+ if (err < 0)
+ return err;
+ return 0;
+}
static ssize_t stk_als_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -935,7 +959,7 @@ static ssize_t stk_als_enable_store(struct device *dev, struct device_attribute
printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
return -EINVAL;
}
- printk(KERN_INFO "%s: Enable ALS : %d\n", __func__, en);
+ dev_dbg(dev, "%s: Enable ALS : %d\n", __func__, en);
mutex_lock(&ps_data->io_lock);
stk3x1x_enable_als(ps_data, en);
mutex_unlock(&ps_data->io_lock);
@@ -971,7 +995,7 @@ static ssize_t stk_als_lux_store(struct device *dev, struct device_attribute *at
input_report_abs(ps_data->als_input_dev, ABS_MISC, value);
input_sync(ps_data->als_input_dev);
mutex_unlock(&ps_data->io_lock);
- printk(KERN_INFO "%s: als input event %ld lux\n",__func__, value);
+ dev_dbg(dev, "%s: als input event %ld lux\n", __func__, value);
return size;
}
@@ -1009,7 +1033,8 @@ static ssize_t stk_als_transmittance_store(struct device *dev, struct device_att
static ssize_t stk_als_delay_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
- return scnprintf(buf, PAGE_SIZE, "%lld\n", ktime_to_ns(ps_data->als_poll_delay));
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ (u32)ktime_to_ms(ps_data->als_poll_delay));
}
static inline void stk_als_delay_store_fir(struct stk3x1x_data *ps_data)
@@ -1018,34 +1043,48 @@ static inline void stk_als_delay_store_fir(struct stk3x1x_data *ps_data)
ps_data->fir.idx = 0;
ps_data->fir.sum = 0;
}
+
+static ssize_t stk_als_poll_delay_set(struct sensors_classdev *sensors_cdev,
+ unsigned int delay_msec)
+{
+ struct stk3x1x_data *als_data = container_of(sensors_cdev,
+ struct stk3x1x_data, als_cdev);
+ uint64_t value = 0;
+
+ value = delay_msec * 1000000;
+
+ if (value < MIN_ALS_POLL_DELAY_NS)
+ value = MIN_ALS_POLL_DELAY_NS;
+
+ mutex_lock(&als_data->io_lock);
+ if (value != ktime_to_ns(als_data->als_poll_delay))
+ als_data->als_poll_delay = ns_to_ktime(value);
+
+ if (als_data->use_fir)
+ stk_als_delay_store_fir(als_data);
+
+ mutex_unlock(&als_data->io_lock);
+
+ return 0;
+}
+
static ssize_t stk_als_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
{
uint64_t value = 0;
int ret;
- struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ struct stk3x1x_data *als_data = dev_get_drvdata(dev);
ret = kstrtoull(buf, 10, &value);
if(ret < 0)
{
- printk(KERN_ERR "%s:kstrtoull failed, ret=0x%x\n",
- __func__, ret);
+ dev_err(dev, "%s:kstrtoull failed, ret=0x%x\n", __func__, ret);
return ret;
}
#ifdef STK_DEBUG_PRINTF
- printk(KERN_INFO "%s: set als poll delay=%lld\n", __func__, value);
+ dev_dbg(dev, "%s: set als poll delay=%lld\n", __func__, value);
#endif
- if(value < MIN_ALS_POLL_DELAY_NS)
- {
- printk(KERN_ERR "%s: delay is too small\n", __func__);
- value = MIN_ALS_POLL_DELAY_NS;
- }
- mutex_lock(&ps_data->io_lock);
- if(value != ktime_to_ns(ps_data->als_poll_delay))
- ps_data->als_poll_delay = ns_to_ktime(value);
-
- if (ps_data->use_fir)
- stk_als_delay_store_fir(ps_data);
-
- mutex_unlock(&ps_data->io_lock);
+ ret = stk_als_poll_delay_set(&als_data->als_cdev, value);
+ if (ret < 0)
+ return ret;
return size;
}
@@ -1136,6 +1175,22 @@ static ssize_t stk_ps_code_show(struct device *dev, struct device_attribute *att
return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
}
+static ssize_t stk_ps_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enabled)
+{
+ struct stk3x1x_data *ps_data = container_of(sensors_cdev,
+ struct stk3x1x_data, ps_cdev);
+ int err;
+
+ mutex_lock(&ps_data->io_lock);
+ err = stk3x1x_enable_ps(ps_data, enabled);
+ mutex_unlock(&ps_data->io_lock);
+
+ if (err < 0)
+ return err;
+ return 0;
+}
+
static ssize_t stk_ps_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int32_t enable, ret;
@@ -1166,7 +1221,7 @@ static ssize_t stk_ps_enable_store(struct device *dev, struct device_attribute *
printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
return -EINVAL;
}
- printk(KERN_INFO "%s: Enable PS : %d\n", __func__, en);
+ dev_dbg(dev, "%s: Enable PS : %d\n", __func__, en);
mutex_lock(&ps_data->io_lock);
stk3x1x_enable_ps(ps_data, en);
mutex_unlock(&ps_data->io_lock);
@@ -1200,7 +1255,7 @@ static ssize_t stk_ps_enable_aso_store(struct device *dev, struct device_attribu
printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
return -EINVAL;
}
- printk(KERN_INFO "%s: Enable PS ASO : %d\n", __func__, en);
+ dev_dbg(dev, "%s: Enable PS ASO : %d\n", __func__, en);
ret = i2c_smbus_read_byte_data(ps_data->client, STK_STATE_REG);
if (ret < 0)
@@ -1288,7 +1343,7 @@ static ssize_t stk_ps_distance_show(struct device *dev, struct device_attribute
input_sync(ps_data->ps_input_dev);
mutex_unlock(&ps_data->io_lock);
wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
- printk(KERN_INFO "%s: ps input event %d cm\n",__func__, dist);
+ dev_dbg(dev, "%s: ps input event %d cm\n", __func__, dist);
return scnprintf(buf, PAGE_SIZE, "%d\n", dist);
}
@@ -1311,7 +1366,7 @@ static ssize_t stk_ps_distance_store(struct device *dev, struct device_attribute
input_sync(ps_data->ps_input_dev);
mutex_unlock(&ps_data->io_lock);
wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
- printk(KERN_INFO "%s: ps input event %ld cm\n",__func__, value);
+ dev_dbg(dev, "%s: ps input event %ld cm\n", __func__, value);
return size;
}
@@ -1507,7 +1562,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att
}
else
{
- printk(KERN_INFO "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]);
+ dev_dbg(dev, "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]);
}
}
ps_reg[cnt] = i2c_smbus_read_byte_data(ps_data->client, STK_PDT_ID_REG);
@@ -1517,7 +1572,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att
printk( KERN_ERR "all_reg_show:i2c_smbus_read_byte_data fail, ret=%d", ps_reg[cnt]);
return -EINVAL;
}
- printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]);
+ dev_dbg(dev, "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]);
cnt++;
ps_reg[cnt] = i2c_smbus_read_byte_data(ps_data->client, STK_RSRVD_REG);
if(ps_reg[cnt] < 0)
@@ -1526,7 +1581,7 @@ static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *att
printk( KERN_ERR "all_reg_show:i2c_smbus_read_byte_data fail, ret=%d", ps_reg[cnt]);
return -EINVAL;
}
- printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]);
+ dev_dbg(dev, "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]);
mutex_unlock(&ps_data->io_lock);
return scnprintf(buf, PAGE_SIZE, "%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X %2X %2X %2X,%2X %2X\n",
@@ -1589,8 +1644,7 @@ static ssize_t stk_send_store(struct device *dev, struct device_attribute *attr,
__func__, ret);
return ret;
}
- printk(KERN_INFO "%s: write reg 0x%x=0x%x\n", __func__, addr, cmd);
-
+ dev_dbg(dev, "%s: write reg 0x%x=0x%x\n", __func__, addr, cmd);
addr_u8 = (u8) addr;
cmd_u8 = (u8) cmd;
//mutex_lock(&ps_data->io_lock);
@@ -1688,7 +1742,6 @@ static void stk_als_work_func(struct work_struct *work)
input_report_abs(ps_data->als_input_dev, ABS_MISC, ps_data->als_lux_last);
input_sync(ps_data->als_input_dev);
mutex_unlock(&ps_data->io_lock);
- //printk(KERN_INFO "%s: als input event %d lux\n",__func__, ps_data->als_lux_last);
}
#endif
@@ -1948,7 +2001,6 @@ static void stk3x1x_early_suspend(struct early_suspend *h)
int err;
#endif
- printk(KERN_INFO "%s", __func__);
mutex_lock(&ps_data->io_lock);
if(ps_data->als_enabled)
{
@@ -1976,7 +2028,6 @@ static void stk3x1x_late_resume(struct early_suspend *h)
int err;
#endif
- printk(KERN_INFO "%s", __func__);
mutex_lock(&ps_data->io_lock);
if(ps_data->als_enabled)
stk3x1x_enable_als(ps_data, 1);
@@ -2399,10 +2450,16 @@ static int stk3x1x_probe(struct i2c_client *client,
register_early_suspend(&ps_data->stk_early_suspend);
#endif
/* make sure everything is ok before registering the class device */
- err = sensors_classdev_register(&client->dev, &sensors_light_cdev);
+ ps_data->als_cdev = sensors_light_cdev;
+ ps_data->als_cdev.sensors_enable = stk_als_enable_set;
+ ps_data->als_cdev.sensors_poll_delay = stk_als_poll_delay_set;
+ err = sensors_classdev_register(&client->dev, &ps_data->als_cdev);
if (err)
goto err_power_on;
- err = sensors_classdev_register(&client->dev, &sensors_proximity_cdev);
+
+ ps_data->ps_cdev = sensors_proximity_cdev;
+ ps_data->ps_cdev.sensors_enable = stk_ps_enable_set;
+ err = sensors_classdev_register(&client->dev, &ps_data->ps_cdev);
if (err)
goto err_class_sysfs;
@@ -2416,9 +2473,9 @@ static int stk3x1x_probe(struct i2c_client *client,
err_init_all_setting:
stk3x1x_power_ctl(ps_data, false);
- sensors_classdev_unregister(&sensors_proximity_cdev);
+ sensors_classdev_unregister(&ps_data->ps_cdev);
err_class_sysfs:
- sensors_classdev_unregister(&sensors_light_cdev);
+ sensors_classdev_unregister(&ps_data->als_cdev);
err_power_on:
stk3x1x_power_init(ps_data, false);
err_power_init:
diff --git a/drivers/input/touchscreen/atmel_maxtouch_ts.c b/drivers/input/touchscreen/atmel_maxtouch_ts.c
new file mode 100755
index 000000000000..f2f9fd047c79
--- /dev/null
+++ b/drivers/input/touchscreen/atmel_maxtouch_ts.c
@@ -0,0 +1,3286 @@
+/*
+ * Atmel maXTouch Touchscreen driver
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ * Copyright (C) 2011-2012 Atmel Corporation
+ * Copyright (C) 2012 Google, Inc.
+ *
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+
+/* Configuration file */
+#define MXT_CFG_MAGIC "OBP_RAW V1"
+
+/* Registers */
+#define MXT_OBJECT_START 0x07
+#define MXT_OBJECT_SIZE 6
+#define MXT_INFO_CHECKSUM_SIZE 3
+#define MXT_MAX_BLOCK_WRITE 256
+
+/* Object types */
+#define MXT_DEBUG_DIAGNOSTIC_T37 37
+#define MXT_GEN_MESSAGE_T5 5
+#define MXT_GEN_COMMAND_T6 6
+#define MXT_GEN_POWER_T7 7
+#define MXT_GEN_ACQUIRE_T8 8
+#define MXT_GEN_DATASOURCE_T53 53
+#define MXT_TOUCH_MULTI_T9 9
+#define MXT_TOUCH_KEYARRAY_T15 15
+#define MXT_TOUCH_PROXIMITY_T23 23
+#define MXT_TOUCH_PROXKEY_T52 52
+#define MXT_PROCI_GRIPFACE_T20 20
+#define MXT_PROCG_NOISE_T22 22
+#define MXT_PROCI_ONETOUCH_T24 24
+#define MXT_PROCI_TWOTOUCH_T27 27
+#define MXT_PROCI_GRIP_T40 40
+#define MXT_PROCI_PALM_T41 41
+#define MXT_PROCI_TOUCHSUPPRESSION_T42 42
+#define MXT_PROCI_STYLUS_T47 47
+#define MXT_PROCG_NOISESUPPRESSION_T48 48
+#define MXT_SPT_COMMSCONFIG_T18 18
+#define MXT_SPT_GPIOPWM_T19 19
+#define MXT_SPT_SELFTEST_T25 25
+#define MXT_SPT_CTECONFIG_T28 28
+#define MXT_SPT_USERDATA_T38 38
+#define MXT_SPT_DIGITIZER_T43 43
+#define MXT_SPT_MESSAGECOUNT_T44 44
+#define MXT_SPT_CTECONFIG_T46 46
+#define MXT_PROCI_ACTIVE_STYLUS_T63 63
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
+
+/* MXT_GEN_MESSAGE_T5 object */
+#define MXT_RPTID_NOMSG 0xff
+
+/* MXT_GEN_COMMAND_T6 field */
+#define MXT_COMMAND_RESET 0
+#define MXT_COMMAND_BACKUPNV 1
+#define MXT_COMMAND_CALIBRATE 2
+#define MXT_COMMAND_REPORTALL 3
+#define MXT_COMMAND_DIAGNOSTIC 5
+
+/* Define for T6 status byte */
+#define MXT_T6_STATUS_RESET (1 << 7)
+#define MXT_T6_STATUS_OFL (1 << 6)
+#define MXT_T6_STATUS_SIGERR (1 << 5)
+#define MXT_T6_STATUS_CAL (1 << 4)
+#define MXT_T6_STATUS_CFGERR (1 << 3)
+#define MXT_T6_STATUS_COMSERR (1 << 2)
+
+/* MXT_GEN_POWER_T7 field */
+struct t7_config {
+ u8 idle;
+ u8 active;
+} __packed;
+
+#define MXT_POWER_CFG_RUN 0
+#define MXT_POWER_CFG_DEEPSLEEP 1
+
+/* MXT_TOUCH_MULTI_T9 field */
+#define MXT_T9_ORIENT 9
+#define MXT_T9_RANGE 18
+
+/* MXT_TOUCH_MULTI_T9 status */
+#define MXT_T9_UNGRIP (1 << 0)
+#define MXT_T9_SUPPRESS (1 << 1)
+#define MXT_T9_AMP (1 << 2)
+#define MXT_T9_VECTOR (1 << 3)
+#define MXT_T9_MOVE (1 << 4)
+#define MXT_T9_RELEASE (1 << 5)
+#define MXT_T9_PRESS (1 << 6)
+#define MXT_T9_DETECT (1 << 7)
+
+struct t9_range {
+ u16 x;
+ u16 y;
+} __packed;
+
+/* MXT_TOUCH_MULTI_T9 orient */
+#define MXT_T9_ORIENT_SWITCH (1 << 0)
+
+/* MXT_SPT_COMMSCONFIG_T18 */
+#define MXT_COMMS_CTRL 0
+#define MXT_COMMS_CMD 1
+#define MXT_COMMS_RETRIGEN (1 << 6)
+
+/* Define for MXT_GEN_COMMAND_T6 */
+#define MXT_BOOT_VALUE 0xa5
+#define MXT_RESET_VALUE 0x01
+#define MXT_BACKUP_VALUE 0x55
+
+/* Define for MXT_PROCI_TOUCHSUPPRESSION_T42 */
+#define MXT_T42_MSG_TCHSUP (1 << 0)
+
+/* T47 Stylus */
+#define MXT_TOUCH_MAJOR_T47_STYLUS 1
+
+/* T63 Stylus */
+#define MXT_T63_STYLUS_PRESS (1 << 0)
+#define MXT_T63_STYLUS_RELEASE (1 << 1)
+#define MXT_T63_STYLUS_MOVE (1 << 2)
+#define MXT_T63_STYLUS_SUPPRESS (1 << 3)
+
+#define MXT_T63_STYLUS_DETECT (1 << 4)
+#define MXT_T63_STYLUS_TIP (1 << 5)
+#define MXT_T63_STYLUS_ERASER (1 << 6)
+#define MXT_T63_STYLUS_BARREL (1 << 7)
+
+#define MXT_T63_STYLUS_PRESSURE_MASK 0x3F
+
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL 0
+#define MXT_T100_CFG1 1
+#define MXT_T100_TCHAUX 3
+#define MXT_T100_XRANGE 13
+#define MXT_T100_YRANGE 24
+
+#define MXT_T100_CFG_SWITCHXY (1 << 5)
+
+#define MXT_T100_TCHAUX_VECT (1 << 0)
+#define MXT_T100_TCHAUX_AMPL (1 << 1)
+#define MXT_T100_TCHAUX_AREA (1 << 2)
+
+#define MXT_T100_DETECT (1 << 7)
+#define MXT_T100_TYPE_MASK 0x70
+#define MXT_T100_TYPE_STYLUS 0x20
+
+/* Delay times */
+#define MXT_BACKUP_TIME 50 /* msec */
+#define MXT_RESET_TIME 200 /* msec */
+#define MXT_RESET_TIMEOUT 3000 /* msec */
+#define MXT_CRC_TIMEOUT 1000 /* msec */
+#define MXT_FW_RESET_TIME 3000 /* msec */
+#define MXT_FW_CHG_TIMEOUT 300 /* msec */
+#define MXT_WAKEUP_TIME 25 /* msec */
+#define MXT_REGULATOR_DELAY 150 /* msec */
+#define MXT_POWERON_DELAY 150 /* msec */
+
+/* Command to unlock bootloader */
+#define MXT_UNLOCK_CMD_MSB 0xaa
+#define MXT_UNLOCK_CMD_LSB 0xdc
+
+/* Bootloader mode status */
+#define MXT_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */
+#define MXT_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */
+#define MXT_FRAME_CRC_CHECK 0x02
+#define MXT_FRAME_CRC_FAIL 0x03
+#define MXT_FRAME_CRC_PASS 0x04
+#define MXT_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */
+#define MXT_BOOT_STATUS_MASK 0x3f
+#define MXT_BOOT_EXTENDED_ID (1 << 5)
+#define MXT_BOOT_ID_MASK 0x1f
+
+/* Touchscreen absolute values */
+#define MXT_MAX_AREA 0xff
+
+#define MXT_PIXELS_PER_MM 20
+
+#define DEBUG_MSG_MAX 200
+
+struct mxt_info {
+ u8 family_id;
+ u8 variant_id;
+ u8 version;
+ u8 build;
+ u8 matrix_xsize;
+ u8 matrix_ysize;
+ u8 object_num;
+};
+
+struct mxt_object {
+ u8 type;
+ u16 start_address;
+ u8 size_minus_one;
+ u8 instances_minus_one;
+ u8 num_report_ids;
+} __packed;
+
+/* Each client has this additional data */
+struct mxt_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ char phys[64]; /* device physical location */
+ struct mxt_platform_data *pdata;
+ struct mxt_object *object_table;
+ struct mxt_info *info;
+ void *raw_info_block;
+ unsigned int irq;
+ unsigned int max_x;
+ unsigned int max_y;
+ bool in_bootloader;
+ u16 mem_size;
+ u8 t100_aux_ampl;
+ u8 t100_aux_area;
+ u8 t100_aux_vect;
+ struct bin_attribute mem_access_attr;
+ bool debug_enabled;
+ bool debug_v2_enabled;
+ u8 *debug_msg_data;
+ u16 debug_msg_count;
+ struct bin_attribute debug_msg_attr;
+ struct mutex debug_msg_lock;
+ u8 max_reportid;
+ u32 config_crc;
+ u32 info_crc;
+ u8 bootloader_addr;
+ struct t7_config t7_cfg;
+ u8 *msg_buf;
+ u8 t6_status;
+ bool update_input;
+ u8 last_message_count;
+ u8 num_touchids;
+ u8 num_stylusids;
+ unsigned long t15_keystatus;
+ bool use_retrigen_workaround;
+ bool use_regulator;
+ struct regulator *reg_vdd;
+ struct regulator *reg_avdd;
+ char *fw_name;
+ char *cfg_name;
+
+ /* Cached parameters from object table */
+ u16 T5_address;
+ u8 T5_msg_size;
+ u8 T6_reportid;
+ u16 T6_address;
+ u16 T7_address;
+ u8 T9_reportid_min;
+ u8 T9_reportid_max;
+ u8 T15_reportid_min;
+ u8 T15_reportid_max;
+ u16 T18_address;
+ u8 T19_reportid;
+ u8 T42_reportid_min;
+ u8 T42_reportid_max;
+ u16 T44_address;
+ u8 T48_reportid;
+ u8 T63_reportid_min;
+ u8 T63_reportid_max;
+ u8 T100_reportid_min;
+ u8 T100_reportid_max;
+
+ /* for fw update in bootloader */
+ struct completion bl_completion;
+
+ /* for reset handling */
+ struct completion reset_completion;
+
+ /* for reset handling */
+ struct completion crc_completion;
+
+ /* Enable reporting of input events */
+ bool enable_reporting;
+
+ /* Indicates whether device is in suspend */
+ bool suspended;
+};
+
+static inline size_t mxt_obj_size(const struct mxt_object *obj)
+{
+ return obj->size_minus_one + 1;
+}
+
+static inline size_t mxt_obj_instances(const struct mxt_object *obj)
+{
+ return obj->instances_minus_one + 1;
+}
+
+static bool mxt_object_readable(unsigned int type)
+{
+ switch (type) {
+ case MXT_GEN_COMMAND_T6:
+ case MXT_GEN_POWER_T7:
+ case MXT_GEN_ACQUIRE_T8:
+ case MXT_GEN_DATASOURCE_T53:
+ case MXT_TOUCH_MULTI_T9:
+ case MXT_TOUCH_KEYARRAY_T15:
+ case MXT_TOUCH_PROXIMITY_T23:
+ case MXT_TOUCH_PROXKEY_T52:
+ case MXT_PROCI_GRIPFACE_T20:
+ case MXT_PROCG_NOISE_T22:
+ case MXT_PROCI_ONETOUCH_T24:
+ case MXT_PROCI_TWOTOUCH_T27:
+ case MXT_PROCI_GRIP_T40:
+ case MXT_PROCI_PALM_T41:
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ case MXT_PROCI_STYLUS_T47:
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ case MXT_SPT_COMMSCONFIG_T18:
+ case MXT_SPT_GPIOPWM_T19:
+ case MXT_SPT_SELFTEST_T25:
+ case MXT_SPT_CTECONFIG_T28:
+ case MXT_SPT_USERDATA_T38:
+ case MXT_SPT_DIGITIZER_T43:
+ case MXT_SPT_CTECONFIG_T46:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static void mxt_dump_message(struct mxt_data *data, u8 *message)
+{
+ print_hex_dump(KERN_DEBUG, "MXT MSG:", DUMP_PREFIX_NONE, 16, 1,
+ message, data->T5_msg_size, false);
+}
+
+static void mxt_debug_msg_enable(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+
+ if (data->debug_v2_enabled)
+ return;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ data->debug_msg_data = kcalloc(DEBUG_MSG_MAX,
+ data->T5_msg_size, GFP_KERNEL);
+ if (!data->debug_msg_data) {
+ dev_err(&data->client->dev, "Failed to allocate buffer\n");
+ return;
+ }
+
+ data->debug_v2_enabled = true;
+ mutex_unlock(&data->debug_msg_lock);
+
+ dev_info(dev, "Enabled message output\n");
+}
+
+static void mxt_debug_msg_disable(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+
+ if (!data->debug_v2_enabled)
+ return;
+
+ dev_info(dev, "disabling message output\n");
+ data->debug_v2_enabled = false;
+
+ mutex_lock(&data->debug_msg_lock);
+ kfree(data->debug_msg_data);
+ data->debug_msg_data = NULL;
+ data->debug_msg_count = 0;
+ mutex_unlock(&data->debug_msg_lock);
+ dev_info(dev, "Disabled message output\n");
+}
+
+static void mxt_debug_msg_add(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ if (!data->debug_msg_data) {
+ dev_err(dev, "No buffer!\n");
+ return;
+ }
+
+ if (data->debug_msg_count < DEBUG_MSG_MAX) {
+ memcpy(data->debug_msg_data + data->debug_msg_count * data->T5_msg_size,
+ msg,
+ data->T5_msg_size);
+ data->debug_msg_count++;
+ } else {
+ dev_dbg(dev, "Discarding %u messages\n", data->debug_msg_count);
+ data->debug_msg_count = 0;
+ }
+
+ mutex_unlock(&data->debug_msg_lock);
+
+ sysfs_notify(&data->client->dev.kobj, NULL, "debug_notify");
+}
+
+static ssize_t mxt_debug_msg_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ return -EIO;
+}
+
+static ssize_t mxt_debug_msg_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t bytes)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int count;
+ size_t bytes_read;
+
+ if (!data->debug_msg_data) {
+ dev_err(dev, "No buffer!\n");
+ return 0;
+ }
+
+ count = bytes / data->T5_msg_size;
+
+ if (count > DEBUG_MSG_MAX)
+ count = DEBUG_MSG_MAX;
+
+ mutex_lock(&data->debug_msg_lock);
+
+ if (count > data->debug_msg_count)
+ count = data->debug_msg_count;
+
+ bytes_read = count * data->T5_msg_size;
+
+ memcpy(buf, data->debug_msg_data, bytes_read);
+ data->debug_msg_count = 0;
+
+ mutex_unlock(&data->debug_msg_lock);
+
+ return bytes_read;
+}
+
+static int mxt_debug_msg_init(struct mxt_data *data)
+{
+ sysfs_bin_attr_init(&data->debug_msg_attr);
+ data->debug_msg_attr.attr.name = "debug_msg";
+ data->debug_msg_attr.attr.mode = 0666;
+ data->debug_msg_attr.read = mxt_debug_msg_read;
+ data->debug_msg_attr.write = mxt_debug_msg_write;
+ data->debug_msg_attr.size = data->T5_msg_size * DEBUG_MSG_MAX;
+
+ if (sysfs_create_bin_file(&data->client->dev.kobj,
+ &data->debug_msg_attr) < 0) {
+ dev_err(&data->client->dev, "Failed to create %s\n",
+ data->debug_msg_attr.attr.name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void mxt_debug_msg_remove(struct mxt_data *data)
+{
+ if (data->debug_msg_attr.attr.name)
+ sysfs_remove_bin_file(&data->client->dev.kobj,
+ &data->debug_msg_attr);
+}
+
+static int mxt_wait_for_completion(struct mxt_data *data,
+ struct completion *comp, unsigned int timeout_ms)
+{
+ struct device *dev = &data->client->dev;
+ unsigned long timeout = msecs_to_jiffies(timeout_ms);
+ long ret;
+
+ ret = wait_for_completion_interruptible_timeout(comp, timeout);
+ if (ret < 0) {
+ dev_err(dev, "Wait for completion interrupted.\n");
+ return -EINTR;
+ } else if (ret == 0) {
+ dev_err(dev, "Wait for completion timed out.\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+}
+
+static int mxt_bootloader_read(struct mxt_data *data,
+ u8 *val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.flags |= I2C_M_RD;
+ msg.len = count;
+ msg.buf = val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = (ret < 0) ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c recv failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_bootloader_write(struct mxt_data *data,
+ const u8 * const val, unsigned int count)
+{
+ int ret;
+ struct i2c_msg msg;
+
+ msg.addr = data->bootloader_addr;
+ msg.flags = data->client->flags & I2C_M_TEN;
+ msg.len = count;
+ msg.buf = (u8 *)val;
+
+ ret = i2c_transfer(data->client->adapter, &msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ ret = (ret < 0) ? ret : -EIO;
+ dev_err(&data->client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
+ }
+
+ return ret;
+}
+
+static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
+{
+ u8 appmode = data->client->addr;
+ u8 bootloader;
+ u8 family_id = 0;
+
+ if (data->info)
+ family_id = data->info->family_id;
+
+ switch (appmode) {
+ case 0x4a:
+ case 0x4b:
+ /* Chips after 1664S use different scheme */
+ if (retry || family_id >= 0xa2) {
+ bootloader = appmode - 0x24;
+ break;
+ }
+ /* Fall through for normal case */
+ case 0x4c:
+ case 0x4d:
+ case 0x5a:
+ case 0x5b:
+ bootloader = appmode - 0x26;
+ break;
+ default:
+ dev_err(&data->client->dev,
+ "Appmode i2c address 0x%02x not found\n",
+ appmode);
+ return -EINVAL;
+ }
+
+ data->bootloader_addr = bootloader;
+ return 0;
+}
+
+static int mxt_probe_bootloader(struct mxt_data *data, bool retry)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ u8 val;
+ bool crc_failure;
+
+ ret = mxt_lookup_bootloader_address(data, retry);
+ if (ret)
+ return ret;
+
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret)
+ return ret;
+
+ /* Check app crc fail mode */
+ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL;
+
+ dev_err(dev, "Detected bootloader, status:%02X%s\n",
+ val, crc_failure ? ", APP_CRC_FAIL" : "");
+
+ return 0;
+}
+
+static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val)
+{
+ struct device *dev = &data->client->dev;
+ u8 buf[3];
+
+ if (val & MXT_BOOT_EXTENDED_ID) {
+ if (mxt_bootloader_read(data, &buf[0], 3) != 0) {
+ dev_err(dev, "%s: i2c failure\n", __func__);
+ return -EIO;
+ }
+
+ dev_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+ return buf[0];
+ } else {
+ dev_info(dev, "Bootloader ID:%d\n", val & MXT_BOOT_ID_MASK);
+
+ return val;
+ }
+}
+
+static int mxt_check_bootloader(struct mxt_data *data, unsigned int state,
+ bool wait)
+{
+ struct device *dev = &data->client->dev;
+ u8 val;
+ int ret;
+
+recheck:
+ if (wait) {
+ /*
+ * In application update mode, the interrupt
+ * line signals state transitions. We must wait for the
+ * CHG assertion before reading the status byte.
+ * Once the status byte has been read, the line is deasserted.
+ */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_CHG_TIMEOUT);
+ if (ret) {
+ /*
+ * TODO: handle -EINTR better by terminating fw update
+ * process before returning to userspace by writing
+ * length 0x000 to device (iff we are in
+ * WAITING_FRAME_DATA state).
+ */
+ dev_err(dev, "Update wait error %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = mxt_bootloader_read(data, &val, 1);
+ if (ret)
+ return ret;
+
+ if (state == MXT_WAITING_BOOTLOAD_CMD)
+ val = mxt_get_bootloader_version(data, val);
+
+ switch (state) {
+ case MXT_WAITING_BOOTLOAD_CMD:
+ case MXT_WAITING_FRAME_DATA:
+ case MXT_APP_CRC_FAIL:
+ val &= ~MXT_BOOT_STATUS_MASK;
+ break;
+ case MXT_FRAME_CRC_PASS:
+ if (val == MXT_FRAME_CRC_CHECK) {
+ goto recheck;
+ } else if (val == MXT_FRAME_CRC_FAIL) {
+ dev_err(dev, "Bootloader CRC fail\n");
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (val != state) {
+ dev_err(dev, "Invalid bootloader state %02X != %02X\n",
+ val, state);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mxt_send_bootloader_cmd(struct mxt_data *data, bool unlock)
+{
+ int ret;
+ u8 buf[2];
+
+ if (unlock) {
+ buf[0] = MXT_UNLOCK_CMD_LSB;
+ buf[1] = MXT_UNLOCK_CMD_MSB;
+ } else {
+ buf[0] = 0x01;
+ buf[1] = 0x01;
+ }
+
+ ret = mxt_bootloader_write(data, buf, 2);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int __mxt_read_reg(struct i2c_client *client,
+ u16 reg, u16 len, void *val)
+{
+ struct i2c_msg xfer[2];
+ u8 buf[2];
+ int ret;
+ bool retry = false;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 2;
+ xfer[0].buf = buf;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = len;
+ xfer[1].buf = val;
+
+retry_read:
+ ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+ if (ret != ARRAY_SIZE(xfer)) {
+ if (!retry) {
+ dev_dbg(&client->dev, "%s: i2c retry\n", __func__);
+ msleep(MXT_WAKEUP_TIME);
+ retry = true;
+ goto retry_read;
+ } else {
+ dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
+ __func__, ret);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
+ const void *val)
+{
+ u8 *buf;
+ size_t count;
+ int ret;
+ bool retry = false;
+
+ count = len + 2;
+ buf = kmalloc(count, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ memcpy(&buf[2], val, len);
+
+retry_write:
+ ret = i2c_master_send(client, buf, count);
+ if (ret != count) {
+ if (!retry) {
+ dev_dbg(&client->dev, "%s: i2c retry\n", __func__);
+ msleep(MXT_WAKEUP_TIME);
+ retry = true;
+ goto retry_write;
+ } else {
+ dev_err(&client->dev, "%s: i2c send failed (%d)\n",
+ __func__, ret);
+ ret = -EIO;
+ }
+ } else {
+ ret = 0;
+ }
+
+ kfree(buf);
+ return ret;
+}
+
+static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
+{
+ return __mxt_write_reg(client, reg, 1, &val);
+}
+
+static struct mxt_object *
+mxt_get_object(struct mxt_data *data, u8 type)
+{
+ struct mxt_object *object;
+ int i;
+
+ for (i = 0; i < data->info->object_num; i++) {
+ object = data->object_table + i;
+ if (object->type == type)
+ return object;
+ }
+
+ dev_warn(&data->client->dev, "Invalid object type T%u\n", type);
+ return NULL;
+}
+
+static void mxt_proc_t6_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ u8 status = msg[1];
+ u32 crc = msg[2] | (msg[3] << 8) | (msg[4] << 16);
+
+ if (crc != data->config_crc) {
+ data->config_crc = crc;
+ dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+ complete(&data->crc_completion);
+ }
+
+ /* Detect transition out of reset */
+ if ((data->t6_status & MXT_T6_STATUS_RESET) &&
+ !(status & MXT_T6_STATUS_RESET))
+ complete(&data->reset_completion);
+
+ /* Output debug if status has changed */
+ if (status != data->t6_status)
+ dev_dbg(dev, "T6 Status 0x%02X%s%s%s%s%s%s%s\n",
+ status,
+ (status == 0) ? " OK" : "",
+ (status & MXT_T6_STATUS_RESET) ? " RESET" : "",
+ (status & MXT_T6_STATUS_OFL) ? " OFL" : "",
+ (status & MXT_T6_STATUS_SIGERR) ? " SIGERR" : "",
+ (status & MXT_T6_STATUS_CAL) ? " CAL" : "",
+ (status & MXT_T6_STATUS_CFGERR) ? " CFGERR" : "",
+ (status & MXT_T6_STATUS_COMSERR) ? " COMSERR" : "");
+
+ /* Save current status */
+ data->t6_status = status;
+}
+
+static void mxt_input_button(struct mxt_data *data, u8 *message)
+{
+ struct input_dev *input = data->input_dev;
+ const struct mxt_platform_data *pdata = data->pdata;
+ bool button;
+ int i;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ /* Active-low switch */
+ for (i = 0; i < pdata->t19_num_keys; i++) {
+ if (pdata->t19_keymap[i] == KEY_RESERVED)
+ continue;
+ button = !(message[1] & (1 << i));
+ input_report_key(input, pdata->t19_keymap[i], button);
+ }
+}
+
+static void mxt_input_sync(struct input_dev *input_dev)
+{
+ input_mt_report_pointer_emulation(input_dev, false);
+ input_sync(input_dev);
+}
+
+static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ int x;
+ int y;
+ int area;
+ int amplitude;
+ u8 vector;
+ int tool;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ id = message[0] - data->T9_reportid_min;
+ status = message[1];
+ x = (message[2] << 4) | ((message[4] >> 4) & 0xf);
+ y = (message[3] << 4) | ((message[4] & 0xf));
+
+ /* Handle 10/12 bit switching */
+ if (data->max_x < 1024)
+ x >>= 2;
+ if (data->max_y < 1024)
+ y >>= 2;
+
+ area = message[5];
+
+ amplitude = message[6];
+ vector = message[7];
+
+ dev_dbg(dev,
+ "[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u vector: %02X\n",
+ id,
+ (status & MXT_T9_DETECT) ? 'D' : '.',
+ (status & MXT_T9_PRESS) ? 'P' : '.',
+ (status & MXT_T9_RELEASE) ? 'R' : '.',
+ (status & MXT_T9_MOVE) ? 'M' : '.',
+ (status & MXT_T9_VECTOR) ? 'V' : '.',
+ (status & MXT_T9_AMP) ? 'A' : '.',
+ (status & MXT_T9_SUPPRESS) ? 'S' : '.',
+ (status & MXT_T9_UNGRIP) ? 'U' : '.',
+ x, y, area, amplitude, vector);
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T9_DETECT) {
+ /* Multiple bits may be set if the host is slow to read the
+ * status messages, indicating all the events that have
+ * happened */
+ if (status & MXT_T9_RELEASE) {
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, 0);
+ mxt_input_sync(input_dev);
+ }
+
+ /* A reported size of zero indicates that the reported touch
+ * is a stylus from a linked Stylus T47 object. */
+ if (area == 0) {
+ area = MXT_TOUCH_MAJOR_T47_STYLUS;
+ tool = MT_TOOL_PEN;
+ } else {
+ tool = MT_TOOL_FINGER;
+ }
+
+ /* Touch active */
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, amplitude);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
+ input_report_abs(input_dev, ABS_MT_ORIENTATION, vector);
+ } else {
+ /* Touch no longer active, close out slot */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ data->update_input = true;
+}
+
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ int x;
+ int y;
+ int tool;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ id = message[0] - data->T100_reportid_min - 2;
+
+ /* ignore SCRSTATUS events */
+ if (id < 0)
+ return;
+
+ status = message[1];
+ x = (message[3] << 8) | message[2];
+ y = (message[5] << 8) | message[4];
+
+ dev_dbg(dev,
+ "[%u] status:%02X x:%u y:%u area:%02X amp:%02X vec:%02X\n",
+ id,
+ status,
+ x, y,
+ (data->t100_aux_area) ? message[data->t100_aux_area] : 0,
+ (data->t100_aux_ampl) ? message[data->t100_aux_ampl] : 0,
+ (data->t100_aux_vect) ? message[data->t100_aux_vect] : 0);
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T100_DETECT) {
+ /* A reported size of zero indicates that the reported touch
+ * is a stylus from a linked Stylus T47 object. */
+ if ((status & MXT_T100_TYPE_MASK) == MXT_T100_TYPE_STYLUS)
+ tool = MT_TOOL_PEN;
+ else
+ tool = MT_TOOL_FINGER;
+
+ /* Touch active */
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+
+ if (data->t100_aux_ampl)
+ input_report_abs(input_dev, ABS_MT_PRESSURE,
+ message[data->t100_aux_ampl]);
+
+ if (data->t100_aux_area) {
+ if (tool == MT_TOOL_PEN)
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ MXT_TOUCH_MAJOR_T47_STYLUS);
+ else
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
+ message[data->t100_aux_area]);
+ }
+
+ if (data->t100_aux_vect)
+ input_report_abs(input_dev, ABS_MT_ORIENTATION,
+ message[data->t100_aux_vect]);
+ } else {
+ /* Touch no longer active, close out slot */
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ data->update_input = true;
+}
+
+
+static void mxt_proc_t15_messages(struct mxt_data *data, u8 *msg)
+{
+ struct input_dev *input_dev = data->input_dev;
+ struct device *dev = &data->client->dev;
+ int key;
+ bool curr_state, new_state;
+ bool sync = false;
+ unsigned long keystates = le32_to_cpu(msg[2]);
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ for (key = 0; key < data->pdata->t15_num_keys; key++) {
+ curr_state = test_bit(key, &data->t15_keystatus);
+ new_state = test_bit(key, &keystates);
+
+ if (!curr_state && new_state) {
+ dev_dbg(dev, "T15 key press: %u\n", key);
+ __set_bit(key, &data->t15_keystatus);
+ input_event(input_dev, EV_KEY,
+ data->pdata->t15_keymap[key], 1);
+ sync = true;
+ } else if (curr_state && !new_state) {
+ dev_dbg(dev, "T15 key release: %u\n", key);
+ __clear_bit(key, &data->t15_keystatus);
+ input_event(input_dev, EV_KEY,
+ data->pdata->t15_keymap[key], 0);
+ sync = true;
+ }
+ }
+
+ if (sync)
+ input_sync(input_dev);
+}
+
+static void mxt_proc_t42_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ u8 status = msg[1];
+
+ if (status & MXT_T42_MSG_TCHSUP)
+ dev_info(dev, "T42 suppress\n");
+ else
+ dev_info(dev, "T42 normal\n");
+}
+
+static int mxt_proc_t48_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ u8 status, state;
+
+ status = msg[1];
+ state = msg[4];
+
+ dev_dbg(dev, "T48 state %d status %02X %s%s%s%s%s\n",
+ state,
+ status,
+ (status & 0x01) ? "FREQCHG " : "",
+ (status & 0x02) ? "APXCHG " : "",
+ (status & 0x04) ? "ALGOERR " : "",
+ (status & 0x10) ? "STATCHG " : "",
+ (status & 0x20) ? "NLVLCHG " : "");
+
+ return 0;
+}
+
+static void mxt_proc_t63_messages(struct mxt_data *data, u8 *msg)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ u8 id;
+ u16 x, y;
+ u8 pressure;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ /* stylus slots come after touch slots */
+ id = data->num_touchids + (msg[0] - data->T63_reportid_min);
+
+ if (id < 0 || id > (data->num_touchids + data->num_stylusids)) {
+ dev_err(dev, "invalid stylus id %d, max slot is %d\n",
+ id, data->num_stylusids);
+ return;
+ }
+
+ x = msg[3] | (msg[4] << 8);
+ y = msg[5] | (msg[6] << 8);
+ pressure = msg[7] & MXT_T63_STYLUS_PRESSURE_MASK;
+
+ dev_dbg(dev,
+ "[%d] %c%c%c%c x: %d y: %d pressure: %d stylus:%c%c%c%c\n",
+ id,
+ (msg[1] & MXT_T63_STYLUS_SUPPRESS) ? 'S' : '.',
+ (msg[1] & MXT_T63_STYLUS_MOVE) ? 'M' : '.',
+ (msg[1] & MXT_T63_STYLUS_RELEASE) ? 'R' : '.',
+ (msg[1] & MXT_T63_STYLUS_PRESS) ? 'P' : '.',
+ x, y, pressure,
+ (msg[2] & MXT_T63_STYLUS_BARREL) ? 'B' : '.',
+ (msg[2] & MXT_T63_STYLUS_ERASER) ? 'E' : '.',
+ (msg[2] & MXT_T63_STYLUS_TIP) ? 'T' : '.',
+ (msg[2] & MXT_T63_STYLUS_DETECT) ? 'D' : '.');
+
+ input_mt_slot(input_dev, id);
+
+ if (msg[2] & MXT_T63_STYLUS_DETECT) {
+ input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ } else {
+ input_mt_report_slot_state(input_dev, MT_TOOL_PEN, 0);
+ }
+
+ input_report_key(input_dev, BTN_STYLUS,
+ (msg[2] & MXT_T63_STYLUS_ERASER));
+ input_report_key(input_dev, BTN_STYLUS2,
+ (msg[2] & MXT_T63_STYLUS_BARREL));
+
+ mxt_input_sync(input_dev);
+}
+
+static int mxt_proc_message(struct mxt_data *data, u8 *message)
+{
+ u8 report_id = message[0];
+ bool dump = data->debug_enabled;
+
+ if (report_id == MXT_RPTID_NOMSG)
+ return 0;
+
+ if (report_id == data->T6_reportid) {
+ mxt_proc_t6_messages(data, message);
+ } else if (report_id >= data->T9_reportid_min
+ && report_id <= data->T9_reportid_max) {
+ mxt_proc_t9_message(data, message);
+ } else if (report_id >= data->T100_reportid_min
+ && report_id <= data->T100_reportid_max) {
+ mxt_proc_t100_message(data, message);
+ } else if (report_id == data->T19_reportid) {
+ mxt_input_button(data, message);
+ data->update_input = true;
+ } else if (report_id >= data->T63_reportid_min
+ && report_id <= data->T63_reportid_max) {
+ mxt_proc_t63_messages(data, message);
+ } else if (report_id >= data->T42_reportid_min
+ && report_id <= data->T42_reportid_max) {
+ mxt_proc_t42_messages(data, message);
+ } else if (report_id == data->T48_reportid) {
+ mxt_proc_t48_messages(data, message);
+ } else if (report_id >= data->T15_reportid_min
+ && report_id <= data->T15_reportid_max) {
+ mxt_proc_t15_messages(data, message);
+ } else {
+ dump = true;
+ }
+
+ if (dump)
+ mxt_dump_message(data, message);
+
+ if (data->debug_v2_enabled)
+ mxt_debug_msg_add(data, message);
+
+ return 1;
+}
+
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ int i;
+ u8 num_valid = 0;
+
+ /* Safety check for msg_buf */
+ if (count > data->max_reportid)
+ return -EINVAL;
+
+ /* Process remaining messages if necessary */
+ ret = __mxt_read_reg(data->client, data->T5_address,
+ data->T5_msg_size * count, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
+ return ret;
+ }
+
+ for (i = 0; i < count; i++) {
+ ret = mxt_proc_message(data,
+ data->msg_buf + data->T5_msg_size * i);
+
+ if (ret == 1)
+ num_valid++;
+ }
+
+ /* return number of messages read */
+ return num_valid;
+}
+
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ u8 count, num_left;
+
+ /* Read T44 and T5 together */
+ ret = __mxt_read_reg(data->client, data->T44_address,
+ data->T5_msg_size + 1, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+ return IRQ_NONE;
+ }
+
+ count = data->msg_buf[0];
+
+ if (count == 0) {
+ dev_warn(dev, "Interrupt triggered but zero messages\n");
+ return IRQ_NONE;
+ } else if (count > data->max_reportid) {
+ dev_err(dev, "T44 count %d exceeded max report id\n", count);
+ count = data->max_reportid;
+ }
+
+ /* Process first message */
+ ret = mxt_proc_message(data, data->msg_buf + 1);
+ if (ret < 0) {
+ dev_warn(dev, "Unexpected invalid message\n");
+ return IRQ_NONE;
+ }
+
+ num_left = count - 1;
+
+ /* Process remaining messages if necessary */
+ if (num_left) {
+ ret = mxt_read_and_process_messages(data, num_left);
+ if (ret < 0)
+ goto end;
+ else if (ret != num_left)
+ dev_warn(dev, "Unexpected invalid message\n");
+ }
+
+end:
+ if (data->update_input) {
+ mxt_input_sync(data->input_dev);
+ data->update_input = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mxt_process_messages_until_invalid(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int count, read;
+ u8 tries = 2;
+
+ count = data->max_reportid;
+
+ /* Read messages until we force an invalid */
+ do {
+ read = mxt_read_and_process_messages(data, count);
+ if (read < count)
+ return 0;
+ } while (--tries);
+
+ if (data->update_input) {
+ mxt_input_sync(data->input_dev);
+ data->update_input = false;
+ }
+
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+ int total_handled, num_handled;
+ u8 count = data->last_message_count;
+
+ if (count < 1 || count > data->max_reportid)
+ count = 1;
+
+ /* include final invalid message */
+ total_handled = mxt_read_and_process_messages(data, count + 1);
+ if (total_handled < 0)
+ return IRQ_NONE;
+ /* if there were invalid messages, then we are done */
+ else if (total_handled <= count)
+ goto update_count;
+
+ /* read two at a time until an invalid message or else we reach
+ * reportid limit */
+ do {
+ num_handled = mxt_read_and_process_messages(data, 2);
+ if (num_handled < 0)
+ return IRQ_NONE;
+
+ total_handled += num_handled;
+
+ if (num_handled < 2)
+ break;
+ } while (total_handled < data->num_touchids);
+
+update_count:
+ data->last_message_count = total_handled;
+
+ if (data->enable_reporting && data->update_input) {
+ mxt_input_sync(data->input_dev);
+ data->update_input = false;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mxt_interrupt(int irq, void *dev_id)
+{
+ struct mxt_data *data = dev_id;
+
+ if (data->in_bootloader) {
+ /* bootloader state transition completion */
+ complete(&data->bl_completion);
+ return IRQ_HANDLED;
+ }
+
+ if (!data->object_table)
+ return IRQ_NONE;
+
+ if (data->T44_address) {
+ return mxt_process_messages_t44(data);
+ } else {
+ return mxt_process_messages(data);
+ }
+}
+
+static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
+ u8 value, bool wait)
+{
+ u16 reg;
+ u8 command_register;
+ int timeout_counter = 0;
+ int ret;
+
+ reg = data->T6_address + cmd_offset;
+
+ ret = mxt_write_reg(data->client, reg, value);
+ if (ret)
+ return ret;
+
+ if (!wait)
+ return 0;
+
+ do {
+ msleep(20);
+ ret = __mxt_read_reg(data->client, reg, 1, &command_register);
+ if (ret)
+ return ret;
+ } while ((command_register != 0) && (timeout_counter++ <= 100));
+
+ if (timeout_counter > 100) {
+ dev_err(&data->client->dev, "Command failed!\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mxt_soft_reset(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret = 0;
+
+ dev_info(dev, "Resetting chip\n");
+
+ INIT_COMPLETION(data->reset_completion);
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false);
+ if (ret)
+ return ret;
+
+ ret = mxt_wait_for_completion(data, &data->reset_completion,
+ MXT_RESET_TIMEOUT);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value)
+{
+ /* on failure, CRC is set to 0 and config will always be downloaded */
+ data->config_crc = 0;
+ INIT_COMPLETION(data->crc_completion);
+
+ mxt_t6_command(data, cmd, value, true);
+
+ /* Wait for crc message. On failure, CRC is set to 0 and config will
+ * always be downloaded */
+ mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT);
+}
+
+static void mxt_calc_crc24(u32 *crc, u8 firstbyte, u8 secondbyte)
+{
+ static const unsigned int crcpoly = 0x80001B;
+ u32 result;
+ u32 data_word;
+
+ data_word = (secondbyte << 8) | firstbyte;
+ result = ((*crc << 1) ^ data_word);
+
+ if (result & 0x1000000)
+ result ^= crcpoly;
+
+ *crc = result;
+}
+
+static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off)
+{
+ u32 crc = 0;
+ u8 *ptr = base + start_off;
+ u8 *last_val = base + end_off - 1;
+
+ if (end_off < start_off)
+ return -EINVAL;
+
+ while (ptr < last_val) {
+ mxt_calc_crc24(&crc, *ptr, *(ptr + 1));
+ ptr += 2;
+ }
+
+ /* if len is odd, fill the last byte with 0 */
+ if (ptr == last_val)
+ mxt_calc_crc24(&crc, *ptr, 0);
+
+ /* Mask to 24-bit */
+ crc &= 0x00FFFFFF;
+
+ return crc;
+}
+
+static int mxt_check_retrigen(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ int val;
+
+ if (data->pdata->irqflags & IRQF_TRIGGER_LOW)
+ return 0;
+
+ if (data->T18_address) {
+ error = __mxt_read_reg(client,
+ data->T18_address + MXT_COMMS_CTRL,
+ 1, &val);
+ if (error)
+ return error;
+
+ if (val & MXT_COMMS_RETRIGEN)
+ return 0;
+ }
+
+ dev_warn(&client->dev, "Enabling RETRIGEN workaround\n");
+ data->use_retrigen_workaround = true;
+ return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data);
+
+/*
+ * mxt_check_reg_init - download configuration to chip
+ *
+ * Atmel Raw Config File Format
+ *
+ * The first four lines of the raw config file contain:
+ * 1) Version
+ * 2) Chip ID Information (first 7 bytes of device memory)
+ * 3) Chip Information Block 24-bit CRC Checksum
+ * 4) Chip Configuration 24-bit CRC Checksum
+ *
+ * The rest of the file consists of one line per object instance:
+ * <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ * <TYPE> - 2-byte object type as hex
+ * <INSTANCE> - 2-byte object instance number as hex
+ * <SIZE> - 2-byte object size as hex
+ * <CONTENTS> - array of <SIZE> 1-byte hex values
+ */
+static int mxt_check_reg_init(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct mxt_info cfg_info;
+ struct mxt_object *object;
+ const struct firmware *cfg = NULL;
+ int ret;
+ int offset;
+ int data_pos;
+ int byte_offset;
+ int i;
+ int cfg_start_ofs;
+ u32 info_crc, config_crc, calculated_crc;
+ u8 *config_mem;
+ size_t config_mem_size;
+ unsigned int type, instance, size;
+ u8 val;
+ u16 reg;
+
+ if (!data->cfg_name) {
+ dev_dbg(dev, "Skipping cfg download\n");
+ return 0;
+ }
+
+ ret = request_firmware(&cfg, data->cfg_name, dev);
+ if (ret < 0) {
+ dev_err(dev, "Failure to request config file %s\n",
+ data->cfg_name);
+ return 0;
+ }
+
+ mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1);
+
+ if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+ dev_err(dev, "Unrecognised config file\n");
+ ret = -EINVAL;
+ goto release;
+ }
+
+ data_pos = strlen(MXT_CFG_MAGIC);
+
+ /* Load information block and check */
+ for (i = 0; i < sizeof(struct mxt_info); i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ (unsigned char *)&cfg_info + i,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format\n");
+ ret = -EINVAL;
+ goto release;
+ }
+
+ data_pos += offset;
+ }
+
+ if (cfg_info.family_id != data->info->family_id) {
+ dev_err(dev, "Family ID mismatch!\n");
+ ret = -EINVAL;
+ goto release;
+ }
+
+ if (cfg_info.variant_id != data->info->variant_id) {
+ dev_err(dev, "Variant ID mismatch!\n");
+ ret = -EINVAL;
+ goto release;
+ }
+
+ /* Read CRCs */
+ ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Info CRC\n");
+ ret = -EINVAL;
+ goto release;
+ }
+ data_pos += offset;
+
+ ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format: failed to parse Config CRC\n");
+ ret = -EINVAL;
+ goto release;
+ }
+ data_pos += offset;
+
+ /* The Info Block CRC is calculated over mxt_info and the object table
+ * If it does not match then we are trying to load the configuration
+ * from a different chip or firmware version, so the configuration CRC
+ * is invalid anyway. */
+ if (info_crc == data->info_crc) {
+ if (config_crc == 0 || data->config_crc == 0) {
+ dev_info(dev, "CRC zero, attempting to apply config\n");
+ } else if (config_crc == data->config_crc) {
+ dev_info(dev, "Config CRC 0x%06X: OK\n",
+ data->config_crc);
+ ret = 0;
+ goto release;
+ } else {
+ dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
+ data->config_crc, config_crc);
+ }
+ } else {
+ dev_warn(dev,
+ "Warning: Info CRC error - device=0x%06X file=0x%06X\n",
+ data->info_crc, info_crc);
+ }
+
+ /* Malloc memory to store configuration */
+ cfg_start_ofs = MXT_OBJECT_START
+ + data->info->object_num * sizeof(struct mxt_object)
+ + MXT_INFO_CHECKSUM_SIZE;
+ config_mem_size = data->mem_size - cfg_start_ofs;
+ config_mem = kzalloc(config_mem_size, GFP_KERNEL);
+ if (!config_mem) {
+ dev_err(dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto release;
+ }
+
+ while (data_pos < cfg->size) {
+ /* Read type, instance, length */
+ ret = sscanf(cfg->data + data_pos, "%x %x %x%n",
+ &type, &instance, &size, &offset);
+ if (ret == 0) {
+ /* EOF */
+ break;
+ } else if (ret != 3) {
+ dev_err(dev, "Bad format: failed to parse object\n");
+ ret = -EINVAL;
+ goto release_mem;
+ }
+ data_pos += offset;
+
+ object = mxt_get_object(data, type);
+ if (!object) {
+ /* Skip object */
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val,
+ &offset);
+ data_pos += offset;
+ }
+ continue;
+ }
+
+ if (size > mxt_obj_size(object)) {
+ /* Either we are in fallback mode due to wrong
+ * config or config from a later fw version,
+ * or the file is corrupt or hand-edited */
+ dev_warn(dev, "Discarding %u byte(s) in T%u\n",
+ size - mxt_obj_size(object), type);
+ } else if (mxt_obj_size(object) > size) {
+ /* If firmware is upgraded, new bytes may be added to
+ * end of objects. It is generally forward compatible
+ * to zero these bytes - previous behaviour will be
+ * retained. However this does invalidate the CRC and
+ * will force fallback mode until the configuration is
+ * updated. We warn here but do nothing else - the
+ * malloc has zeroed the entire configuration. */
+ dev_warn(dev, "Zeroing %u byte(s) in T%d\n",
+ mxt_obj_size(object) - size, type);
+ }
+
+ if (instance >= mxt_obj_instances(object)) {
+ dev_err(dev, "Object instances exceeded!\n");
+ ret = -EINVAL;
+ goto release_mem;
+ }
+
+ reg = object->start_address + mxt_obj_size(object) * instance;
+
+ for (i = 0; i < size; i++) {
+ ret = sscanf(cfg->data + data_pos, "%hhx%n",
+ &val,
+ &offset);
+ if (ret != 1) {
+ dev_err(dev, "Bad format in T%d\n", type);
+ ret = -EINVAL;
+ goto release_mem;
+ }
+ data_pos += offset;
+
+ if (i > mxt_obj_size(object))
+ continue;
+
+ byte_offset = reg + i - cfg_start_ofs;
+
+ if ((byte_offset >= 0)
+ && (byte_offset <= config_mem_size)) {
+ *(config_mem + byte_offset) = val;
+ } else {
+ dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n",
+ reg, object->type, byte_offset);
+ ret = -EINVAL;
+ goto release_mem;
+ }
+ }
+ }
+
+ /* calculate crc of the received configs (not the raw config file) */
+ if (data->T7_address < cfg_start_ofs) {
+ dev_err(dev, "Bad T7 address, T7addr = %x, config offset %x\n",
+ data->T7_address, cfg_start_ofs);
+ ret = 0;
+ goto release_mem;
+ }
+
+ calculated_crc = mxt_calculate_crc(config_mem,
+ data->T7_address - cfg_start_ofs,
+ config_mem_size);
+
+ if (config_crc > 0 && (config_crc != calculated_crc))
+ dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n",
+ calculated_crc, config_crc);
+
+ /* Write configuration as blocks */
+ byte_offset = 0;
+ while (byte_offset < config_mem_size) {
+ size = config_mem_size - byte_offset;
+
+ if (size > MXT_MAX_BLOCK_WRITE)
+ size = MXT_MAX_BLOCK_WRITE;
+
+ ret = __mxt_write_reg(data->client,
+ cfg_start_ofs + byte_offset,
+ size, config_mem + byte_offset);
+ if (ret != 0) {
+ dev_err(dev, "Config write error, ret=%d\n", ret);
+ goto release_mem;
+ }
+
+ byte_offset += size;
+ }
+
+ mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE);
+
+ ret = mxt_check_retrigen(data);
+ if (ret)
+ goto release_mem;
+
+ ret = mxt_soft_reset(data);
+ if (ret)
+ goto release_mem;
+
+ dev_info(dev, "Config written\n");
+
+ /* T7 config may have changed */
+ mxt_init_t7_power_cfg(data);
+
+release_mem:
+ kfree(config_mem);
+release:
+ release_firmware(cfg);
+ return ret;
+}
+
+static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ struct t7_config *new_config;
+ struct t7_config deepsleep = { .active = 0, .idle = 0 };
+
+ if (sleep == MXT_POWER_CFG_DEEPSLEEP)
+ new_config = &deepsleep;
+ else
+ new_config = &data->t7_cfg;
+
+ error = __mxt_write_reg(data->client, data->T7_address,
+ sizeof(data->t7_cfg),
+ new_config);
+ if (error)
+ return error;
+
+ dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
+ new_config->active, new_config->idle);
+
+ return 0;
+}
+
+static int mxt_init_t7_power_cfg(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+ bool retry = false;
+
+recheck:
+ error = __mxt_read_reg(data->client, data->T7_address,
+ sizeof(data->t7_cfg), &data->t7_cfg);
+ if (error)
+ return error;
+
+ if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
+ if (!retry) {
+ dev_info(dev, "T7 cfg zero, resetting\n");
+ mxt_soft_reset(data);
+ retry = true;
+ goto recheck;
+ } else {
+ dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
+ data->t7_cfg.active = 20;
+ data->t7_cfg.idle = 100;
+ return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+ }
+ } else {
+ dev_info(dev, "Initialised power cfg: ACTV %d, IDLE %d\n",
+ data->t7_cfg.active, data->t7_cfg.idle);
+ return 0;
+ }
+}
+
+static int mxt_acquire_irq(struct mxt_data *data)
+{
+ int error;
+
+ enable_irq(data->irq);
+
+ if (data->use_retrigen_workaround) {
+ error = mxt_process_messages_until_invalid(data);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static void mxt_free_input_device(struct mxt_data *data)
+{
+ if (data->input_dev) {
+ input_unregister_device(data->input_dev);
+ data->input_dev = NULL;
+ }
+}
+
+static void mxt_free_object_table(struct mxt_data *data)
+{
+ mxt_debug_msg_remove(data);
+
+ kfree(data->raw_info_block);
+ data->object_table = NULL;
+ data->info = NULL;
+ data->raw_info_block = NULL;
+ kfree(data->msg_buf);
+ data->msg_buf = NULL;
+
+ mxt_free_input_device(data);
+
+ data->enable_reporting = false;
+ data->T5_address = 0;
+ data->T5_msg_size = 0;
+ data->T6_reportid = 0;
+ data->T7_address = 0;
+ data->T9_reportid_min = 0;
+ data->T9_reportid_max = 0;
+ data->T15_reportid_min = 0;
+ data->T15_reportid_max = 0;
+ data->T18_address = 0;
+ data->T19_reportid = 0;
+ data->T42_reportid_min = 0;
+ data->T42_reportid_max = 0;
+ data->T44_address = 0;
+ data->T48_reportid = 0;
+ data->T63_reportid_min = 0;
+ data->T63_reportid_max = 0;
+ data->T100_reportid_min = 0;
+ data->T100_reportid_max = 0;
+ data->max_reportid = 0;
+}
+
+static int mxt_parse_object_table(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int i;
+ u8 reportid;
+ u16 end_address;
+
+ /* Valid Report IDs start counting from 1 */
+ reportid = 1;
+ data->mem_size = 0;
+ for (i = 0; i < data->info->object_num; i++) {
+ struct mxt_object *object = data->object_table + i;
+ u8 min_id, max_id;
+
+ le16_to_cpus(&object->start_address);
+
+ if (object->num_report_ids) {
+ min_id = reportid;
+ reportid += object->num_report_ids *
+ mxt_obj_instances(object);
+ max_id = reportid - 1;
+ } else {
+ min_id = 0;
+ max_id = 0;
+ }
+
+ dev_dbg(&data->client->dev,
+ "T%u Start:%u Size:%u Instances:%u Report IDs:%u-%u\n",
+ object->type, object->start_address,
+ mxt_obj_size(object), mxt_obj_instances(object),
+ min_id, max_id);
+
+ switch (object->type) {
+ case MXT_GEN_MESSAGE_T5:
+ if (data->info->family_id == 0x80) {
+ /* On mXT224 read and discard unused CRC byte
+ * otherwise DMA reads are misaligned */
+ data->T5_msg_size = mxt_obj_size(object);
+ } else {
+ /* CRC not enabled, so skip last byte */
+ data->T5_msg_size = mxt_obj_size(object) - 1;
+ }
+ data->T5_address = object->start_address;
+ case MXT_GEN_COMMAND_T6:
+ data->T6_reportid = min_id;
+ data->T6_address = object->start_address;
+ break;
+ case MXT_GEN_POWER_T7:
+ data->T7_address = object->start_address;
+ break;
+ case MXT_TOUCH_MULTI_T9:
+ /* Only handle messages from first T9 instance */
+ data->T9_reportid_min = min_id;
+ data->T9_reportid_max = min_id +
+ object->num_report_ids - 1;
+ data->num_touchids = object->num_report_ids;
+ break;
+ case MXT_TOUCH_KEYARRAY_T15:
+ data->T15_reportid_min = min_id;
+ data->T15_reportid_max = max_id;
+ break;
+ case MXT_SPT_COMMSCONFIG_T18:
+ data->T18_address = object->start_address;
+ break;
+ case MXT_PROCI_TOUCHSUPPRESSION_T42:
+ data->T42_reportid_min = min_id;
+ data->T42_reportid_max = max_id;
+ break;
+ case MXT_SPT_MESSAGECOUNT_T44:
+ data->T44_address = object->start_address;
+ break;
+ case MXT_SPT_GPIOPWM_T19:
+ data->T19_reportid = min_id;
+ break;
+ case MXT_PROCG_NOISESUPPRESSION_T48:
+ data->T48_reportid = min_id;
+ break;
+ case MXT_PROCI_ACTIVE_STYLUS_T63:
+ /* Only handle messages from first T63 instance */
+ data->T63_reportid_min = min_id;
+ data->T63_reportid_max = min_id;
+ data->num_stylusids = 1;
+ break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ data->T100_reportid_min = min_id;
+ data->T100_reportid_max = max_id;
+ /* first two report IDs reserved */
+ data->num_touchids = object->num_report_ids - 2;
+ break;
+ }
+
+ end_address = object->start_address
+ + mxt_obj_size(object) * mxt_obj_instances(object) - 1;
+
+ if (end_address >= data->mem_size)
+ data->mem_size = end_address + 1;
+ }
+
+ /* Store maximum reportid */
+ data->max_reportid = reportid;
+
+ /* If T44 exists, T5 position has to be directly after */
+ if (data->T44_address && (data->T5_address != data->T44_address + 1)) {
+ dev_err(&client->dev, "Invalid T44 position\n");
+ return -EINVAL;
+ }
+
+ data->msg_buf = kcalloc(data->max_reportid,
+ data->T5_msg_size, GFP_KERNEL);
+ if (!data->msg_buf) {
+ dev_err(&client->dev, "Failed to allocate message buffer\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int mxt_read_info_block(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ size_t size;
+ void *buf;
+ uint8_t num_objects;
+ u32 calculated_crc;
+ u8 *crc_ptr;
+
+ /* If info block already allocated, free it */
+ if (data->raw_info_block != NULL)
+ mxt_free_object_table(data);
+
+ /* Read 7-byte ID information block starting at address 0 */
+ size = sizeof(struct mxt_info);
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ error = __mxt_read_reg(client, 0, size, buf);
+ if (error)
+ goto err_free_mem;
+
+ /* Resize buffer to give space for rest of info block */
+ num_objects = ((struct mxt_info *)buf)->object_num;
+ size += (num_objects * sizeof(struct mxt_object))
+ + MXT_INFO_CHECKSUM_SIZE;
+
+ buf = krealloc(buf, size, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ /* Read rest of info block */
+ error = __mxt_read_reg(client, MXT_OBJECT_START,
+ size - MXT_OBJECT_START,
+ buf + MXT_OBJECT_START);
+ if (error)
+ goto err_free_mem;
+
+ /* Extract & calculate checksum */
+ crc_ptr = buf + size - MXT_INFO_CHECKSUM_SIZE;
+ data->info_crc = crc_ptr[0] | (crc_ptr[1] << 8) | (crc_ptr[2] << 16);
+
+ calculated_crc = mxt_calculate_crc(buf, 0,
+ size - MXT_INFO_CHECKSUM_SIZE);
+
+ /* CRC mismatch can be caused by data corruption due to I2C comms
+ * issue or else device is not using Object Based Protocol */
+ if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) {
+ dev_err(&client->dev,
+ "Info Block CRC error calculated=0x%06X read=0x%06X\n",
+ data->info_crc, calculated_crc);
+ return -EIO;
+ }
+
+ /* Save pointers in device data structure */
+ data->raw_info_block = buf;
+ data->info = (struct mxt_info *)buf;
+ data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
+
+ dev_info(&client->dev,
+ "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
+ data->info->family_id, data->info->variant_id,
+ data->info->version >> 4, data->info->version & 0xf,
+ data->info->build, data->info->object_num);
+
+ /* Parse object table information */
+ error = mxt_parse_object_table(data);
+ if (error) {
+ dev_err(&client->dev, "Error %d reading object table\n", error);
+ mxt_free_object_table(data);
+ return error;
+ }
+
+ return 0;
+
+err_free_mem:
+ kfree(buf);
+ return error;
+}
+
+static int mxt_read_t9_resolution(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct t9_range range;
+ unsigned char orient;
+ struct mxt_object *object;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_RANGE,
+ sizeof(range), &range);
+ if (error)
+ return error;
+
+ le16_to_cpus(range.x);
+ le16_to_cpus(range.y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T9_ORIENT,
+ 1, &orient);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range.x == 0)
+ range.x = 1023;
+
+ if (range.y == 0)
+ range.y = 1023;
+
+ if (orient & MXT_T9_ORIENT_SWITCH) {
+ data->max_x = range.y;
+ data->max_y = range.x;
+ } else {
+ data->max_x = range.x;
+ data->max_y = range.y;
+ }
+
+ dev_info(&client->dev,
+ "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static void mxt_regulator_enable(struct mxt_data *data)
+{
+ gpio_set_value(data->pdata->gpio_reset, 0);
+
+ regulator_enable(data->reg_vdd);
+ regulator_enable(data->reg_avdd);
+ msleep(MXT_REGULATOR_DELAY);
+
+ INIT_COMPLETION(data->bl_completion);
+ gpio_set_value(data->pdata->gpio_reset, 1);
+ mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY);
+}
+
+static void mxt_regulator_disable(struct mxt_data *data)
+{
+ regulator_disable(data->reg_vdd);
+ regulator_disable(data->reg_avdd);
+}
+
+static void mxt_probe_regulators(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int error;
+
+ /* According to maXTouch power sequencing specification, RESET line
+ * must be kept low until some time after regulators come up to
+ * voltage */
+ if (!data->pdata->gpio_reset) {
+ dev_warn(dev, "Must have reset GPIO to use regulator support\n");
+ goto fail;
+ }
+
+ data->reg_vdd = regulator_get(dev, "vdd");
+ if (IS_ERR(data->reg_vdd)) {
+ error = PTR_ERR(data->reg_vdd);
+ dev_err(dev, "Error %d getting vdd regulator\n", error);
+ goto fail;
+ }
+
+ data->reg_avdd = regulator_get(dev, "avdd");
+ if (IS_ERR(data->reg_vdd)) {
+ error = PTR_ERR(data->reg_vdd);
+ dev_err(dev, "Error %d getting avdd regulator\n", error);
+ goto fail_release;
+ }
+
+ data->use_regulator = true;
+ mxt_regulator_enable(data);
+
+ dev_dbg(dev, "Initialised regulators\n");
+ return;
+
+fail_release:
+ regulator_put(data->reg_vdd);
+fail:
+ data->reg_vdd = NULL;
+ data->reg_avdd = NULL;
+ data->use_regulator = false;
+}
+
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct mxt_object *object;
+ u16 range_x, range_y;
+ u8 cfg, tchaux;
+ u8 aux;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_XRANGE,
+ sizeof(range_x), &range_x);
+ if (error)
+ return error;
+
+ le16_to_cpus(range_x);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_YRANGE,
+ sizeof(range_y), &range_y);
+ if (error)
+ return error;
+
+ le16_to_cpus(range_y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_CFG1,
+ 1, &cfg);
+ if (error)
+ return error;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_TCHAUX,
+ 1, &tchaux);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ if (range_y == 0)
+ range_y = 1023;
+
+ if (cfg & MXT_T100_CFG_SWITCHXY) {
+ data->max_x = range_y;
+ data->max_y = range_x;
+ } else {
+ data->max_x = range_x;
+ data->max_y = range_y;
+ }
+
+ /* allocate aux bytes */
+ aux = 6;
+
+ if (tchaux & MXT_T100_TCHAUX_VECT)
+ data->t100_aux_vect = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AMPL)
+ data->t100_aux_ampl = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AREA)
+ data->t100_aux_area = aux++;
+
+ dev_info(&client->dev,
+ "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
+static int mxt_input_open(struct input_dev *dev);
+static void mxt_input_close(struct input_dev *dev);
+
+static int mxt_initialize_t100_input_device(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev;
+ int error;
+
+ error = mxt_read_t100_config(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "atmel_mxt_ts T100 touchscreen";
+
+ input_dev->phys = data->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &data->client->dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, data->max_y, 0, 0);
+
+ if (data->t100_aux_ampl)
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
+
+ /* For multi touch */
+ error = input_mt_init_slots(input_dev, data->num_touchids);
+ if (error) {
+ dev_err(dev, "Error %d initialising slots\n", error);
+ goto err_free_mem;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+
+ if (data->t100_aux_area)
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+
+ if (data->t100_aux_ampl)
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+
+ if (data->t100_aux_vect)
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Error %d registering input device\n", error);
+ goto err_free_mem;
+ }
+
+ data->input_dev = input_dev;
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int mxt_initialize_t9_input_device(struct mxt_data *data);
+static int mxt_configure_objects(struct mxt_data *data);
+
+static int mxt_initialize(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ bool alt_bootloader_addr = false;
+ bool retry = false;
+
+retry_info:
+ error = mxt_read_info_block(data);
+ if (error) {
+retry_bootloader:
+ error = mxt_probe_bootloader(data, alt_bootloader_addr);
+ if (error) {
+ if (alt_bootloader_addr) {
+ /* Chip is not in appmode or bootloader mode */
+ return error;
+ }
+
+ dev_info(&client->dev, "Trying alternate bootloader address\n");
+ alt_bootloader_addr = true;
+ goto retry_bootloader;
+ } else {
+ if (retry) {
+ dev_err(&client->dev,
+ "Could not recover device from "
+ "bootloader mode\n");
+ /* this is not an error state, we can reflash
+ * from here */
+ data->in_bootloader = true;
+ return 0;
+ }
+
+ /* Attempt to exit bootloader into app mode */
+ mxt_send_bootloader_cmd(data, false);
+ msleep(MXT_FW_RESET_TIME);
+ retry = true;
+ goto retry_info;
+ }
+ }
+
+ error = mxt_check_retrigen(data);
+ if (error)
+ return error;
+
+ error = mxt_acquire_irq(data);
+ if (error)
+ return error;
+
+ error = mxt_configure_objects(data);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int mxt_configure_objects(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+
+ error = mxt_debug_msg_init(data);
+ if (error)
+ return error;
+
+ error = mxt_init_t7_power_cfg(data);
+ if (error) {
+ dev_err(&client->dev, "Failed to initialize power cfg\n");
+ return error;
+ }
+
+ /* Check register init values */
+ error = mxt_check_reg_init(data);
+ if (error) {
+ dev_err(&client->dev, "Error %d initialising configuration\n",
+ error);
+ return error;
+ }
+
+ if (data->T9_reportid_min) {
+ error = mxt_initialize_t9_input_device(data);
+ if (error)
+ return error;
+ } else if (data->T100_reportid_min) {
+ error = mxt_initialize_t100_input_device(data);
+ if (error)
+ return error;
+ } else {
+ dev_warn(&client->dev, "No touch object detected\n");
+ }
+
+ data->enable_reporting = true;
+ return 0;
+}
+
+/* Firmware Version is returned as Major.Minor.Build */
+static ssize_t mxt_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
+ data->info->version >> 4, data->info->version & 0xf,
+ data->info->build);
+}
+
+/* Hardware Version is returned as FamilyID.VariantID */
+static ssize_t mxt_hw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
+ data->info->family_id, data->info->variant_id);
+}
+
+static ssize_t mxt_show_instance(char *buf, int count,
+ struct mxt_object *object, int instance,
+ const u8 *val)
+{
+ int i;
+
+ if (mxt_obj_instances(object) > 1)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "Instance %u\n", instance);
+
+ for (i = 0; i < mxt_obj_size(object); i++)
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
+ count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
+
+ return count;
+}
+
+static ssize_t mxt_object_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ struct mxt_object *object;
+ int count = 0;
+ int i, j;
+ int error;
+ u8 *obuf;
+
+ /* Pre-allocate buffer large enough to hold max sized object. */
+ obuf = kmalloc(256, GFP_KERNEL);
+ if (!obuf)
+ return -ENOMEM;
+
+ error = 0;
+ for (i = 0; i < data->info->object_num; i++) {
+ object = data->object_table + i;
+
+ if (!mxt_object_readable(object->type))
+ continue;
+
+ count += scnprintf(buf + count, PAGE_SIZE - count,
+ "T%u:\n", object->type);
+
+ for (j = 0; j < mxt_obj_instances(object); j++) {
+ u16 size = mxt_obj_size(object);
+ u16 addr = object->start_address + j * size;
+
+ error = __mxt_read_reg(data->client, addr, size, obuf);
+ if (error)
+ goto done;
+
+ count = mxt_show_instance(buf, count, object, j, obuf);
+ }
+ }
+
+done:
+ kfree(obuf);
+ return error ?: count;
+}
+
+static int mxt_check_firmware_format(struct device *dev,
+ const struct firmware *fw)
+{
+ unsigned int pos = 0;
+ char c;
+
+ while (pos < fw->size) {
+ c = *(fw->data + pos);
+
+ if (c < '0' || (c > '9' && c < 'A') || c > 'F')
+ return 0;
+
+ pos++;
+ }
+
+ /* To convert file try
+ * xxd -r -p mXTXXX__APP_VX-X-XX.enc > maxtouch.fw */
+ dev_err(dev, "Aborting: firmware file must be in binary format\n");
+
+ return -1;
+}
+
+static int mxt_load_fw(struct device *dev)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ const struct firmware *fw = NULL;
+ unsigned int frame_size;
+ unsigned int pos = 0;
+ unsigned int retry = 0;
+ unsigned int frame = 0;
+ int ret;
+
+ ret = request_firmware(&fw, data->fw_name, dev);
+ if (ret) {
+ dev_err(dev, "Unable to open firmware %s\n", data->fw_name);
+ return ret;
+ }
+
+ /* Check for incorrect enc file */
+ ret = mxt_check_firmware_format(dev, fw);
+ if (ret)
+ goto release_firmware;
+
+ if (data->suspended) {
+ if (data->use_regulator)
+ mxt_regulator_enable(data);
+
+ enable_irq(data->irq);
+ data->suspended = false;
+ }
+
+ if (!data->in_bootloader) {
+ /* Change to the bootloader mode */
+ data->in_bootloader = true;
+
+ ret = mxt_t6_command(data, MXT_COMMAND_RESET,
+ MXT_BOOT_VALUE, false);
+ if (ret)
+ goto release_firmware;
+
+ msleep(MXT_RESET_TIME);
+
+ /* At this stage, do not need to scan since we know
+ * family ID */
+ ret = mxt_lookup_bootloader_address(data, 0);
+ if (ret)
+ goto release_firmware;
+ } else {
+ enable_irq(data->irq);
+ }
+
+ mxt_free_object_table(data);
+ INIT_COMPLETION(data->bl_completion);
+
+ ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false);
+ if (ret) {
+ /* Bootloader may still be unlocked from previous update
+ * attempt */
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false);
+ if (ret)
+ goto disable_irq;
+ } else {
+ dev_info(dev, "Unlocking bootloader\n");
+
+ /* Unlock bootloader */
+ ret = mxt_send_bootloader_cmd(data, true);
+ if (ret)
+ goto disable_irq;
+ }
+
+ while (pos < fw->size) {
+ ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true);
+ if (ret)
+ goto disable_irq;
+
+ frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1));
+
+ /* Take account of CRC bytes */
+ frame_size += 2;
+
+ /* Write one frame to device */
+ ret = mxt_bootloader_write(data, fw->data + pos, frame_size);
+ if (ret)
+ goto disable_irq;
+
+ ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true);
+ if (ret) {
+ retry++;
+
+ /* Back off by 20ms per retry */
+ msleep(retry * 20);
+
+ if (retry > 20) {
+ dev_err(dev, "Retry count exceeded\n");
+ goto disable_irq;
+ }
+ } else {
+ retry = 0;
+ pos += frame_size;
+ frame++;
+ }
+
+ if (frame % 50 == 0)
+ dev_info(dev, "Sent %d frames, %d/%zd bytes\n",
+ frame, pos, fw->size);
+ }
+
+ /* Wait for flash. */
+ ret = mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_RESET_TIME);
+ if (ret)
+ goto disable_irq;
+
+ dev_info(dev, "Sent %d frames, %zd bytes\n", frame, pos);
+
+ /* Wait for device to reset. Some bootloader versions do not assert
+ * the CHG line after bootloading has finished, so ignore error */
+ mxt_wait_for_completion(data, &data->bl_completion,
+ MXT_FW_RESET_TIME);
+
+ data->in_bootloader = false;
+
+disable_irq:
+ disable_irq(data->irq);
+release_firmware:
+ release_firmware(fw);
+ return ret;
+}
+
+static int mxt_update_file_name(struct device *dev, char **file_name,
+ const char *buf, size_t count)
+{
+ char *file_name_tmp;
+
+ /* Simple sanity check */
+ if (count > 64) {
+ dev_warn(dev, "File name too long\n");
+ return -EINVAL;
+ }
+
+ file_name_tmp = krealloc(*file_name, count + 1, GFP_KERNEL);
+ if (!file_name_tmp) {
+ dev_warn(dev, "no memory\n");
+ return -ENOMEM;
+ }
+
+ *file_name = file_name_tmp;
+ memcpy(*file_name, buf, count);
+
+ /* Echo into the sysfs entry may append newline at the end of buf */
+ if (buf[count - 1] == '\n')
+ (*file_name)[count - 1] = '\0';
+ else
+ (*file_name)[count] = '\0';
+
+ return 0;
+}
+
+static ssize_t mxt_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int error;
+
+ error = mxt_update_file_name(dev, &data->fw_name, buf, count);
+ if (error)
+ return error;
+
+ error = mxt_load_fw(dev);
+ if (error) {
+ dev_err(dev, "The firmware update failed(%d)\n", error);
+ count = error;
+ } else {
+ dev_info(dev, "The firmware update succeeded\n");
+
+ data->suspended = false;
+
+ error = mxt_initialize(data);
+ if (error)
+ return error;
+ }
+
+ return count;
+}
+
+static ssize_t mxt_update_cfg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret;
+
+ if (data->in_bootloader) {
+ dev_err(dev, "Not in appmode\n");
+ return -EINVAL;
+ }
+
+ ret = mxt_update_file_name(dev, &data->cfg_name, buf, count);
+ if (ret)
+ return ret;
+
+ data->enable_reporting = false;
+ mxt_free_input_device(data);
+
+ if (data->suspended) {
+ if (data->use_regulator)
+ mxt_regulator_enable(data);
+ else
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+ mxt_acquire_irq(data);
+
+ data->suspended = false;
+ }
+
+ ret = mxt_configure_objects(data);
+ if (ret)
+ goto out;
+
+ ret = count;
+out:
+ return ret;
+}
+
+static ssize_t mxt_debug_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ char c;
+
+ c = data->debug_enabled ? '1' : '0';
+ return scnprintf(buf, PAGE_SIZE, "%c\n", c);
+}
+
+static ssize_t mxt_debug_notify_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "0\n");
+}
+
+static ssize_t mxt_debug_v2_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int i;
+
+ if (sscanf(buf, "%u", &i) == 1 && i < 2) {
+ if (i == 1)
+ mxt_debug_msg_enable(data);
+ else
+ mxt_debug_msg_disable(data);
+
+ return count;
+ } else {
+ dev_dbg(dev, "debug_enabled write error\n");
+ return -EINVAL;
+ }
+}
+
+static ssize_t mxt_debug_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int i;
+
+ if (sscanf(buf, "%u", &i) == 1 && i < 2) {
+ data->debug_enabled = (i == 1);
+
+ dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled");
+ return count;
+ } else {
+ dev_dbg(dev, "debug_enabled write error\n");
+ return -EINVAL;
+ }
+}
+
+static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off,
+ size_t *count)
+{
+ if (off >= data->mem_size)
+ return -EIO;
+
+ if (off + *count > data->mem_size)
+ *count = data->mem_size - off;
+
+ if (*count > MXT_MAX_BLOCK_WRITE)
+ *count = MXT_MAX_BLOCK_WRITE;
+
+ return 0;
+}
+
+static ssize_t mxt_mem_access_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mxt_check_mem_access_params(data, off, &count);
+ if (ret < 0)
+ return ret;
+
+ if (count > 0)
+ ret = __mxt_read_reg(data->client, off, count, buf);
+
+ return ret == 0 ? count : ret;
+}
+
+static ssize_t mxt_mem_access_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf, loff_t off,
+ size_t count)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct mxt_data *data = dev_get_drvdata(dev);
+ int ret = 0;
+
+ ret = mxt_check_mem_access_params(data, off, &count);
+ if (ret < 0)
+ return ret;
+
+ if (count > 0)
+ ret = __mxt_write_reg(data->client, off, count, buf);
+
+ return ret == 0 ? count : 0;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
+static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
+static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
+static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store);
+static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store);
+static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL);
+static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show,
+ mxt_debug_enable_store);
+
+static struct attribute *mxt_attrs[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_hw_version.attr,
+ &dev_attr_object.attr,
+ &dev_attr_update_fw.attr,
+ &dev_attr_update_cfg.attr,
+ &dev_attr_debug_enable.attr,
+ &dev_attr_debug_v2_enable.attr,
+ &dev_attr_debug_notify.attr,
+ NULL
+};
+
+static const struct attribute_group mxt_attr_group = {
+ .attrs = mxt_attrs,
+};
+
+static void mxt_reset_slots(struct mxt_data *data)
+{
+ struct input_dev *input_dev = data->input_dev;
+ unsigned int num_mt_slots;
+ int id;
+
+ num_mt_slots = data->num_touchids + data->num_stylusids;
+
+ for (id = 0; id < num_mt_slots; id++) {
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0);
+ }
+
+ mxt_input_sync(input_dev);
+}
+
+static void mxt_start(struct mxt_data *data)
+{
+ if (!data->suspended || data->in_bootloader)
+ return;
+
+ if (data->use_regulator) {
+ mxt_regulator_enable(data);
+ } else {
+ /* Discard any messages still in message buffer from before
+ * chip went to sleep */
+ mxt_process_messages_until_invalid(data);
+
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
+
+ /* Recalibrate since chip has been in deep sleep */
+ mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
+ }
+
+ mxt_acquire_irq(data);
+ data->enable_reporting = true;
+ data->suspended = false;
+}
+
+static void mxt_stop(struct mxt_data *data)
+{
+ if (data->suspended || data->in_bootloader)
+ return;
+
+ data->enable_reporting = false;
+ disable_irq(data->irq);
+
+ if (data->use_regulator)
+ mxt_regulator_disable(data);
+ else
+ mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
+
+ mxt_reset_slots(data);
+ data->suspended = true;
+}
+
+static int mxt_input_open(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_start(data);
+
+ return 0;
+}
+
+static void mxt_input_close(struct input_dev *dev)
+{
+ struct mxt_data *data = input_get_drvdata(dev);
+
+ mxt_stop(data);
+}
+
+static int mxt_handle_pdata(struct mxt_data *data)
+{
+ data->pdata = dev_get_platdata(&data->client->dev);
+
+ /* Use provided platform data if present */
+ if (data->pdata) {
+ if (data->pdata->cfg_name)
+ mxt_update_file_name(&data->client->dev,
+ &data->cfg_name,
+ data->pdata->cfg_name,
+ strlen(data->pdata->cfg_name));
+
+ return 0;
+ }
+
+ data->pdata = kzalloc(sizeof(*data->pdata), GFP_KERNEL);
+ if (!data->pdata) {
+ dev_err(&data->client->dev, "Failed to allocate pdata\n");
+ return -ENOMEM;
+ }
+
+ /* Set default parameters */
+ data->pdata->irqflags = IRQF_TRIGGER_FALLING;
+
+ return 0;
+}
+
+static int mxt_initialize_t9_input_device(struct mxt_data *data)
+{
+ struct device *dev = &data->client->dev;
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct input_dev *input_dev;
+ int error;
+ unsigned int num_mt_slots;
+ int i;
+
+ error = mxt_read_t9_resolution(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "Atmel maXTouch Touchscreen";
+ input_dev->phys = data->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = dev;
+ input_dev->open = mxt_input_open;
+ input_dev->close = mxt_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+
+ if (pdata->t19_num_keys) {
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
+ input_set_capability(input_dev, EV_KEY,
+ pdata->t19_keymap[i]);
+
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+
+ input_dev->name = "Atmel maXTouch Touchpad";
+ }
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 255, 0, 0);
+
+ /* For multi touch */
+ num_mt_slots = data->num_touchids + data->num_stylusids;
+ error = input_mt_init_slots(input_dev, num_mt_slots);
+ if (error) {
+ dev_err(dev, "Error %d initialising slots\n", error);
+ goto err_free_mem;
+ }
+
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, data->max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+
+ /* For T63 active stylus */
+ if (data->T63_reportid_min) {
+ input_set_capability(input_dev, EV_KEY, BTN_STYLUS);
+ input_set_capability(input_dev, EV_KEY, BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ }
+
+ /* For T15 key array */
+ if (data->T15_reportid_min) {
+ data->t15_keystatus = 0;
+
+ for (i = 0; i < data->pdata->t15_num_keys; i++)
+ input_set_capability(input_dev, EV_KEY,
+ data->pdata->t15_keymap[i]);
+ }
+
+ input_set_drvdata(input_dev, data);
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(dev, "Error %d registering input device\n", error);
+ goto err_free_mem;
+ }
+
+ data->input_dev = input_dev;
+
+ return 0;
+
+err_free_mem:
+ input_free_device(input_dev);
+ return error;
+}
+
+static int __devinit mxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mxt_data *data;
+ int error;
+
+ data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
+ client->adapter->nr, client->addr);
+
+ data->client = client;
+ data->irq = client->irq;
+ i2c_set_clientdata(client, data);
+
+ error = mxt_handle_pdata(data);
+ if (error)
+ goto err_free_mem;
+
+ init_completion(&data->bl_completion);
+ init_completion(&data->reset_completion);
+ init_completion(&data->crc_completion);
+ mutex_init(&data->debug_msg_lock);
+
+ error = request_threaded_irq(data->irq, NULL, mxt_interrupt,
+ data->pdata->irqflags | IRQF_ONESHOT,
+ client->name, data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_pdata;
+ }
+
+ mxt_probe_regulators(data);
+
+ disable_irq(data->irq);
+
+ error = mxt_initialize(data);
+ if (error)
+ goto err_free_irq;
+
+ error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
+ if (error) {
+ dev_err(&client->dev, "Failure %d creating sysfs group\n",
+ error);
+ goto err_free_object;
+ }
+
+ sysfs_bin_attr_init(&data->mem_access_attr);
+ data->mem_access_attr.attr.name = "mem_access";
+ data->mem_access_attr.attr.mode = S_IRUGO | S_IWUSR;
+ data->mem_access_attr.read = mxt_mem_access_read;
+ data->mem_access_attr.write = mxt_mem_access_write;
+ data->mem_access_attr.size = data->mem_size;
+
+ if (sysfs_create_bin_file(&client->dev.kobj,
+ &data->mem_access_attr) < 0) {
+ dev_err(&client->dev, "Failed to create %s\n",
+ data->mem_access_attr.attr.name);
+ goto err_remove_sysfs_group;
+ }
+
+ return 0;
+
+err_remove_sysfs_group:
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+err_free_object:
+ mxt_free_object_table(data);
+err_free_irq:
+ free_irq(data->irq, data);
+err_free_pdata:
+ if (!dev_get_platdata(&data->client->dev))
+ kfree(data->pdata);
+err_free_mem:
+ kfree(data);
+ return error;
+}
+
+static int __devexit mxt_remove(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ if (data->mem_access_attr.attr.name)
+ sysfs_remove_bin_file(&client->dev.kobj,
+ &data->mem_access_attr);
+
+ sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
+ free_irq(data->irq, data);
+ regulator_put(data->reg_avdd);
+ regulator_put(data->reg_vdd);
+ mxt_free_object_table(data);
+ if (!dev_get_platdata(&data->client->dev))
+ kfree(data->pdata);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mxt_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_stop(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int mxt_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mxt_data *data = i2c_get_clientdata(client);
+ struct input_dev *input_dev = data->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ mxt_start(data);
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
+
+static void mxt_shutdown(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ disable_irq(data->irq);
+}
+
+static const struct i2c_device_id mxt_id[] = {
+ { "qt602240_ts", 0 },
+ { "atmel_mxt_ts", 0 },
+ { "atmel_mxt_tp", 0 },
+ { "mXT224", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mxt_id);
+
+static struct i2c_driver mxt_driver = {
+ .driver = {
+ .name = "atmel_mxt_ts",
+ .owner = THIS_MODULE,
+ .pm = &mxt_pm_ops,
+ },
+ .probe = mxt_probe,
+ .remove = __devexit_p(mxt_remove),
+ .shutdown = mxt_shutdown,
+ .id_table = mxt_id,
+};
+
+static int __init mxt_init(void)
+{
+ return i2c_add_driver(&mxt_driver);
+}
+
+static void __exit mxt_exit(void)
+{
+ i2c_del_driver(&mxt_driver);
+}
+
+module_init(mxt_init);
+module_exit(mxt_exit);
+
+/* Module information */
+MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
+MODULE_DESCRIPTION("Atmel maXTouch Touchscreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index b658cc52b920..897e8ff92fc2 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -2121,15 +2121,17 @@ static ssize_t mxt_secure_touch_show(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u", val);
}
-static DEVICE_ATTR(secure_touch_enable, 0666, mxt_secure_touch_enable_show,
- mxt_secure_touch_enable_store);
-static DEVICE_ATTR(secure_touch, 0444, mxt_secure_touch_show, NULL);
+static DEVICE_ATTR(secure_touch_enable, S_IRUGO | S_IWUSR | S_IWGRP ,
+ mxt_secure_touch_enable_show,
+ mxt_secure_touch_enable_store);
+static DEVICE_ATTR(secure_touch, S_IRUGO, mxt_secure_touch_show, NULL);
#endif
-static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
-static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
-static DEVICE_ATTR(force_cfg_update, 0664, NULL, mxt_force_cfg_update_store);
-static DEVICE_ATTR(update_object_byte, 0664, NULL,
+static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR | S_IWGRP , NULL, mxt_update_fw_store);
+static DEVICE_ATTR(force_cfg_update, S_IWUSR | S_IWGRP ,
+ NULL, mxt_force_cfg_update_store);
+static DEVICE_ATTR(update_object_byte, S_IWUSR | S_IWGRP, NULL,
mxt_update_single_byte_store);
static struct attribute *mxt_attrs[] = {
diff --git a/drivers/input/touchscreen/ft5x06_ts.c b/drivers/input/touchscreen/ft5x06_ts.c
index d95c68c2fa80..2f781f45acbf 100644
--- a/drivers/input/touchscreen/ft5x06_ts.c
+++ b/drivers/input/touchscreen/ft5x06_ts.c
@@ -1550,8 +1550,9 @@ static int ft5x06_ts_probe(struct i2c_client *client,
data->family_id = pdata->family_id;
err = request_threaded_irq(client->irq, NULL,
- ft5x06_ts_interrupt, pdata->irqflags,
- client->dev.driver->name, data);
+ ft5x06_ts_interrupt,
+ pdata->irqflags | IRQF_ONESHOT,
+ client->dev.driver->name, data);
if (err) {
dev_err(&client->dev, "request irq failed\n");
goto free_reset_gpio;
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.c b/drivers/input/touchscreen/gt9xx/gt9xx.c
index 3c040a85c280..d819d23c56ca 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.c
@@ -605,26 +605,6 @@ exit_work_func:
/*******************************************************
Function:
- Timer interrupt service routine for polling mode.
-Input:
- timer: timer struct pointer
-Output:
- Timer work mode.
- HRTIMER_NORESTART: no restart mode
-*********************************************************/
-static enum hrtimer_restart goodix_ts_timer_handler(struct hrtimer *timer)
-{
- struct goodix_ts_data
- *ts = container_of(timer, struct goodix_ts_data, timer);
-
- queue_work(ts->goodix_wq, &ts->work);
- hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
- HRTIMER_MODE_REL);
- return HRTIMER_NORESTART;
-}
-
-/*******************************************************
-Function:
External interrupt service routine for interrupt mode.
Input:
irq: interrupt number.
@@ -740,16 +720,13 @@ static s8 gtp_enter_doze(struct goodix_ts_data *ts)
return ret;
}
#else
-/*******************************************************
-Function:
- Enter sleep mode.
-Input:
- ts: private data.
-Output:
- Executive outcomes.
- >0: succeed, otherwise failed.
-*******************************************************/
-static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
+/**
+ * gtp_enter_sleep - Enter sleep mode
+ * @ts: driver private data
+ *
+ * Returns zero on success, else an error.
+ */
+static u8 gtp_enter_sleep(struct goodix_ts_data *ts)
{
int ret = -1;
s8 retry = 0;
@@ -771,16 +748,16 @@ static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
ret = goodix_power_off(ts);
if (ret) {
dev_err(&ts->client->dev, "GTP power off failed.\n");
- return 0;
+ return ret;
}
- return 1;
+ return 0;
} else {
usleep(5000);
while (retry++ < GTP_I2C_RETRY_5) {
ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
if (ret == 1) {
dev_dbg(&ts->client->dev, "GTP enter sleep!");
- return ret;
+ return 0;
}
msleep(20);
}
@@ -1204,7 +1181,7 @@ Output:
*******************************************************/
static int gtp_request_irq(struct goodix_ts_data *ts)
{
- int ret;
+ int ret = 0;
const u8 irq_table[] = GTP_IRQ_TAB;
ret = request_threaded_irq(ts->client->irq, NULL,
@@ -1212,21 +1189,12 @@ static int gtp_request_irq(struct goodix_ts_data *ts)
irq_table[ts->int_trigger_type],
ts->client->name, ts);
if (ret) {
- dev_err(&ts->client->dev, "Request IRQ failed!ERRNO:%d.\n",
- ret);
- gpio_direction_input(ts->pdata->irq_gpio);
-
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- ts->timer.function = goodix_ts_timer_handler;
- hrtimer_start(&ts->timer, ktime_set(1, 0),
- HRTIMER_MODE_REL);
ts->use_irq = false;
return ret;
} else {
gtp_irq_disable(ts);
ts->use_irq = true;
- return 0;
+ return ret;
}
}
@@ -1903,8 +1871,8 @@ static int goodix_ts_probe(struct i2c_client *client,
INIT_WORK(&ts->work, goodix_ts_work_func);
ret = gtp_request_irq(ts);
- if (ret < 0)
- dev_info(&client->dev, "GTP works in polling mode.\n");
+ if (ret)
+ dev_info(&client->dev, "GTP request irq failed %d.\n", ret);
else
dev_info(&client->dev, "GTP works in interrupt mode.\n");
@@ -2084,7 +2052,7 @@ static int goodix_ts_suspend(struct device *dev)
ret = gtp_enter_sleep(ts);
#endif
- if (ret <= 0)
+ if (ret < 0)
dev_err(&ts->client->dev, "GTP early suspend failed.\n");
/* to avoid waking up while not sleeping,
* delay 48 + 10ms to ensure reliability
@@ -2327,8 +2295,15 @@ static void gtp_esd_check_func(struct work_struct *work)
}
#endif
-static SIMPLE_DEV_PM_OPS(goodix_ts_dev_pm_ops, goodix_ts_suspend,
- goodix_ts_resume);
+#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND))
+static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
+ .suspend = goodix_ts_suspend,
+ .resume = goodix_ts_resume,
+};
+#else
+static const struct dev_pm_ops goodix_ts_dev_pm_ops = {
+};
+#endif
static const struct i2c_device_id goodix_ts_id[] = {
{ GTP_I2C_NAME, 0 },
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx.h b/drivers/input/touchscreen/gt9xx/gt9xx.h
index 6e59b29c9215..4d656ddc6056 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx.h
+++ b/drivers/input/touchscreen/gt9xx/gt9xx.h
@@ -111,11 +111,6 @@ extern u16 total_len;
#define GTP_CHANGE_X2Y 0
#define GTP_DRIVER_SEND_CFG 1
#define GTP_HAVE_TOUCH_KEY 1
-
-/* auto updated by head_fw_array in gt9xx_firmware.h,
- * function together with CONFIG_GT9XX_TOUCHPANEL_UPDATE */
-#define GTP_HEADER_FW_UPDATE 0
-
#define GTP_ESD_PROTECT 0
#define GTP_WITH_PEN 0
diff --git a/drivers/input/touchscreen/gt9xx/gt9xx_update.c b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
index d7ca2a8992d1..71e8a55a72ff 100644
--- a/drivers/input/touchscreen/gt9xx/gt9xx_update.c
+++ b/drivers/input/touchscreen/gt9xx/gt9xx_update.c
@@ -36,11 +36,6 @@
#include <linux/workqueue.h>
#include <linux/kernel.h>
-#if GTP_HEADER_FW_UPDATE
-#include <linux/namei.h>
-#include <linux/mount.h>
-#endif
-
#define FIRMWARE_NAME_LEN_MAX 256
#define GUP_REG_HW_INFO 0x4220
@@ -641,26 +636,6 @@ static s8 gup_update_config(struct i2c_client *client,
return ret;
}
-#if GTP_HEADER_FW_UPDATE
-static u32 gup_get firmware_file(struct i2c_client,
- struct st_update_msg *msg, u8 *path)
-{
- if (sizeiof(header_fw_array) < (FW_HEAD_LENGTH +
- FW_SECTION_LENGTH * 4 +
- FW_DSP_ISP_LENGTH +
- FW_DSP_LENGTH +
- FW_BOOT_LENGTH)) {
- dev_err(&client->dev,
- "INVALID header_fw_array!");
- return -EINVAL;
- }
- msg->fw_data = (u8 *)header_fw_array;
- msg->fw_len = sizeof(header_fw_array);
- dev_dbg(&client->dev, "Found firmware from header file, len=%d",
- msg->fw_len);
- return 0;
-}
-#else
static s32 gup_get_firmware_file(struct i2c_client *client,
struct st_update_msg *msg, u8 *path)
{
@@ -690,7 +665,6 @@ static s32 gup_get_firmware_file(struct i2c_client *client,
release_firmware(fw);
return 0;
}
-#endif
static u8 gup_check_firmware_name(struct i2c_client *client,
u8 **path_p)
diff --git a/drivers/input/touchscreen/synaptics_i2c_rmi4.c b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
index f859b98756a5..adfb72c29569 100644
--- a/drivers/input/touchscreen/synaptics_i2c_rmi4.c
+++ b/drivers/input/touchscreen/synaptics_i2c_rmi4.c
@@ -2424,6 +2424,8 @@ static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data)
}
}
+ INIT_LIST_HEAD(&rmi->support_fn_list);
+
retval = synaptics_rmi4_query_device(rmi4_data);
if (retval < 0) {
dev_err(&rmi4_data->i2c_client->dev,
diff --git a/drivers/iommu/msm_iommu-v0.c b/drivers/iommu/msm_iommu-v0.c
index d1cd6994f78e..bc59d6ca5192 100644
--- a/drivers/iommu/msm_iommu-v0.c
+++ b/drivers/iommu/msm_iommu-v0.c
@@ -31,9 +31,10 @@
#include <mach/iommu_hw-v0.h>
#include <mach/msm_iommu_priv.h>
#include <mach/iommu.h>
-#include <mach/msm_smem.h>
#include <mach/msm_bus.h>
+#include <soc/qcom/smem.h>
+
/* Sharability attributes of MSM IOMMU mappings */
#define MSM_IOMMU_ATTR_NON_SH 0x0
#define MSM_IOMMU_ATTR_SH 0x4
diff --git a/drivers/iommu/msm_iommu-v1.c b/drivers/iommu/msm_iommu-v1.c
index d7552586e8c0..d47210099320 100644
--- a/drivers/iommu/msm_iommu-v1.c
+++ b/drivers/iommu/msm_iommu-v1.c
@@ -708,7 +708,6 @@ static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
goto unlock;
}
}
- SET_MICRO_MMU_CTRL_RESERVED(iommu_drvdata->base, 0x3);
program_iommu_bfb_settings(iommu_drvdata->base,
iommu_drvdata->bfb_settings);
set_m2v = true;
diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c
index d9c24ae8d218..bf3bf4b5e95f 100644
--- a/drivers/iommu/msm_iommu_dev-v0.c
+++ b/drivers/iommu/msm_iommu_dev-v0.c
@@ -185,11 +185,11 @@ static int msm_iommu_parse_dt(struct platform_device *pdev,
}
drvdata->glb_base = drvdata->base;
- if (!of_property_read_u32(pdev->dev.of_node, "qti,glb-offset",
+ if (!of_property_read_u32(pdev->dev.of_node, "qcom,glb-offset",
&glb_offset)) {
drvdata->glb_base += glb_offset;
} else {
- pr_err("%s: Missing property qti,glb-offset\n", __func__);
+ pr_err("%s: Missing property qcom,glb-offset\n", __func__);
ret = -EINVAL;
goto fail;
}
@@ -206,7 +206,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev,
}
needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
- "qti,needs-alt-core-clk");
+ "qcom,needs-alt-core-clk");
ret = __get_clocks(pdev, drvdata, needs_alt_core_clk);
@@ -217,7 +217,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev,
drvdata->ttbr_split = 0;
drvdata->needs_rem_spinlock = of_property_read_bool(pdev->dev.of_node,
- "qti,msm-enable-remote-spinlock");
+ "qcom,msm-enable-remote-spinlock");
if (drvdata->needs_rem_spinlock)
pr_info("%s enabled remote spinlock\n", drvdata->name);
@@ -288,24 +288,24 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
ret = of_property_read_u32(pdev->dev.of_node,
- "qti,iommu-pmu-ngroups",
+ "qcom,iommu-pmu-ngroups",
&pmon_info->num_groups);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-ngroups\n");
+ pr_err("Error reading qcom,iommu-pmu-ngroups\n");
goto fail;
}
ret = of_property_read_u32(pdev->dev.of_node,
- "qti,iommu-pmu-ncounters",
+ "qcom,iommu-pmu-ncounters",
&pmon_info->num_counters);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-ncounters\n");
+ pr_err("Error reading qcom,iommu-pmu-ncounters\n");
goto fail;
}
if (!of_get_property(pdev->dev.of_node,
- "qti,iommu-pmu-event-classes",
+ "qcom,iommu-pmu-event-classes",
&cls_prop_size)) {
- pr_err("Error reading qti,iommu-pmu-event-classes\n");
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return -EINVAL;
}
@@ -320,11 +320,11 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
ret = of_property_read_u32_array(pdev->dev.of_node,
- "qti,iommu-pmu-event-classes",
+ "qcom,iommu-pmu-event-classes",
pmon_info->event_cls_supported,
pmon_info->nevent_cls_supported);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-event-classes\n");
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return ret;
}
} else {
@@ -516,7 +516,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
goto out;
}
- if (!of_get_property(pdev->dev.of_node, "qti,iommu-ctx-mids",
+ if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-mids",
&nmid_array_size)) {
pr_err("Could not find iommu-ctx-mids property\n");
ret = -EINVAL;
@@ -530,7 +530,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
}
nmid = nmid_array_size / sizeof(*ctx_drvdata->sids);
- if (of_property_read_u32_array(pdev->dev.of_node, "qti,iommu-ctx-mids",
+ if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-mids",
ctx_drvdata->sids, nmid)) {
pr_err("Could not find iommu-ctx-mids property\n");
ret = -EINVAL;
@@ -666,7 +666,7 @@ static int msm_iommu_ctx_remove(struct platform_device *pdev)
static struct of_device_id msm_iommu_match_table[] = {
- { .compatible = "qti,msm-smmu-v0", },
+ { .compatible = "qcom,msm-smmu-v0", },
{}
};
@@ -680,7 +680,7 @@ static struct platform_driver msm_iommu_driver = {
};
static struct of_device_id msm_iommu_v0_ctx_match_table[] = {
- { .compatible = "qti,msm-smmu-v0-ctx", },
+ { .compatible = "qcom,msm-smmu-v0-ctx", },
{}
};
diff --git a/drivers/iommu/msm_iommu_dev-v1.c b/drivers/iommu/msm_iommu_dev-v1.c
index 3200d7e51c25..0799f8792864 100644
--- a/drivers/iommu/msm_iommu_dev-v1.c
+++ b/drivers/iommu/msm_iommu_dev-v1.c
@@ -33,11 +33,11 @@
static struct of_device_id msm_iommu_ctx_match_table[];
#ifdef CONFIG_IOMMU_LPAE
-static const char *BFB_REG_NODE_NAME = "qti,iommu-lpae-bfb-regs";
-static const char *BFB_DATA_NODE_NAME = "qti,iommu-lpae-bfb-data";
+static const char *BFB_REG_NODE_NAME = "qcom,iommu-lpae-bfb-regs";
+static const char *BFB_DATA_NODE_NAME = "qcom,iommu-lpae-bfb-data";
#else
-static const char *BFB_REG_NODE_NAME = "qti,iommu-bfb-regs";
-static const char *BFB_DATA_NODE_NAME = "qti,iommu-bfb-data";
+static const char *BFB_REG_NODE_NAME = "qcom,iommu-bfb-regs";
+static const char *BFB_DATA_NODE_NAME = "qcom,iommu-bfb-data";
#endif
static int msm_iommu_parse_bfb_settings(struct platform_device *pdev,
@@ -141,14 +141,14 @@ static inline void get_secure_ctx(struct device_node *node,
static void get_secure_id(struct device_node *node,
struct msm_iommu_drvdata *drvdata)
{
- of_property_read_u32(node, "qti,iommu-secure-id", &drvdata->sec_id);
+ of_property_read_u32(node, "qcom,iommu-secure-id", &drvdata->sec_id);
}
static void get_secure_ctx(struct device_node *node,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
ctx_drvdata->secure_context =
- of_property_read_bool(node, "qti,secure-context");
+ of_property_read_bool(node, "qcom,secure-context");
}
#endif
@@ -203,7 +203,7 @@ static int msm_iommu_parse_dt(struct platform_device *pdev,
}
drvdata->halt_enabled = of_property_read_bool(pdev->dev.of_node,
- "qti,iommu-enable-halt");
+ "qcom,iommu-enable-halt");
ret = of_platform_populate(pdev->dev.of_node,
msm_iommu_ctx_match_table,
@@ -232,24 +232,24 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
pmon_info->iommu.evt_irq = platform_get_irq(pdev, 0);
ret = of_property_read_u32(pdev->dev.of_node,
- "qti,iommu-pmu-ngroups",
+ "qcom,iommu-pmu-ngroups",
&pmon_info->num_groups);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-ngroups\n");
+ pr_err("Error reading qcom,iommu-pmu-ngroups\n");
goto fail;
}
ret = of_property_read_u32(pdev->dev.of_node,
- "qti,iommu-pmu-ncounters",
+ "qcom,iommu-pmu-ncounters",
&pmon_info->num_counters);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-ncounters\n");
+ pr_err("Error reading qcom,iommu-pmu-ncounters\n");
goto fail;
}
if (!of_get_property(pdev->dev.of_node,
- "qti,iommu-pmu-event-classes",
+ "qcom,iommu-pmu-event-classes",
&cls_prop_size)) {
- pr_err("Error reading qti,iommu-pmu-event-classes\n");
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return -EINVAL;
}
@@ -264,11 +264,11 @@ static int msm_iommu_pmon_parse_dt(struct platform_device *pdev,
pmon_info->nevent_cls_supported = cls_prop_size / sizeof(u32);
ret = of_property_read_u32_array(pdev->dev.of_node,
- "qti,iommu-pmu-event-classes",
+ "qcom,iommu-pmu-event-classes",
pmon_info->event_cls_supported,
pmon_info->nevent_cls_supported);
if (ret) {
- pr_err("Error reading qti,iommu-pmu-event-classes\n");
+ pr_err("Error reading qcom,iommu-pmu-event-classes\n");
return ret;
}
} else {
@@ -309,7 +309,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
return PTR_ERR(drvdata->gdsc);
drvdata->alt_gdsc = devm_regulator_get(&pdev->dev,
- "qti,alt-vdd");
+ "qcom,alt-vdd");
if (IS_ERR(drvdata->alt_gdsc))
drvdata->alt_gdsc = NULL;
} else {
@@ -325,7 +325,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
return PTR_ERR(drvdata->clk);
needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
- "qti,needs-alt-core-clk");
+ "qcom,needs-alt-core-clk");
if (needs_alt_core_clk) {
drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
if (IS_ERR(drvdata->aclk))
@@ -333,7 +333,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
}
needs_alt_iface_clk = of_property_read_bool(pdev->dev.of_node,
- "qti,needs-alt-iface-clk");
+ "qcom,needs-alt-iface-clk");
if (needs_alt_iface_clk) {
drvdata->aiclk = devm_clk_get(&pdev->dev, "alt_iface_clk");
if (IS_ERR(drvdata->aclk))
@@ -341,7 +341,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
}
drvdata->no_atos_support = of_property_read_bool(pdev->dev.of_node,
- "qti,no-atos-support");
+ "qcom,no-atos-support");
if (clk_get_rate(drvdata->clk) == 0) {
ret = clk_round_rate(drvdata->clk, 1000);
@@ -495,7 +495,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
&ctx_drvdata->name))
ctx_drvdata->name = dev_name(&pdev->dev);
- if (!of_get_property(pdev->dev.of_node, "qti,iommu-ctx-sids", &nsid)) {
+ if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &nsid)) {
ret = -EINVAL;
goto out;
}
@@ -504,7 +504,7 @@ static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
goto out;
}
- if (of_property_read_u32_array(pdev->dev.of_node, "qti,iommu-ctx-sids",
+ if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
ctx_drvdata->sids,
nsid / sizeof(*ctx_drvdata->sids))) {
ret = -EINVAL;
@@ -551,8 +551,8 @@ static int msm_iommu_ctx_remove(struct platform_device *pdev)
}
static struct of_device_id msm_iommu_match_table[] = {
- { .compatible = "qti,msm-smmu-v1", },
- { .compatible = "qti,msm-smmu-v2", },
+ { .compatible = "qcom,msm-smmu-v1", },
+ { .compatible = "qcom,msm-smmu-v2", },
{}
};
@@ -566,8 +566,8 @@ static struct platform_driver msm_iommu_driver = {
};
static struct of_device_id msm_iommu_ctx_match_table[] = {
- { .compatible = "qti,msm-smmu-v1-ctx", },
- { .compatible = "qti,msm-smmu-v2-ctx", },
+ { .compatible = "qcom,msm-smmu-v1-ctx", },
+ { .compatible = "qcom,msm-smmu-v2-ctx", },
{}
};
diff --git a/drivers/iommu/msm_iommu_domains.c b/drivers/iommu/msm_iommu_domains.c
index 1281167b01d3..6b9ecad6165d 100644
--- a/drivers/iommu/msm_iommu_domains.c
+++ b/drivers/iommu/msm_iommu_domains.c
@@ -573,7 +573,7 @@ static int find_and_add_contexts(struct iommu_group *group,
for (i = 0; i < num_contexts; ++i) {
ctx_node = of_parse_phandle((struct device_node *) node,
- "qti,iommu-contexts", i);
+ "qcom,iommu-contexts", i);
if (!ctx_node) {
pr_err("Unable to parse phandle #%u\n", i);
ret_val = -EINVAL;
@@ -613,7 +613,7 @@ static int create_and_add_domain(struct iommu_group *group,
int secure_domain;
int l2_redirect;
- if (of_get_property(node, "qti,virtual-addr-pool", &array_size)) {
+ if (of_get_property(node, "qcom,virtual-addr-pool", &array_size)) {
l.npartitions = array_size / sizeof(unsigned int) / 2;
part = kmalloc(
sizeof(struct msm_iova_partition) * l.npartitions,
@@ -633,7 +633,7 @@ static int create_and_add_domain(struct iommu_group *group,
}
ret_val = of_property_read_u32_array(node,
- "qti,virtual-addr-pool",
+ "qcom,virtual-addr-pool",
addr_array,
array_size/sizeof(unsigned int));
if (ret_val) {
@@ -663,10 +663,10 @@ static int create_and_add_domain(struct iommu_group *group,
l.client_name = name;
l.partitions = part;
- secure_domain = of_property_read_bool(node, "qti,secure-domain");
+ secure_domain = of_property_read_bool(node, "qcom,secure-domain");
l.is_secure = (secure_domain) ? MSM_IOMMU_DOMAIN_SECURE : 0;
- l2_redirect = of_property_read_bool(node, "qti,l2-redirect");
+ l2_redirect = of_property_read_bool(node, "qcom,l2-redirect");
l.domain_flags = (l2_redirect) ? MSM_IOMMU_DOMAIN_PT_CACHEABLE : 0;
domain_no = msm_register_domain(&l);
@@ -756,8 +756,8 @@ static int iommu_domain_parse_dt(const struct device_node *dt_node)
}
iommu_group_set_name(group, name);
- if (!of_get_property(node, "qti,iommu-contexts", &sz)) {
- pr_err("Could not find qti,iommu-contexts property\n");
+ if (!of_get_property(node, "qcom,iommu-contexts", &sz)) {
+ pr_err("Could not find qcom,iommu-contexts property\n");
ret_val = -EINVAL;
goto free_group;
}
@@ -873,7 +873,7 @@ static int iommu_domain_exit(struct platform_device *pdev)
}
static struct of_device_id msm_iommu_domain_match_table[] = {
- { .name = "qti,iommu-domains", },
+ { .name = "qcom,iommu-domains", },
{}
};
diff --git a/drivers/iommu/msm_iommu_sec.c b/drivers/iommu/msm_iommu_sec.c
index 9ae950b8776c..65dcb42fcf70 100644
--- a/drivers/iommu/msm_iommu_sec.c
+++ b/drivers/iommu/msm_iommu_sec.c
@@ -283,8 +283,8 @@ static int msm_iommu_sec_ptbl_init(void)
unsigned int spare;
int ret, ptbl_ret = 0;
- for_each_compatible_node(np, NULL, "qti,msm-smmu-v1")
- if (of_find_property(np, "qti,iommu-secure-id", NULL))
+ for_each_compatible_node(np, NULL, "qcom,msm-smmu-v1")
+ if (of_find_property(np, "qcom,iommu-secure-id", NULL))
break;
if (!np)
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
index 9a85d2d0925a..b18f0e43e534 100644
--- a/drivers/leds/leds-qpnp.c
+++ b/drivers/leds/leds-qpnp.c
@@ -848,7 +848,7 @@ static int qpnp_mpp_set(struct qpnp_led_data *led)
duty_us = (led->mpp_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / LED_FULL;
/*config pwm for brightness scaling*/
- rc = pwm_config(led->mpp_cfg->pwm_cfg->pwm_dev,
+ rc = pwm_config_us(led->mpp_cfg->pwm_cfg->pwm_dev,
duty_us,
led->mpp_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
@@ -1402,7 +1402,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
duty_us = (led->kpdbl_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / KPDBL_MAX_LEVEL;
- rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev,
+ rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev,
duty_us,
led->kpdbl_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
@@ -1424,7 +1424,7 @@ static int qpnp_kpdbl_set(struct qpnp_led_data *led)
led->kpdbl_cfg->pwm_cfg->default_mode;
if (led->kpdbl_cfg->always_on) {
- rc = pwm_config(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
+ rc = pwm_config_us(led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
led->kpdbl_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
@@ -1473,7 +1473,8 @@ static int qpnp_rgb_set(struct qpnp_led_data *led)
if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
duty_us = (led->rgb_cfg->pwm_cfg->pwm_period_us *
led->cdev.brightness) / LED_FULL;
- rc = pwm_config(led->rgb_cfg->pwm_cfg->pwm_dev, duty_us,
+ rc = pwm_config_us(led->rgb_cfg->pwm_cfg->pwm_dev,
+ duty_us,
led->rgb_cfg->pwm_cfg->pwm_period_us);
if (rc < 0) {
dev_err(&led->spmi_dev->dev,
@@ -3693,10 +3694,15 @@ static int qpnp_leds_remove(struct spmi_device *spmi)
return 0;
}
+
+#ifdef CONFIG_OF
static struct of_device_id spmi_match_table[] = {
- { .compatible = "qcom,leds-qpnp",
- }
+ { .compatible = "qcom,leds-qpnp",},
+ { },
};
+#else
+#define spmi_match_table NULL
+#endif
static struct spmi_driver qpnp_leds_driver = {
.driver = {
diff --git a/drivers/media/dvb-core/dmxdev.c b/drivers/media/dvb-core/dmxdev.c
index 33bb369a8e79..d278d7cfcc1f 100644
--- a/drivers/media/dvb-core/dmxdev.c
+++ b/drivers/media/dvb-core/dmxdev.c
@@ -2862,7 +2862,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
event.type = DMX_EVENT_EOS;
dvb_dmxdev_add_event(events, &event);
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up_all(&dmxdevfilter->buffer.queue);
+ wake_up_all(&buffer->queue);
return 0;
}
@@ -2873,7 +2873,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
event.params.marker.id = dmx_data_ready->marker.id;
dvb_dmxdev_add_event(events, &event);
spin_unlock(&dmxdevfilter->dev->lock);
- wake_up_all(&dmxdevfilter->buffer.queue);
+ wake_up_all(&buffer->queue);
return 0;
}
@@ -2957,7 +2957,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
return 0;
}
- free = dvb_ringbuffer_free(&dmxdevfilter->buffer);
+ free = dvb_ringbuffer_free(buffer);
if ((DMX_OVERRUN_ERROR == dmx_data_ready->status) ||
(dmx_data_ready->data_length > free)) {
@@ -2982,8 +2982,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) {
if ((dmx_data_ready->status == DMX_OK) &&
(!events->current_event_data_size)) {
- events->current_event_start_offset =
- dmxdevfilter->buffer.pwrite;
+ events->current_event_start_offset = buffer->pwrite;
} else if (dmx_data_ready->status == DMX_OK_PES_END) {
event.type = DMX_EVENT_NEW_PES;
@@ -2992,7 +2991,7 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
event.params.pes.start_offset =
(events->current_event_start_offset +
dmx_data_ready->pes_end.start_gap) %
- dmxdevfilter->buffer.size;
+ buffer->size;
event.params.pes.actual_length =
dmx_data_ready->pes_end.actual_length;
@@ -3021,12 +3020,11 @@ static int dvb_dmxdev_ts_event_cb(struct dmx_ts_feed *feed,
}
} else {
if (!events->current_event_data_size)
- events->current_event_start_offset =
- dmxdevfilter->buffer.pwrite;
+ events->current_event_start_offset = buffer->pwrite;
}
events->current_event_data_size += dmx_data_ready->data_length;
- DVB_RINGBUFFER_PUSH(&dmxdevfilter->buffer, dmx_data_ready->data_length);
+ DVB_RINGBUFFER_PUSH(buffer, dmx_data_ready->data_length);
if ((dmxdevfilter->params.pes.output == DMX_OUT_TS_TAP) ||
(dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP)) {
@@ -3396,12 +3394,15 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter)
* even if user sets the buffer in deocder
* filter as external buffer.
*/
- if ((filter->type == DMXDEV_TYPE_PES) &&
- (filter->params.pes.output == DMX_OUT_DECODER))
+ if (filter->type == DMXDEV_TYPE_PES &&
+ (filter->params.pes.output == DMX_OUT_DECODER ||
+ filter->params.pes.output == DMX_OUT_TS_TAP))
filter->buffer_mode = DMX_BUFFER_MODE_INTERNAL;
- if ((filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL) ||
- dvb_filter_external_buffer_only(dmxdev, filter))
+ if (!(filter->type == DMXDEV_TYPE_PES &&
+ filter->params.pes.output == DMX_OUT_TS_TAP) &&
+ (filter->buffer_mode == DMX_BUFFER_MODE_EXTERNAL ||
+ dvb_filter_external_buffer_only(dmxdev, filter)))
return -ENOMEM;
mem = vmalloc_user(filter->buffer.size);
@@ -4623,6 +4624,7 @@ static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p)
struct dmx_buffer_status buffer_status;
struct dmx_scrambling_bits scrambling_bits;
const char *pes_feeds[] = {"DEC", "PES", "DVR", "REC"};
+ int ret;
if (!dmxdev)
return 0;
@@ -4650,8 +4652,14 @@ static int dvb_dmxdev_dbgfs_print(struct seq_file *s, void *p)
dvb_dmxdev_get_scrambling_bits(filter,
&scrambling_bits);
- if (0 == dvb_dmxdev_get_buffer_status(
- filter, &buffer_status)) {
+ if (filter->type == DMXDEV_TYPE_PES &&
+ filter->params.pes.output == DMX_OUT_TS_TAP)
+ ret = dvb_dvr_get_buffer_status(dmxdev,
+ O_RDONLY, &buffer_status);
+ else
+ ret = dvb_dmxdev_get_buffer_status(filter,
+ &buffer_status);
+ if (!ret) {
seq_printf(s, "size: %08d, ",
buffer_status.size);
seq_printf(s, "fullness: %08d, ",
diff --git a/drivers/media/platform/msm/broadcast/ensigma_uccp330.c b/drivers/media/platform/msm/broadcast/ensigma_uccp330.c
index 5b8a1553885f..e0fb662b3f71 100644
--- a/drivers/media/platform/msm/broadcast/ensigma_uccp330.c
+++ b/drivers/media/platform/msm/broadcast/ensigma_uccp330.c
@@ -654,7 +654,7 @@ static const struct dev_pm_ops demod_dev_pm_ops = {
/* Platform driver information */
static struct of_device_id msm_demod_match_table[] = {
- {.compatible = "qti,msm-demod"}
+ {.compatible = "qcom,msm-demod"}
};
static struct platform_driver msm_demod_driver = {
diff --git a/drivers/media/platform/msm/broadcast/tsc.c b/drivers/media/platform/msm/broadcast/tsc.c
index e4f08aa59fa6..39b9ba906253 100644
--- a/drivers/media/platform/msm/broadcast/tsc.c
+++ b/drivers/media/platform/msm/broadcast/tsc.c
@@ -67,6 +67,9 @@ module_param(tsc_iommu_bypass, int, S_IRUGO | S_IWUSR | S_IWGRP);
#define CICAM_CLK_RATE_12MHZ 12000000
#define CICAM_CLK_RATE_9MHZ 8971962
#define CICAM_CLK_RATE_7MHZ 7218045
+/* Rates for TSC serial and parallel clocks */
+#define TSC_SER_CLK_RATE 192000000
+#define TSC_PAR_CLK_RATE 24000000
/*
* TSC register offsets
@@ -189,13 +192,15 @@ enum transaction_state {
};
/**
- * enum pcmcia_state - states for the pcmcia pinctrl states
- */
+* enum pcmcia_state - states for the pcmcia pinctrl states
+* Note: the numbers here corresponds to the numbers of enum tsc_cam_personality
+* in tsc.h file.
+*/
enum pcmcia_state {
- DISABLE,
- PC_CARD,
- CI_CARD,
- CI_PLUS
+ PCMCIA_STATE_DISABLE = 0,
+ PCMCIA_STATE_CI_CARD = 1,
+ PCMCIA_STATE_CI_PLUS = 2,
+ PCMCIA_STATE_PC_CARD = 3
};
/**
@@ -206,6 +211,7 @@ enum pcmcia_state {
* @domain_num: TSC IOMMU domain number.
* @partition_num: TSC iommu partition number.
* @ion_client: TSC IOMMU client.
+ * @iommu_group_name TSC IOMMU group name.
*/
struct iommu_info {
struct iommu_group *group;
@@ -213,6 +219,7 @@ struct iommu_info {
int domain_num;
int partition_num;
struct ion_client *ion_client;
+ const char *iommu_group_name;
};
/**
@@ -402,21 +409,6 @@ struct tsc_device {
struct dentry *debugfs_entry;
};
-/**
- * struct msm_tsc_platform_data - TSC platform data
- *
- * @iommu_group: TSC IOMMU group name.
- * @iommu_partition: TSC IOMMU partition number.
- * @ts0_config: The TS0 configuration (1=A, 2=B).
- * @ts1_config: The TS1 configuration (1=A, 2=B).
- */
-struct msm_tsc_platform_data {
- const char *iommu_group;
- u32 iommu_partition;
- u32 ts0_config;
- u32 ts1_config;
-};
-
/* Global TSC device class */
static struct class *tsc_class;
@@ -821,16 +813,20 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
+ if (mutex_lock_interruptible(&tsc_device->mutex))
+ return -ERESTARTSYS;
+
if (source == TSC_SOURCE_EXTERNAL0) {
if (!ppinctrl->is_ts0) {
pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n",
__func__);
+ mutex_unlock(&tsc_device->mutex);
return -EPERM;
}
/* Transition from current pinctrl state to curr + ts0 sleep */
switch (pcurr_state->pcmcia_state) {
- case DISABLE:
+ case PCMCIA_STATE_DISABLE:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1);
@@ -838,7 +834,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->disable);
break;
- case PC_CARD:
+ case PCMCIA_STATE_PC_CARD:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1_pc_card);
@@ -846,7 +842,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->pc_card);
break;
- case CI_CARD:
+ case PCMCIA_STATE_CI_CARD:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1_ci_card);
@@ -854,7 +850,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ci_card);
break;
- case CI_PLUS:
+ case PCMCIA_STATE_CI_PLUS:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1_ci_plus);
@@ -867,12 +863,13 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
if (!ppinctrl->is_ts1) {
pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n",
__func__);
+ mutex_unlock(&tsc_device->mutex);
return -EPERM;
}
/* Transition from current pinctrl state to curr + ts1 sleep */
switch (pcurr_state->pcmcia_state) {
- case DISABLE:
+ case PCMCIA_STATE_DISABLE:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0);
@@ -880,7 +877,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->disable);
break;
- case PC_CARD:
+ case PCMCIA_STATE_PC_CARD:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0_pc_card);
@@ -888,7 +885,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->pc_card);
break;
- case CI_CARD:
+ case PCMCIA_STATE_CI_CARD:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0_ci_card);
@@ -896,7 +893,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ci_card);
break;
- case CI_PLUS:
+ case PCMCIA_STATE_CI_PLUS:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0_ci_plus);
@@ -910,6 +907,7 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
if (ret != 0) {
pr_err("%s: error disabling TS-in pins. ret value = %d\n",
__func__, ret);
+ mutex_unlock(&tsc_device->mutex);
return -EINVAL;
}
@@ -919,6 +917,8 @@ static int tsc_suspend_ts_pins(enum tsc_source source)
else
pcurr_state->ts1 = false;
+ mutex_unlock(&tsc_device->mutex);
+
return 0;
}
@@ -938,16 +938,20 @@ static int tsc_activate_ts_pins(enum tsc_source source)
struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
+ if (mutex_lock_interruptible(&tsc_device->mutex))
+ return -ERESTARTSYS;
+
if (source == TSC_SOURCE_EXTERNAL0) {
if (!ppinctrl->is_ts0) {
pr_err("%s: No TS0-in pinctrl definitions were found in the TSC devicetree\n",
__func__);
+ mutex_unlock(&tsc_device->mutex);
return -EPERM;
}
/* Transition from current pinctrl state to curr + ts0 active */
switch (pcurr_state->pcmcia_state) {
- case DISABLE:
+ case PCMCIA_STATE_DISABLE:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts);
@@ -955,7 +959,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0);
break;
- case PC_CARD:
+ case PCMCIA_STATE_PC_CARD:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_pc_card);
@@ -963,7 +967,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0_pc_card);
break;
- case CI_CARD:
+ case PCMCIA_STATE_CI_CARD:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_ci_card);
@@ -971,7 +975,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts0_ci_card);
break;
- case CI_PLUS:
+ case PCMCIA_STATE_CI_PLUS:
if (pcurr_state->ts1)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_ci_plus);
@@ -984,12 +988,13 @@ static int tsc_activate_ts_pins(enum tsc_source source)
if (!ppinctrl->is_ts1) {
pr_err("%s: No TS1-in pinctrl definitions were found in the TSC devicetree\n",
__func__);
+ mutex_unlock(&tsc_device->mutex);
return -EPERM;
}
/* Transition from current pinctrl state to curr + ts1 active */
switch (pcurr_state->pcmcia_state) {
- case DISABLE:
+ case PCMCIA_STATE_DISABLE:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts);
@@ -997,7 +1002,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1);
break;
- case PC_CARD:
+ case PCMCIA_STATE_PC_CARD:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_pc_card);
@@ -1005,7 +1010,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1_pc_card);
break;
- case CI_CARD:
+ case PCMCIA_STATE_CI_CARD:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_ci_card);
@@ -1013,7 +1018,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->ts1_ci_card);
break;
- case CI_PLUS:
+ case PCMCIA_STATE_CI_PLUS:
if (pcurr_state->ts0)
ret = pinctrl_select_state(ppinctrl->pinctrl,
ppinctrl->dual_ts_ci_plus);
@@ -1027,6 +1032,7 @@ static int tsc_activate_ts_pins(enum tsc_source source)
if (ret != 0) {
pr_err("%s: error activating TS-in pins. ret value = %d\n",
__func__, ret);
+ mutex_unlock(&tsc_device->mutex);
return -EINVAL;
}
@@ -1036,6 +1042,8 @@ static int tsc_activate_ts_pins(enum tsc_source source)
else
pcurr_state->ts1 = true;
+ mutex_unlock(&tsc_device->mutex);
+
return 0;
}
@@ -1496,6 +1504,96 @@ err_copy_arg:
}
/**
+ * tsc_personality_change() - change the PCMCIA pins state.
+ *
+ * @pcmcia_state: The new state of the PCMCIA pins.
+ *
+ * Configure the TLMM pins of the PCMCIA according to received state and
+ * the current pinctrl configuration of the other pins. This function assums the
+ * PCMCIA pinctrl definitions were successfully parsed from the devicetree (this
+ * check is done at open device).
+ *
+ * Return 0 on success, error value otherwise.
+ */
+static int tsc_personality_change(enum tsc_cam_personality pcmcia_state)
+{
+ int ret = 0;
+ struct pinctrl_info *ppinctrl = &tsc_device->pinctrl_info;
+ struct pinctrl_current_state *pcurr_state = &ppinctrl->curr_state;
+
+ if (mutex_lock_interruptible(&tsc_device->mutex))
+ return -ERESTARTSYS;
+
+ if (pcmcia_state == (enum tsc_cam_personality)pcurr_state->pcmcia_state)
+ goto exit;
+
+
+ /* Transition from current pinctrl state to curr + new pcmcia state */
+ switch (pcmcia_state) {
+ case TSC_CICAM_PERSONALITY_CI:
+ if (pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->dual_ts_ci_card);
+ else if (pcurr_state->ts0 && !pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts0_ci_card);
+ else if (!pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts1_ci_card);
+ else
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ci_card);
+ break;
+ case TSC_CICAM_PERSONALITY_CIPLUS:
+ if (pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->dual_ts_ci_plus);
+ else if (pcurr_state->ts0 && !pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts0_ci_plus);
+ else if (!pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts1_ci_plus);
+ else
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ci_plus);
+ break;
+ case TSC_CICAM_PERSONALITY_DISABLE:
+ if (pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->dual_ts);
+ else if (pcurr_state->ts0 && !pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts0);
+ else if (!pcurr_state->ts0 && pcurr_state->ts1)
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->ts1);
+ else
+ ret = pinctrl_select_state(ppinctrl->pinctrl,
+ ppinctrl->disable);
+ break;
+ default:
+ pr_err("%s: Wrong personality parameter\n", __func__);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (ret != 0) {
+ pr_err("%s: error changing PCMCIA pins. ret value = %d\n",
+ __func__, ret);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ /* Update the current pcmcia state in the internal struct */
+ pcurr_state->pcmcia_state = (enum pcmcia_state)pcmcia_state;
+
+exit:
+ mutex_unlock(&tsc_device->mutex);
+ return ret;
+}
+
+/**
* tsc_reset_cam() - HW reset to the CAM.
*
* Toggle the reset pin of the pcmcia to make a HW reset.
@@ -1667,6 +1765,20 @@ static int tsc_mux_power_on_clocks(void)
goto err_set_rate;
}
+ /* Setting the TSC serial clock rate */
+ ret = clk_set_rate(tsc_device->ser_clk, TSC_SER_CLK_RATE);
+ if (ret != 0) {
+ pr_err("%s: Can't set rate for tsc serial clock", __func__);
+ goto err_set_rate;
+ }
+
+ /* Setting the TSC parallel clock rate */
+ ret = clk_set_rate(tsc_device->par_clk, TSC_PAR_CLK_RATE);
+ if (ret != 0) {
+ pr_err("%s: Can't set rate for tsc parallel clock", __func__);
+ goto err_set_rate;
+ }
+
/* Enabling the clocks */
ret = clk_prepare_enable(tsc_device->ser_clk);
if (ret != 0) {
@@ -1758,7 +1870,8 @@ static int tsc_device_power_up(void)
/* Update the current pinctrl state in the internal struct */
tsc_device->pinctrl_info.curr_state.ts0 = false;
tsc_device->pinctrl_info.curr_state.ts1 = false;
- tsc_device->pinctrl_info.curr_state.pcmcia_state = DISABLE;
+ tsc_device->pinctrl_info.curr_state.pcmcia_state =
+ TSC_CICAM_PERSONALITY_DISABLE;
/* Reset TSC registers to a default known state */
tsc_reset_registers();
@@ -2167,8 +2280,10 @@ static long tsc_mux_ioctl(struct file *filp,
switch (cmd) {
case TSC_CONFIG_ROUTE:
if (!arg || copy_from_user(&tsc_route, (void *)arg,
- sizeof(struct tsc_route)))
- return -EFAULT;
+ sizeof(struct tsc_route))) {
+ ret = -EFAULT;
+ goto err;
+ }
ret = tsc_route_mux(tsc_mux, tsc_route.source, tsc_route.dest);
break;
case TSC_ENABLE_INPUT:
@@ -2179,8 +2294,10 @@ static long tsc_mux_ioctl(struct file *filp,
break;
case TSC_SET_TSIF_CONFIG:
if (!arg || copy_from_user(&tsif_params, (void *)arg,
- sizeof(struct tsc_tsif_params)))
- return -EFAULT;
+ sizeof(struct tsc_tsif_params))) {
+ ret = -EFAULT;
+ goto err;
+ }
ret = tsc_config_tsif(tsc_mux, &tsif_params);
break;
case TSC_CLEAR_RATE_MISMATCH_IRQ:
@@ -2194,6 +2311,7 @@ static long tsc_mux_ioctl(struct file *filp,
pr_err("%s: Unknown ioctl %i", __func__, cmd);
}
+err:
mutex_unlock(&tsc_mux->mutex);
return ret;
}
@@ -2231,7 +2349,7 @@ static long tsc_ci_ioctl(struct file *filp,
ret = tsc_reset_cam();
break;
case TSC_CICAM_PERSONALITY_CHANGE:
- /* TODO: set the pcmcia pins accordingly */
+ ret = tsc_personality_change(arg);
break;
case TSC_GET_CARD_STATUS:
spin_lock_irqsave(&tsc_ci->spinlock, flags);
@@ -2700,7 +2818,6 @@ static void tsc_free_iommu_info(void)
static int tsc_get_iommu_info(struct platform_device *pdev)
{
int ret = 0;
- struct msm_tsc_platform_data *pdata = pdev->dev.platform_data;
/* Create a new ION client used by tsc ci to allocate memory */
tsc_device->iommu_info.ion_client = msm_ion_client_create(UINT_MAX,
@@ -2715,7 +2832,8 @@ static int tsc_get_iommu_info(struct platform_device *pdev)
}
/* Find the iommu group by the name obtained from the device tree */
- tsc_device->iommu_info.group = iommu_group_find(pdata->iommu_group);
+ tsc_device->iommu_info.group =
+ iommu_group_find(tsc_device->iommu_info.iommu_group_name);
if (!tsc_device->iommu_info.group) {
pr_err("%s: error in iommu_group_find", __func__);
ret = -EINVAL;
@@ -2735,9 +2853,6 @@ static int tsc_get_iommu_info(struct platform_device *pdev)
tsc_device->iommu_info.domain_num =
msm_find_domain_no(tsc_device->iommu_info.domain);
- /* Save the partition number */
- tsc_device->iommu_info.partition_num = pdata->iommu_partition;
-
return ret;
err_domain:
@@ -2751,78 +2866,60 @@ err_client:
}
/**
- * msm_tsc_dt_to_pdata() - Copy device-tree data to platfrom data structure.
+ * tsc_parse_dt() - Parse device-tree data and save it.
*
* @pdev: A pointer to the TSC platform device.
*
- * Return pointer to allocated platform data on success, NULL on failure.
+ * Return 0 on success, error value otherwise.
*/
-static struct msm_tsc_platform_data *msm_tsc_dt_to_pdata
- (struct platform_device *pdev)
+static int tsc_parse_dt(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
- struct msm_tsc_platform_data *pdata;
struct device_node *iommu_pnode;
int ret;
- /* Note: memory allocated by devm_kzalloc is freed automatically */
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev, "%s: Unable to allocate platform data\n",
- __func__);
- return NULL;
- }
-
/* Check that power regulator property exist */
if (!of_get_property(node, "vdd-supply", NULL)) {
dev_err(&pdev->dev, "%s: Could not find vdd-supply property\n",
__func__);
- return NULL;
+ return -EINVAL;
}
/* Reading IOMMU group label by obtaining the group's phandle */
- iommu_pnode = of_parse_phandle(node, "qti,iommu-group", 0);
+ iommu_pnode = of_parse_phandle(node, "qcom,iommu-group", 0);
if (!iommu_pnode) {
dev_err(&pdev->dev, "%s: Couldn't find iommu-group property\n",
__func__);
- return NULL;
+ return -EINVAL;
}
ret = of_property_read_string(iommu_pnode, "label",
- &pdata->iommu_group);
+ &tsc_device->iommu_info.iommu_group_name);
of_node_put(iommu_pnode);
if (ret) {
dev_err(&pdev->dev, "%s: Couldn't find label property of the IOMMU group, err=%d\n",
__func__, ret);
- return NULL;
+ return -EINVAL;
}
/* Reading IOMMU partition */
- ret = of_property_read_u32(node, "qti,iommu-partition",
- &pdata->iommu_partition);
+ ret = of_property_read_u32(node, "qcom,iommu-partition",
+ &tsc_device->iommu_info.partition_num);
if (ret) {
dev_err(&pdev->dev, "%s: Couldn't find iommu-partition property, err=%d\n",
__func__, ret);
- return NULL;
+ return -EINVAL;
}
/* Reading reset cam gpio */
tsc_device->reset_cam_gpio = of_get_named_gpio(node,
- "qti,tsc-reset-cam-gpio", 0);
+ "qcom,tsc-reset-cam-gpio", 0);
if (tsc_device->reset_cam_gpio < 0) {
- dev_err(&pdev->dev, "%s: Couldn't find qti,tsc-reset-cam-gpio property\n",
+ dev_err(&pdev->dev, "%s: Couldn't find qcom,tsc-reset-cam-gpio property\n",
__func__);
- return NULL;
+ return -EINVAL;
}
- /* Reading the a/b configuration - if exist */
- ret = of_property_read_u32(node, "qti,ts0-config", &pdata->ts0_config);
- if (ret)
- pdata->ts0_config = 0;
- ret = of_property_read_u32(node, "qti,ts1-config", &pdata->ts1_config);
- if (ret)
- pdata->ts1_config = 0;
-
- return pdata;
+ return 0;
}
/* TSC Mux file operations */
@@ -2848,7 +2945,6 @@ static const struct file_operations tsc_ci_fops = {
static int msm_tsc_probe(struct platform_device *pdev)
{
int ret;
- struct msm_tsc_platform_data *pdata = NULL;
tsc_device = kzalloc(sizeof(struct tsc_device), GFP_KERNEL);
if (!tsc_device) {
@@ -2858,16 +2954,16 @@ static int msm_tsc_probe(struct platform_device *pdev)
/* get information from device tree */
if (pdev->dev.of_node) {
- pdata = msm_tsc_dt_to_pdata(pdev);
- pdev->dev.platform_data = pdata;
- } else { /* else - must have platform data */
- pdata = pdev->dev.platform_data;
- }
-
- if (!pdata) {
- pr_err("%s: Platform data not available", __func__);
+ ret = tsc_parse_dt(pdev);
+ if (ret != 0) {
+ pr_err("%s: devicetree data not available", __func__);
+ ret = -EINVAL;
+ goto err_dt;
+ }
+ } else { /* else - devicetree is not found */
+ pr_err("%s: devicetree data is missing", __func__);
ret = -EINVAL;
- goto err_pdata;
+ goto err_dt;
}
/* set up references */
@@ -2962,7 +3058,7 @@ err_map_io:
tsc_clocks_put();
err_clocks_get:
tsc_free_iommu_info();
-err_pdata:
+err_dt:
kfree(tsc_device);
return ret;
@@ -3018,7 +3114,7 @@ static int msm_tsc_remove(struct platform_device *pdev)
/*********************** Platform driver information ***********************/
static struct of_device_id msm_match_table[] = {
- {.compatible = "qti,msm-tsc"},
+ {.compatible = "qcom,msm-tsc"},
{}
};
diff --git a/drivers/media/platform/msm/broadcast/tspp.c b/drivers/media/platform/msm/broadcast/tspp.c
index 9b58edbd0b54..6f1ea5f602ba 100644
--- a/drivers/media/platform/msm/broadcast/tspp.c
+++ b/drivers/media/platform/msm/broadcast/tspp.c
@@ -34,6 +34,7 @@
#include <linux/tspp.h> /* tspp functions */
#include <linux/bitops.h> /* BIT() macro */
#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <mach/sps.h> /* BAM stuff */
#include <mach/gpio.h>
#include <linux/wakelock.h> /* Locking functions */
@@ -41,7 +42,6 @@
#include <linux/jiffies.h> /* Jiffies counter */
#include <mach/dma.h>
#include <mach/msm_tspp.h>
-#include <mach/rpm-regulator-smd.h>
#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
diff --git a/drivers/media/platform/msm/broadcast/tspp2.c b/drivers/media/platform/msm/broadcast/tspp2.c
index af96642953e6..c30723860632 100644
--- a/drivers/media/platform/msm/broadcast/tspp2.c
+++ b/drivers/media/platform/msm/broadcast/tspp2.c
@@ -31,6 +31,7 @@
#include <linux/msm_iommu_domains.h>
#include <mach/msm_bus.h>
#include <mach/msm_tspp2.h>
+#include <linux/clk/msm-clk.h>
#define TSPP2_MODULUS_OP(val, mod) ((val) & ((mod) - 1))
@@ -46,6 +47,13 @@
#define TSPP2_NUM_EVENT_WORK_ELEMENTS 256
/*
+ * Based on the hardware programming guide, HW requires we wait for up to 2ms
+ * before closing the pipes used by the filter.
+ * This is required to avoid unexpected pipe reset interrupts.
+ */
+#define TSPP2_HW_DELAY_USEC 2000
+
+/*
* Default source configuration:
* Sync byte 0x47, check sync byte,
* Do not monitor scrambling bits,
@@ -798,6 +806,8 @@ struct tspp2_iommu_info {
* @tspp2_ahb_clk: TSPP2 AHB clock.
* @tspp2_core_clk: TSPP2 core clock.
* @tspp2_vbif_clk: TSPP2 VBIF clock.
+ * @vbif_ahb_clk: VBIF AHB clock.
+ * @vbif_axi_clk: VBIF AXI clock.
* @tspp2_klm_ahb_clk: TSPP2 KLM AHB clock.
* @tsif_ref_clk: TSIF reference clock.
* @batches: An array of filter batch objects.
@@ -844,6 +854,8 @@ struct tspp2_device {
struct clk *tspp2_ahb_clk;
struct clk *tspp2_core_clk;
struct clk *tspp2_vbif_clk;
+ struct clk *vbif_ahb_clk;
+ struct clk *vbif_axi_clk;
struct clk *tspp2_klm_ahb_clk;
struct clk *tsif_ref_clk;
struct tspp2_filter_batch batches[TSPP2_NUM_BATCHES];
@@ -982,7 +994,7 @@ static void tspp2_tsif_debugfs_init(struct tspp2_tsif_device *tsif_device)
"stat_pkt_read_err",
S_IRUGO | S_IWUSR | S_IWGRP,
dentry,
- &tsif_device->stat_pkt_write_err);
+ &tsif_device->stat_pkt_read_err);
debugfs_create_u32(
"stat_overflow",
@@ -1004,6 +1016,286 @@ static void tspp2_tsif_debugfs_init(struct tspp2_tsif_device *tsif_device)
}
}
+static char *op_to_string(enum tspp2_operation_type op)
+{
+ switch (op) {
+ case TSPP2_OP_PES_ANALYSIS:
+ return "TSPP2_OP_PES_ANALYSIS";
+ case TSPP2_OP_RAW_TRANSMIT:
+ return "TSPP2_OP_RAW_TRANSMIT";
+ case TSPP2_OP_PES_TRANSMIT:
+ return "TSPP2_OP_PES_TRANSMIT";
+ case TSPP2_OP_PCR_EXTRACTION:
+ return "TSPP2_OP_PCR_EXTRACTION";
+ case TSPP2_OP_CIPHER:
+ return "TSPP2_OP_CIPHER";
+ case TSPP2_OP_INDEXING:
+ return "TSPP2_OP_INDEXING";
+ case TSPP2_OP_COPY_PACKET:
+ return "TSPP2_OP_COPY_PACKET";
+ default:
+ return "Invalid Operation";
+ }
+}
+
+static char *src_input_to_string(enum tspp2_src_input src_input)
+{
+ switch (src_input) {
+ case TSPP2_INPUT_TSIF0:
+ return "TSPP2_INPUT_TSIF0";
+ case TSPP2_INPUT_TSIF1:
+ return "TSPP2_INPUT_TSIF1";
+ case TSPP2_INPUT_MEMORY:
+ return "TSPP2_INPUT_MEMORY";
+ default:
+ return "Unknown source input type";
+ }
+}
+
+static char *pkt_format_to_string(enum tspp2_packet_format pkt_format)
+{
+ switch (pkt_format) {
+ case TSPP2_PACKET_FORMAT_188_RAW:
+ return "TSPP2_PACKET_FORMAT_188_RAW";
+ case TSPP2_PACKET_FORMAT_192_HEAD:
+ return "TSPP2_PACKET_FORMAT_192_HEAD";
+ case TSPP2_PACKET_FORMAT_192_TAIL:
+ return "TSPP2_PACKET_FORMAT_192_TAIL";
+ default:
+ return "Unknown packet format";
+ }
+}
+
+/**
+ * debugfs service to print device information.
+ */
+static int tspp2_device_debugfs_print(struct seq_file *s, void *p)
+{
+ int count;
+ int exist_flag = 0;
+ struct tspp2_device *device = (struct tspp2_device *)s->private;
+
+ seq_printf(s, "dev_id: %d\n", device->dev_id);
+ seq_puts(s, "Enabled filters:");
+ for (count = 0; count < TSPP2_NUM_HW_FILTERS; count++)
+ if (device->filters[count].enabled) {
+ seq_printf(s, "\n\tfilter%3d", count);
+ exist_flag = 1;
+ }
+ if (!exist_flag)
+ seq_puts(s, " none\n");
+ else
+ seq_puts(s, "\n");
+
+ exist_flag = 0;
+ seq_puts(s, "Opened filters:");
+ for (count = 0; count < TSPP2_NUM_HW_FILTERS; count++)
+ if (device->filters[count].opened) {
+ seq_printf(s, "\n\tfilter%3d", count);
+ exist_flag = 1;
+ }
+ if (!exist_flag)
+ seq_puts(s, " none\n");
+ else
+ seq_puts(s, "\n");
+
+ exist_flag = 0;
+ seq_puts(s, "Opened pipes:\n");
+ for (count = 0; count < TSPP2_NUM_PIPES; count++)
+ if (device->pipes[count].opened) {
+ seq_printf(s, "\tpipe%2d\n", count);
+ exist_flag = 1;
+ }
+ if (!exist_flag)
+ seq_puts(s, " none\n");
+ else
+ seq_puts(s, "\n");
+
+ return 0;
+}
+
+/**
+ * debugfs service to print source information.
+ */
+static int tspp2_src_debugfs_print(struct seq_file *s, void *p)
+{
+ struct tspp2_filter_batch *batch;
+ struct tspp2_filter *filter;
+ struct tspp2_output_pipe *output_pipe;
+ struct tspp2_src *src = (struct tspp2_src *)s->private;
+
+ if (!src) {
+ seq_puts(s, "error\n");
+ return 1;
+ }
+ seq_printf(s, "Status: %s\n", src->enabled ? "enabled" : "disabled");
+ seq_printf(s, "hw_index: %d\n", src->hw_index);
+ seq_printf(s, "event_bitmask: 0x%08X\n", src->event_bitmask);
+ if (src->input_pipe)
+ seq_printf(s, "input_pipe hw_index: %d\n",
+ src->input_pipe->hw_index);
+ seq_printf(s, "tspp2_src_input: %s\n", src_input_to_string(src->input));
+ seq_printf(s, "pkt_format: %s\n",
+ pkt_format_to_string(src->pkt_format));
+ seq_printf(s, "num_associated_batches: %d\n",
+ src->num_associated_batches);
+
+ if (src->num_associated_batches) {
+ seq_puts(s, "batch_ids: ");
+ list_for_each_entry(batch, &src->batches_list, link)
+ seq_printf(s, "%d ", batch->batch_id);
+ seq_puts(s, "\n");
+ }
+
+ seq_printf(s, "num_associated_pipes: %d\n", src->num_associated_pipes);
+ if (src->num_associated_pipes) {
+ seq_puts(s, "pipes_hw_idxs: ");
+ list_for_each_entry(output_pipe, &src->output_pipe_list, link) {
+ seq_printf(s, "%d ", output_pipe->pipe->hw_index);
+ }
+ seq_puts(s, "\n");
+ }
+
+ seq_printf(s, "reserved_filter_hw_index: %d\n",
+ src->reserved_filter_hw_index);
+
+ seq_printf(s, "num_associated_filters: %d\n",
+ src->num_associated_filters);
+ if (src->num_associated_filters) {
+ int i;
+ seq_puts(s, "Open filters:\n");
+ list_for_each_entry(filter, &src->filters_list, link) {
+ if (!filter->opened)
+ continue;
+ seq_printf(s, "\thw_index: %d\n",
+ filter->hw_index);
+ seq_printf(s, "\tStatus: %s\n",
+ filter->enabled ? "enabled"
+ : "disabled");
+ seq_printf(s, "\tpid_value: 0x%08X\n",
+ filter->pid_value);
+ seq_printf(s, "\tmask: 0x%08X\n", filter->mask);
+ seq_printf(s, "\tnum_user_operations: %d\n",
+ filter->num_user_operations);
+ if (filter->num_user_operations) {
+ seq_puts(
+ s, "\tTypes of operations:\n");
+ for (i = 0;
+ i < filter->num_user_operations; i++) {
+ seq_printf(s, "\t\t%s\n", op_to_string(
+ filter->operations[i].type));
+ }
+ }
+ }
+
+ } else {
+ seq_puts(s, "no filters\n");
+ }
+
+ return 0;
+}
+
+/**
+ * debugfs service to print filter information.
+ */
+static int filter_debugfs_print(struct seq_file *s, void *p)
+{
+ int i;
+ struct tspp2_filter *filter = (struct tspp2_filter *)s->private;
+
+ seq_printf(s, "Status: %s\n", filter->opened ? "opened" : "closed");
+ if (filter->batch)
+ seq_printf(s, "Located in batch %d\n", filter->batch->batch_id);
+ if (filter->src)
+ seq_printf(s, "Associated with src %d\n",
+ filter->src->hw_index);
+ seq_printf(s, "hw_index: %d\n", filter->hw_index);
+ seq_printf(s, "pid_value: 0x%08X\n", filter->pid_value);
+ seq_printf(s, "mask: 0x%08X\n", filter->mask);
+ seq_printf(s, "context: %d\n", filter->context);
+ seq_printf(s, "indexing_table_id: %d\n", filter->indexing_table_id);
+ seq_printf(s, "num_user_operations: %d\n", filter->num_user_operations);
+ seq_puts(s, "Types of operations:\n");
+ for (i = 0; i < filter->num_user_operations; i++)
+ seq_printf(s, "\t%s\n", op_to_string(
+ filter->operations[i].type));
+ seq_printf(s, "indexing_op_set: %d\n", filter->indexing_op_set);
+ seq_printf(s, "raw_op_with_indexing: %d\n",
+ filter->raw_op_with_indexing);
+ seq_printf(s, "pes_analysis_op_set: %d\n", filter->pes_analysis_op_set);
+ seq_printf(s, "raw_op_set: %d\n", filter->raw_op_set);
+ seq_printf(s, "pes_tx_op_set: %d\n", filter->pes_tx_op_set);
+ seq_printf(s, "Status: %s\n", filter->enabled ? "enabled" : "disabled");
+ return 0;
+}
+
+/**
+ * debugfs service to print pipe information.
+ */
+static int pipe_debugfs_print(struct seq_file *s, void *p)
+{
+ struct tspp2_pipe *pipe = (struct tspp2_pipe *)s->private;
+ seq_printf(s, "hw_index: %d\n", pipe->hw_index);
+ seq_printf(s, "iova: 0x%08X\n", pipe->iova);
+ seq_printf(s, "threshold: %d\n", pipe->threshold);
+ seq_printf(s, "Status: %s\n", pipe->opened ? "opened" : "closed");
+ seq_printf(s, "ref_cnt: %d\n", pipe->ref_cnt);
+ return 0;
+}
+
+static int tspp2_dev_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tspp2_device_debugfs_print,
+ inode->i_private);
+}
+
+static int tspp2_filter_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, filter_debugfs_print, inode->i_private);
+}
+
+static int tspp2_pipe_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pipe_debugfs_print, inode->i_private);
+}
+
+static int tspp2_src_dbgfs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, tspp2_src_debugfs_print, inode->i_private);
+}
+
+static const struct file_operations dbgfs_tspp2_device_fops = {
+ .open = tspp2_dev_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations dbgfs_filter_fops = {
+ .open = tspp2_filter_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations dbgfs_pipe_fops = {
+ .open = tspp2_pipe_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static const struct file_operations dbgfs_src_fops = {
+ .open = tspp2_src_dbgfs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
/**
* tspp2_tsif_debugfs_exit() - TSIF device debugfs teardown.
*
@@ -1299,6 +1591,41 @@ static void tspp2_debugfs_init(struct tspp2_device *device)
}
}
}
+ dir = debugfs_create_dir("software", device->debugfs_entry);
+ debugfs_create_file("device", S_IRUGO, dir, device,
+ &dbgfs_tspp2_device_fops);
+
+ dentry = debugfs_create_dir("filters", dir);
+ if (dentry) {
+ for (i = 0; i < TSPP2_NUM_HW_FILTERS; i++) {
+ snprintf(name, 20, "filter%03i", i);
+ debugfs_create_file(name, S_IRUGO, dentry,
+ &(device->filters[i]), &dbgfs_filter_fops);
+ }
+ }
+
+ dentry = debugfs_create_dir("pipes", dir);
+ if (dentry) {
+ for (i = 0; i < TSPP2_NUM_PIPES; i++) {
+ snprintf(name, 20, "pipe%02i", i);
+ debugfs_create_file(name, S_IRUGO, dentry,
+ &(device->pipes[i]), &dbgfs_pipe_fops);
+ }
+ }
+
+ dentry = debugfs_create_dir("sources", dir);
+ if (dentry) {
+ for (i = 0; i < TSPP2_NUM_TSIF_INPUTS; i++) {
+ snprintf(name, 20, "tsif%d", i);
+ debugfs_create_file(name, S_IRUGO, dentry,
+ &(device->tsif_sources[i]), &dbgfs_src_fops);
+ }
+ for (i = 0; i < TSPP2_NUM_MEM_INPUTS; i++) {
+ snprintf(name, 20, "mem%d", i);
+ debugfs_create_file(name, S_IRUGO, dentry,
+ &(device->mem_sources[i]), &dbgfs_src_fops);
+ }
+ }
}
/**
@@ -1381,6 +1708,58 @@ static int tspp2_tsif_start(struct tspp2_tsif_device *tsif_device)
return (ctl & TSIF_STS_CTL_START) ? 0 : -EBUSY;
}
+
+static int tspp2_vbif_clock_start(struct tspp2_device *device)
+{
+ int ret;
+
+ if (device->tspp2_vbif_clk) {
+ ret = clk_prepare_enable(device->tspp2_vbif_clk);
+ if (ret) {
+ pr_err("%s: Can't start tspp2_vbif_clk\n", __func__);
+ return ret;
+ }
+ }
+
+ if (device->vbif_ahb_clk) {
+ ret = clk_prepare_enable(device->vbif_ahb_clk);
+ if (ret) {
+ pr_err("%s: Can't start vbif_ahb_clk\n", __func__);
+ goto disable_vbif_tspp2;
+ }
+ }
+ if (device->vbif_axi_clk) {
+ ret = clk_prepare_enable(device->vbif_axi_clk);
+ if (ret) {
+ pr_err("%s: Can't start vbif_ahb_clk\n", __func__);
+ goto disable_vbif_ahb;
+ }
+ }
+
+ return 0;
+
+disable_vbif_ahb:
+ if (device->vbif_ahb_clk)
+ clk_disable_unprepare(device->vbif_ahb_clk);
+disable_vbif_tspp2:
+ if (device->tspp2_vbif_clk)
+ clk_disable_unprepare(device->tspp2_vbif_clk);
+
+ return ret;
+}
+
+static void tspp2_vbif_clock_stop(struct tspp2_device *device)
+{
+ if (device->tspp2_vbif_clk)
+ clk_disable_unprepare(device->tspp2_vbif_clk);
+
+ if (device->vbif_ahb_clk)
+ clk_disable_unprepare(device->vbif_ahb_clk);
+
+ if (device->vbif_axi_clk)
+ clk_disable_unprepare(device->vbif_axi_clk);
+}
+
/**
* tspp2_tsif_stop() - Stop TSIF device HW.
*
@@ -1492,14 +1871,6 @@ static int tspp2_clock_start(struct tspp2_device *device)
tspp2_core_clk = 1;
}
- if (device->tspp2_vbif_clk) {
- if (clk_prepare_enable(device->tspp2_vbif_clk) != 0) {
- pr_err("%s: Can't start tspp2_vbif_clk\n", __func__);
- goto err_clocks;
- }
- tspp2_vbif_clk = 1;
- }
-
if (device->tspp2_klm_ahb_clk) {
if (clk_prepare_enable(device->tspp2_klm_ahb_clk) != 0) {
pr_err("%s: Can't start tspp2_klm_ahb_clk\n", __func__);
@@ -1566,9 +1937,6 @@ static void tspp2_clock_stop(struct tspp2_device *device)
if (device->tspp2_klm_ahb_clk)
clk_disable_unprepare(device->tspp2_klm_ahb_clk);
- if (device->tspp2_vbif_clk)
- clk_disable_unprepare(device->tspp2_vbif_clk);
-
if (device->tspp2_core_clk)
clk_disable_unprepare(device->tspp2_core_clk);
@@ -1820,17 +2188,21 @@ static void tspp2_event_work_handler(struct work_struct *work)
{
struct tspp2_event_work *event_work =
container_of(work, struct tspp2_event_work, work);
+ struct tspp2_event_work cb_info = *event_work;
if (mutex_lock_interruptible(&event_work->device->mutex))
return;
- if (event_work->callback)
- event_work->callback(event_work->cookie,
- event_work->event_bitmask);
-
list_add_tail(&event_work->link, &event_work->device->free_work_list);
mutex_unlock(&event_work->device->mutex);
+
+ /*
+ * Must run callback with tspp2 device mutex unlocked,
+ * as callback might call tspp2 driver API and cause a deadlock.
+ */
+ if (cb_info.callback)
+ cb_info.callback(cb_info.cookie, cb_info.event_bitmask);
}
/**
@@ -2013,10 +2385,10 @@ static int tspp2_src_disable_internal(struct tspp2_src *src)
}
/*
- * HW requires we wait for up to 1ms here before closing the pipes
+ * HW requires we wait for up to 2ms here before closing the pipes
* attached to (and used by) this source
*/
- udelay(1000);
+ udelay(TSPP2_HW_DELAY_USEC);
src->enabled = 0;
src->device->num_enabled_sources--;
@@ -2069,6 +2441,11 @@ int tspp2_device_open(u32 dev_id)
if (rc)
goto err_mutex_unlock;
+ /* Reset TSPP2 core */
+ clk_reset(device->tspp2_core_clk, CLK_RESET_ASSERT);
+ udelay(10);
+ clk_reset(device->tspp2_core_clk, CLK_RESET_DEASSERT);
+
/* Start HW clocks before accessing registers */
rc = tspp2_reg_clock_start(device);
if (rc)
@@ -2625,6 +3002,7 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe)
int partition = 0;
int hlos_group_attached = 0;
int cpz_group_attached = 0;
+ int vbif_clk_started = 0;
if (pipe->cfg.is_secure) {
domain = pipe->device->iommu_info.cpz_domain_num;
@@ -2647,6 +3025,18 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe)
__func__, ret);
return ret;
}
+
+ if ((pipe->device->num_secured_opened_pipes +
+ pipe->device->num_non_secured_opened_pipes) == 0) {
+ ret = tspp2_vbif_clock_start(pipe->device);
+ if (ret) {
+ pr_err(
+ "%s: tspp2_vbif_clock_start failed, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+ vbif_clk_started = 1;
+ }
} else {
/*
* We need to attach the group to enable the IOMMU and support
@@ -2661,6 +3051,14 @@ static int tspp2_pipe_memory_init(struct tspp2_pipe *pipe)
*/
if ((pipe->device->num_secured_opened_pipes +
pipe->device->num_non_secured_opened_pipes) == 0) {
+ ret = tspp2_vbif_clock_start(pipe->device);
+ if (ret) {
+ pr_err("%s: tspp2_vbif_clock_start failed, ret=%d\n",
+ __func__, ret);
+ goto err_out;
+ }
+ vbif_clk_started = 1;
+
pr_debug("%s: attaching HLOS group\n", __func__);
ret = iommu_attach_group(
pipe->device->iommu_info.hlos_domain,
@@ -2725,6 +3123,9 @@ err_out:
pipe->device->iommu_info.cpz_group);
}
+ if (vbif_clk_started)
+ tspp2_vbif_clock_stop(pipe->device);
+
return ret;
}
@@ -2773,7 +3174,11 @@ static void tspp2_pipe_memory_terminate(struct tspp2_pipe *pipe)
iommu_detach_group(
pipe->device->iommu_info.hlos_domain,
pipe->device->iommu_info.hlos_group);
+ tspp2_vbif_clock_stop(pipe->device);
}
+ } else if ((pipe->device->num_secured_opened_pipes +
+ pipe->device->num_non_secured_opened_pipes) == 0) {
+ tspp2_vbif_clock_stop(pipe->device);
}
pipe->iova = 0;
@@ -5227,10 +5632,10 @@ int tspp2_filter_disable(u32 filter_handle)
TSPP2_FILTER_ENTRY0(filter->hw_index));
/*
- * HW requires we wait for up to 1ms here before closing the pipes
+ * HW requires we wait for up to 2ms here before closing the pipes
* used by this filter
*/
- udelay(1000);
+ udelay(TSPP2_HW_DELAY_USEC);
filter->enabled = 0;
@@ -5957,10 +6362,10 @@ static int tspp2_filter_ops_update(struct tspp2_filter *filter,
TSPP2_FILTER_ENTRY0(filter->hw_index));
/*
- * HW requires we wait for up to 1ms here before removing the
+ * HW requires we wait for up to 2ms here before removing the
* operations used by this filter.
*/
- udelay(1000);
+ udelay(TSPP2_HW_DELAY_USEC);
tspp2_filter_ops_clear(filter);
@@ -6947,6 +7352,69 @@ int tspp2_filter_event_notification_register(u32 filter_handle,
}
EXPORT_SYMBOL(tspp2_filter_event_notification_register);
+/**
+ * tspp2_get_filter_hw_index() - Get a filter's hardware index.
+ *
+ * @filter_handle: Filter handle.
+ *
+ * This is an helper function to support tspp2 auto-testing.
+ *
+ * Return the filter's hardware index on success, error value otherwise.
+ */
+int tspp2_get_filter_hw_index(u32 filter_handle)
+{
+ struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
+ if (!filter_handle)
+ return -EINVAL;
+ return filter->hw_index;
+}
+EXPORT_SYMBOL(tspp2_get_filter_hw_index);
+
+/**
+ * tspp2_get_reserved_hw_index() - Get a source's reserved hardware index.
+ *
+ * @src_handle: Source handle.
+ *
+ * This is an helper function to support tspp2 auto-testing.
+ *
+ * Return the source's reserved hardware index on success,
+ * error value otherwise.
+ */
+int tspp2_get_reserved_hw_index(u32 src_handle)
+{
+ struct tspp2_src *src = (struct tspp2_src *)src_handle;
+ if (!src_handle)
+ return -EINVAL;
+ return src->reserved_filter_hw_index;
+}
+EXPORT_SYMBOL(tspp2_get_reserved_hw_index);
+
+/**
+ * tspp2_get_ops_array() - Get filter's operations.
+ *
+ * @filter_handle: Filter handle.
+ * @ops_array: The filter's operations.
+ * @num_of_ops: The filter's number of operations.
+ *
+ * This is an helper function to support tspp2 auto-testing.
+ *
+ * Return 0 on success, error value otherwise.
+ */
+int tspp2_get_ops_array(u32 filter_handle,
+ struct tspp2_operation ops_array[TSPP2_MAX_OPS_PER_FILTER],
+ u8 *num_of_ops)
+{
+ int i;
+ struct tspp2_filter *filter = (struct tspp2_filter *)filter_handle;
+ if (!filter_handle || !num_of_ops)
+ return -EINVAL;
+ *num_of_ops = filter->num_user_operations;
+ for (i = 0; i < *num_of_ops; i++)
+ ops_array[i] = filter->operations[i];
+ return 0;
+}
+EXPORT_SYMBOL(tspp2_get_ops_array);
+
/* Platform driver related functions: */
/**
@@ -7014,28 +7482,28 @@ msm_tspp2_dt_to_pdata(struct platform_device *pdev)
}
/* Get IOMMU information */
- rc = of_property_read_string(node, "qti,iommu-hlos-group",
+ rc = of_property_read_string(node, "qcom,iommu-hlos-group",
&data->hlos_group);
if (rc) {
pr_err("%s: Could not find iommu-hlos-group property, err = %d\n",
__func__, rc);
return NULL;
}
- rc = of_property_read_string(node, "qti,iommu-cpz-group",
+ rc = of_property_read_string(node, "qcom,iommu-cpz-group",
&data->cpz_group);
if (rc) {
pr_err("%s: Could not find iommu-cpz-group property, err = %d\n",
__func__, rc);
return NULL;
}
- rc = of_property_read_u32(node, "qti,iommu-hlos-partition",
+ rc = of_property_read_u32(node, "qcom,iommu-hlos-partition",
&data->hlos_partition);
if (rc) {
pr_err("%s: Could not find iommu-hlos-partition property, err = %d\n",
__func__, rc);
return NULL;
}
- rc = of_property_read_u32(node, "qti,iommu-cpz-partition",
+ rc = of_property_read_u32(node, "qcom,iommu-cpz-partition",
&data->cpz_partition);
if (rc) {
pr_err("%s: Could not find iommu-cpz-partition property, err = %d\n",
@@ -7154,6 +7622,12 @@ static void tspp2_clocks_put(struct tspp2_device *device)
if (device->tspp2_vbif_clk)
clk_put(device->tspp2_vbif_clk);
+ if (device->vbif_ahb_clk)
+ clk_put(device->vbif_ahb_clk);
+
+ if (device->vbif_axi_clk)
+ clk_put(device->vbif_axi_clk);
+
if (device->tspp2_core_clk)
clk_put(device->tspp2_core_clk);
@@ -7163,6 +7637,8 @@ static void tspp2_clocks_put(struct tspp2_device *device)
device->tspp2_ahb_clk = NULL;
device->tspp2_core_clk = NULL;
device->tspp2_vbif_clk = NULL;
+ device->vbif_ahb_clk = NULL;
+ device->vbif_axi_clk = NULL;
device->tspp2_klm_ahb_clk = NULL;
device->tsif_ref_clk = NULL;
}
@@ -7196,6 +7672,8 @@ static int msm_tspp2_clocks_setup(struct platform_device *pdev,
device->tspp2_ahb_clk = NULL;
device->tspp2_core_clk = NULL;
device->tspp2_vbif_clk = NULL;
+ device->vbif_ahb_clk = NULL;
+ device->vbif_axi_clk = NULL;
device->tspp2_klm_ahb_clk = NULL;
device->tsif_ref_clk = NULL;
@@ -7235,6 +7713,22 @@ static int msm_tspp2_clocks_setup(struct platform_device *pdev,
}
}
+ device->vbif_ahb_clk = clk_get(&pdev->dev, "iface_vbif_clk");
+ if (IS_ERR(device->vbif_ahb_clk)) {
+ pr_err("%s: Failed to get %s", __func__, "iface_vbif_clk");
+ ret = PTR_ERR(device->vbif_ahb_clk);
+ device->vbif_ahb_clk = NULL;
+ goto err_clocks;
+ }
+
+ device->vbif_axi_clk = clk_get(&pdev->dev, "vbif_core_clk");
+ if (IS_ERR(device->vbif_axi_clk)) {
+ pr_err("%s: Failed to get %s", __func__, "vbif_core_clk");
+ ret = PTR_ERR(device->vbif_axi_clk);
+ device->vbif_axi_clk = NULL;
+ goto err_clocks;
+ }
+
if (data->tspp2_klm_ahb_clk) {
device->tspp2_klm_ahb_clk =
clk_get(&pdev->dev, data->tspp2_klm_ahb_clk);
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index fc746f2721a3..1e1d59995af7 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -64,8 +64,13 @@ static int camera_check_event_status(struct v4l2_event *event)
struct msm_v4l2_event_data *event_data =
(struct msm_v4l2_event_data *)&event->u.data[0];
- if (event_data->status > MSM_CAMERA_ERR_EVT_BASE)
+ if (event_data->status > MSM_CAMERA_ERR_EVT_BASE) {
+ pr_err("%s : event_data status out of bounds\n",
+ __func__);
+ pr_err("%s : Line %d event_data->status 0X%x\n",
+ __func__, __LINE__, event_data->status);
return -EFAULT;
+ }
return 0;
}
@@ -462,8 +467,10 @@ static int camera_v4l2_fh_open(struct file *filep)
unsigned int stream_id;
sp = kzalloc(sizeof(*sp), GFP_KERNEL);
- if (!sp)
+ if (!sp) {
+ pr_err("%s : memory not available\n", __func__);
return -ENOMEM;
+ }
filep->private_data = &sp->fh;
@@ -502,8 +509,10 @@ static int camera_v4l2_vb2_q_init(struct file *filep)
/* free up this buffer when stream is done */
q->drv_priv =
kzalloc(sizeof(struct msm_v4l2_format_data), GFP_KERNEL);
- if (!q->drv_priv)
+ if (!q->drv_priv) {
+ pr_err("%s : memory not available\n", __func__);
return -ENOMEM;
+ }
q->mem_ops = msm_vb2_get_q_mem_ops();
q->ops = msm_vb2_get_q_ops();
@@ -534,44 +543,67 @@ static int camera_v4l2_open(struct file *filep)
BUG_ON(!pvdev);
rc = camera_v4l2_fh_open(filep);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
goto fh_open_fail;
+ }
opn_idx = atomic_read(&pvdev->opened);
idx = opn_idx;
/* every stream has a vb2 queue */
rc = camera_v4l2_vb2_q_init(filep);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : vb2 queue init fails Line %d rc %d\n",
+ __func__, __LINE__, rc);
goto vb2_q_fail;
+ }
if (!atomic_read(&pvdev->opened)) {
pm_stay_awake(&pvdev->vdev->dev);
/* create a new session when first opened */
rc = msm_create_session(pvdev->vdev->num, pvdev->vdev);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : session creation failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
goto session_fail;
+ }
rc = msm_create_command_ack_q(pvdev->vdev->num,
find_first_zero_bit(&opn_idx,
MSM_CAMERA_STREAM_CNT_BITS));
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : creation of command_ack queue failed\n",
+ __func__);
+ pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc);
goto command_ack_q_fail;
+ }
camera_pack_event(filep, MSM_CAMERA_NEW_SESSION, 0, -1, &event);
rc = msm_post_event(&event, MSM_POST_EVT_TIMEOUT);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : posting of NEW_SESSION event failed\n",
+ __func__);
+ pr_err("%s : Line %d rc %d\n", __func__, __LINE__, rc);
goto post_fail;
+ }
rc = camera_check_event_status(&event);
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : checking event status fails Line %d rc %d\n",
+ __func__, __LINE__, rc);
goto post_fail;
+ }
} else {
rc = msm_create_command_ack_q(pvdev->vdev->num,
find_first_zero_bit(&opn_idx,
MSM_CAMERA_STREAM_CNT_BITS));
- if (rc < 0)
+ if (rc < 0) {
+ pr_err("%s : creation of command_ack queue failed Line %d rc %d\n",
+ __func__, __LINE__, rc);
goto session_fail;
+ }
}
pr_debug("%s: Open stream_id=%d\n", __func__,
find_first_zero_bit(&opn_idx, MSM_CAMERA_STREAM_CNT_BITS));
diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
index 7de0dacc444c..557333355956 100644
--- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
+++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
@@ -491,7 +491,7 @@ long msm_isp_ioctl(struct v4l2_subdev *sd,
break;
default:
- pr_err("%s: Invalid ISP command\n", __func__);
+ pr_err_ratelimited("%s: Invalid ISP command\n", __func__);
rc = -EINVAL;
}
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
index 26ec2829977b..be955f60560a 100644
--- a/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
+++ b/drivers/media/platform/msm/camera_v2/ispif/msm_ispif.c
@@ -200,7 +200,7 @@ static int msm_ispif_reset(struct ispif_device *ispif)
ispif->base + ISPIF_VFE_m_INTF_CMD_0(i));
msm_camera_io_w(ISPIF_STOP_INTF_IMMEDIATELY,
ispif->base + ISPIF_VFE_m_INTF_CMD_1(i));
- pr_debug("%s: base %x", __func__, (unsigned int)ispif->base);
+ pr_debug("%s: base %lx", __func__, (unsigned long)ispif->base);
msm_camera_io_w(0, ispif->base +
ISPIF_VFE_m_PIX_INTF_n_CID_MASK(i, 0));
msm_camera_io_w(0, ispif->base +
@@ -965,6 +965,7 @@ end:
static void msm_ispif_release(struct ispif_device *ispif)
{
BUG_ON(!ispif);
+ BUG_ON(!ispif->base);
if (ispif->ispif_state != ISPIF_POWER_UP) {
pr_err("%s: ispif invalid state %d\n", __func__,
@@ -1049,7 +1050,8 @@ static long msm_ispif_subdev_ioctl(struct v4l2_subdev *sd,
return 0;
}
default:
- pr_err("%s: invalid cmd 0x%x received\n", __func__, cmd);
+ pr_err_ratelimited("%s: invalid cmd 0x%x received\n",
+ __func__, cmd);
return -ENOIOCTLCMD;
}
}
@@ -1114,29 +1116,6 @@ static int ispif_probe(struct platform_device *pdev)
return -ENOMEM;
}
- v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops);
- ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops;
- ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
-
- snprintf(ispif->msm_sd.sd.name,
- ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME);
- v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
-
- platform_set_drvdata(pdev, &ispif->msm_sd.sd);
- mutex_init(&ispif->mutex);
-
- media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0);
- ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
- ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF;
- ispif->msm_sd.sd.entity.name = pdev->name;
- ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1;
- rc = msm_sd_register(&ispif->msm_sd);
- if (rc) {
- pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
- goto error_sd_register;
- }
-
-
if (pdev->dev.of_node) {
of_property_read_u32((&pdev->dev)->of_node,
"cell-index", &pdev->id);
@@ -1149,6 +1128,7 @@ static int ispif_probe(struct platform_device *pdev)
rc = 0;
}
+ mutex_init(&ispif->mutex);
ispif->mem = platform_get_resource_byname(pdev,
IORESOURCE_MEM, "ispif");
if (!ispif->mem) {
@@ -1181,15 +1161,33 @@ static int ispif_probe(struct platform_device *pdev)
pr_err("%s: no valid csi_mux region\n", __func__);
}
+ v4l2_subdev_init(&ispif->msm_sd.sd, &msm_ispif_subdev_ops);
+ ispif->msm_sd.sd.internal_ops = &msm_ispif_internal_ops;
+ ispif->msm_sd.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ snprintf(ispif->msm_sd.sd.name,
+ ARRAY_SIZE(ispif->msm_sd.sd.name), MSM_ISPIF_DRV_NAME);
+ v4l2_set_subdevdata(&ispif->msm_sd.sd, ispif);
+
+ platform_set_drvdata(pdev, &ispif->msm_sd.sd);
+
+ media_entity_init(&ispif->msm_sd.sd.entity, 0, NULL, 0);
+ ispif->msm_sd.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
+ ispif->msm_sd.sd.entity.group_id = MSM_CAMERA_SUBDEV_ISPIF;
+ ispif->msm_sd.sd.entity.name = pdev->name;
+ ispif->msm_sd.close_seq = MSM_SD_CLOSE_1ST_CATEGORY | 0x1;
+ rc = msm_sd_register(&ispif->msm_sd);
+ if (rc) {
+ pr_err("%s: msm_sd_register error = %d\n", __func__, rc);
+ goto error;
+ }
+
ispif->pdev = pdev;
ispif->ispif_state = ISPIF_POWER_DOWN;
ispif->open_cnt = 0;
-
return 0;
error:
- msm_sd_unregister(&ispif->msm_sd);
-error_sd_register:
mutex_destroy(&ispif->mutex);
kfree(ispif);
return rc;
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
index 44a40144067b..ddf9261e727c 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_hw.c
@@ -398,7 +398,7 @@ void msm_jpeg_io_dump(void *base, int size)
p_str = line_str;
for (i = 0; i < size/4; i++) {
if (i % 4 == 0) {
- snprintf(p_str, 12, "%08x: ", (u32) p);
+ snprintf(p_str, 12, "%08lx: ", (unsigned long)p);
p_str += 10;
}
data = readl_relaxed(p++);
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
index 8604b1079f18..064f52c04e12 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_platform.c
@@ -31,14 +31,19 @@
int msm_jpeg_platform_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
long clk_rate)
{
- struct msm_cam_clk_info jpeg_core_clk_info[] = {
- {"core_clk", JPEG_CLK_RATE, 0}
- };
+ int rc = 0;
+ struct clk *jpeg_clk;
+
+ jpeg_clk = clk_get(&pgmn_dev->pdev->dev, "core_clk");
+ if (IS_ERR(jpeg_clk)) {
+ JPEG_PR_ERR("%s get failed\n", "core_clk");
+ rc = PTR_ERR(jpeg_clk);
+ return rc;
+ }
- jpeg_core_clk_info[0].clk_rate = clk_rate;
+ rc = clk_set_rate(jpeg_clk, clk_rate);
- return msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_core_clk_info,
- pgmn_dev->jpeg_clk, ARRAY_SIZE(jpeg_core_clk_info), 1);
+ return rc;
}
void msm_jpeg_platform_p2v(struct msm_jpeg_device *pgmn_dev, struct file *file,
@@ -170,6 +175,50 @@ static struct msm_bus_scale_pdata msm_jpeg_bus_client_pdata = {
.name = "msm_jpeg",
};
+#ifdef CONFIG_MSM_IOMMU
+static int msm_jpeg_attach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ int i;
+
+ for (i = 0; i < pgmn_dev->iommu_cnt; i++) {
+ int rc = iommu_attach_device(pgmn_dev->domain,
+ pgmn_dev->iommu_ctx_arr[i]);
+ if (rc < 0) {
+ JPEG_PR_ERR("%s: Device attach failed\n", __func__);
+ return -ENODEV;
+ }
+ JPEG_DBG("%s:%d] dom 0x%lx ctx 0x%lx", __func__, __LINE__,
+ (unsigned long)pgmn_dev->domain,
+ (unsigned long)pgmn_dev->iommu_ctx_arr[i]);
+ }
+ return 0;
+}
+static int msm_jpeg_detach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ int i;
+
+ for (i = 0; i < pgmn_dev->iommu_cnt; i++) {
+ JPEG_DBG("%s:%d] dom 0x%lx ctx 0x%lx", __func__, __LINE__,
+ (unsigned long)pgmn_dev->domain,
+ (unsigned long)pgmn_dev->iommu_ctx_arr[i]);
+ iommu_detach_device(pgmn_dev->domain,
+ pgmn_dev->iommu_ctx_arr[i]);
+ }
+ return 0;
+}
+#else
+static int msm_jpeg_attach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ return 0;
+}
+static int msm_jpeg_detach_iommu(struct msm_jpeg_device *pgmn_dev)
+{
+ return 0;
+}
+#endif
+
+
+
int msm_jpeg_platform_init(struct platform_device *pdev,
struct resource **mem,
void **base,
@@ -178,7 +227,6 @@ int msm_jpeg_platform_init(struct platform_device *pdev,
void *context)
{
int rc = -1;
- int i = 0;
int jpeg_irq;
struct resource *jpeg_mem, *jpeg_io, *jpeg_irq_res;
void *jpeg_base;
@@ -255,20 +303,10 @@ int msm_jpeg_platform_init(struct platform_device *pdev,
JPEG_DBG("%s:%d] jpeg_vbif 0x%x", __func__, __LINE__,
(uint32_t)pgmn_dev->jpeg_vbif);
-#ifdef CONFIG_MSM_IOMMU
- for (i = 0; i < pgmn_dev->iommu_cnt; i++) {
- rc = iommu_attach_device(pgmn_dev->domain,
- pgmn_dev->iommu_ctx_arr[i]);
- if (rc < 0) {
- rc = -ENODEV;
- JPEG_PR_ERR("%s: Device attach failed\n", __func__);
- goto fail_iommu;
- }
- JPEG_DBG("%s:%d] dom 0x%x ctx 0x%x", __func__, __LINE__,
- (uint32_t)pgmn_dev->domain,
- (uint32_t)pgmn_dev->iommu_ctx_arr[i]);
- }
-#endif
+ rc = msm_jpeg_attach_iommu(pgmn_dev);
+ if (rc < 0)
+ goto fail_iommu;
+
set_vbif_params(pgmn_dev, pgmn_dev->jpeg_vbif);
rc = request_irq(jpeg_irq, handler, IRQF_TRIGGER_RISING, "jpeg",
@@ -290,19 +328,12 @@ int msm_jpeg_platform_init(struct platform_device *pdev,
return rc;
fail_request_irq:
-#ifdef CONFIG_MSM_IOMMU
- for (i = 0; i < pgmn_dev->iommu_cnt; i++) {
- JPEG_PR_ERR("%s:%d] dom 0x%x ctx 0x%x", __func__, __LINE__,
- (uint32_t)pgmn_dev->domain,
- (uint32_t)pgmn_dev->iommu_ctx_arr[i]);
- iommu_detach_device(pgmn_dev->domain,
- pgmn_dev->iommu_ctx_arr[i]);
- }
-#endif
+ msm_jpeg_detach_iommu(pgmn_dev);
fail_iommu:
iounmap(pgmn_dev->jpeg_vbif);
+
fail_vbif:
msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_8x_clk_info,
pgmn_dev->jpeg_clk, ARRAY_SIZE(jpeg_8x_clk_info), 0);
@@ -329,19 +360,13 @@ int msm_jpeg_platform_release(struct resource *mem, void *base, int irq,
void *context)
{
int result = 0;
- int i = 0;
+
struct msm_jpeg_device *pgmn_dev =
(struct msm_jpeg_device *) context;
free_irq(irq, context);
-#ifdef CONFIG_MSM_IOMMU
- for (i = 0; i < pgmn_dev->iommu_cnt; i++) {
- iommu_detach_device(pgmn_dev->domain,
- pgmn_dev->iommu_ctx_arr[i]);
- JPEG_DBG("%s:%d]", __func__, __LINE__);
- }
-#endif
+ msm_jpeg_detach_iommu(pgmn_dev);
msm_bus_scale_unregister_client(pgmn_dev->jpeg_bus_client);
msm_cam_clk_enable(&pgmn_dev->pdev->dev, jpeg_8x_clk_info,
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
index 5cc51ffa5733..9ec7d87e698b 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/msm_jpeg_sync.c
@@ -105,7 +105,7 @@ inline int msm_jpeg_q_in_buf(struct msm_jpeg_q *q_p,
inline int msm_jpeg_q_wait(struct msm_jpeg_q *q_p)
{
- int tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */
+ long tm = MAX_SCHEDULE_TIMEOUT; /* 500ms */
int rc;
JPEG_DBG("%s:%d] %s wait\n", __func__, __LINE__, q_p->name);
@@ -807,7 +807,7 @@ int msm_jpeg_ioctl_test_dump_region(struct msm_jpeg_device *pgmn_dev,
}
int msm_jpeg_ioctl_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
- unsigned long arg)
+ void * __user arg)
{
long clk_rate;
int rc;
@@ -817,7 +817,7 @@ int msm_jpeg_ioctl_set_clk_rate(struct msm_jpeg_device *pgmn_dev,
JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
return -EFAULT;
}
- if (get_user(clk_rate, (long __user *)arg)) {
+ if (get_user(clk_rate, (unsigned int __user *)arg)) {
JPEG_PR_ERR("%s:%d] failed\n", __func__, __LINE__);
return -EFAULT;
}
@@ -906,7 +906,7 @@ long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev,
break;
case MSM_JPEG_IOCTL_SET_CLK_RATE:
- rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev, arg);
+ rc = msm_jpeg_ioctl_set_clk_rate(pgmn_dev, (void __user *) arg);
break;
default:
JPEG_PR_ERR(KERN_INFO "%s:%d] cmd = %d not supported\n",
@@ -916,7 +916,7 @@ long __msm_jpeg_ioctl(struct msm_jpeg_device *pgmn_dev,
}
return rc;
}
-
+#ifdef CONFIG_MSM_IOMMU
static int camera_register_domain(void)
{
struct msm_iova_partition camera_fw_partition = {
@@ -932,13 +932,17 @@ static int camera_register_domain(void)
};
return msm_register_domain(&camera_fw_layout);
}
+#endif
int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev)
{
- int rc = 0, i = 0, j = 0;
+ int rc = 0;
int idx = 0;
+#ifdef CONFIG_MSM_IOMMU
+ int i = 0, j = 0;
char *iommu_name[JPEG_DEV_CNT] = {"jpeg_enc0", "jpeg_enc1",
"jpeg_dec"};
+#endif
mutex_init(&pgmn_dev->lock);
@@ -987,7 +991,9 @@ int __msm_jpeg_init(struct msm_jpeg_device *pgmn_dev)
#endif
return rc;
+#ifdef CONFIG_MSM_IOMMU
error:
+#endif
mutex_destroy(&pgmn_dev->lock);
return -EFAULT;
}
diff --git a/drivers/media/platform/msm/camera_v2/msm.c b/drivers/media/platform/msm/camera_v2/msm.c
index a7f135b28097..4ac2b226ebb7 100644
--- a/drivers/media/platform/msm/camera_v2/msm.c
+++ b/drivers/media/platform/msm/camera_v2/msm.c
@@ -341,17 +341,26 @@ int msm_create_session(unsigned int session_id, struct video_device *vdev)
{
struct msm_session *session = NULL;
- if (!msm_session_q)
+ if (!msm_session_q) {
+ pr_err("%s : session queue not available Line %d\n",
+ __func__, __LINE__);
return -ENODEV;
+ }
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
- if (session)
+ if (session) {
+ pr_err("%s : Session not found Line %d\n",
+ __func__, __LINE__);
return -EINVAL;
+ }
session = kzalloc(sizeof(*session), GFP_KERNEL);
- if (!session)
+ if (!session) {
+ pr_err("%s : Memory not available Line %d\n",
+ __func__, __LINE__);
return -ENOMEM;
+ }
session->session_id = session_id;
session->event_q.vdev = vdev;
@@ -367,17 +376,25 @@ int msm_create_command_ack_q(unsigned int session_id, unsigned int stream_id)
struct msm_session *session;
struct msm_command_ack *cmd_ack;
- if (!msm_session_q)
+ if (!msm_session_q) {
+ pr_err("%s : Session queue not available Line %d\n",
+ __func__, __LINE__);
return -ENODEV;
+ }
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
- if (!session)
+ if (!session) {
+ pr_err("%s : Session not found Line %d\n",
+ __func__, __LINE__);
return -EINVAL;
+ }
mutex_lock(&session->lock);
cmd_ack = kzalloc(sizeof(*cmd_ack), GFP_KERNEL);
if (!cmd_ack) {
mutex_unlock(&session->lock);
+ pr_err("%s : memory not available Line %d\n",
+ __func__, __LINE__);
return -ENOMEM;
}
@@ -664,6 +681,8 @@ int msm_post_event(struct v4l2_event *event, int timeout)
spin_lock_irqsave(&msm_eventq_lock, flags);
if (!msm_eventq) {
spin_unlock_irqrestore(&msm_eventq_lock, flags);
+ pr_err("%s : msm event queue not available Line %d\n",
+ __func__, __LINE__);
return -ENODEV;
}
spin_unlock_irqrestore(&msm_eventq_lock, flags);
@@ -673,14 +692,19 @@ int msm_post_event(struct v4l2_event *event, int timeout)
/* send to imaging server and wait for ACK */
session = msm_queue_find(msm_session_q, struct msm_session,
list, __msm_queue_find_session, &session_id);
- if (WARN_ON(!session))
+ if (WARN_ON(!session)) {
+ pr_err("%s : session not found Line %d\n",
+ __func__, __LINE__);
return -EIO;
+ }
mutex_lock(&session->lock);
cmd_ack = msm_queue_find(&session->command_ack_q,
struct msm_command_ack, list,
__msm_queue_find_command_ack_q, &stream_id);
if (WARN_ON(!cmd_ack)) {
mutex_unlock(&session->lock);
+ pr_err("%s : cmd_ack not found Line %d\n",
+ __func__, __LINE__);
return -EIO;
}
@@ -688,6 +712,8 @@ int msm_post_event(struct v4l2_event *event, int timeout)
if (timeout < 0) {
mutex_unlock(&session->lock);
+ pr_err("%s : timeout cannot be negative Line %d\n",
+ __func__, __LINE__);
return rc;
}
@@ -718,6 +744,8 @@ int msm_post_event(struct v4l2_event *event, int timeout)
struct msm_command, list);
if (!cmd) {
mutex_unlock(&session->lock);
+ pr_err("%s : cmd dequeue failed Line %d\n",
+ __func__, __LINE__);
return -EINVAL;
}
@@ -725,8 +753,15 @@ int msm_post_event(struct v4l2_event *event, int timeout)
/* compare cmd_ret and event */
if (WARN_ON(event->type != cmd->event.type) ||
- WARN_ON(event->id != cmd->event.id))
+ WARN_ON(event->id != cmd->event.id)) {
+ pr_err("%s : Either event type or id didnot match Line %d\n",
+ __func__, __LINE__);
+ pr_err("%s : event->type %d event->id %d\n", __func__,
+ event->type, event->id);
+ pr_err("%s : cmd->event.type %d cmd->event.id %d\n", __func__,
+ cmd->event.type, cmd->event.id);
rc = -EINVAL;
+ }
*event = cmd->event;
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
index 2a72845a4581..40e51647583a 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.c
@@ -18,7 +18,7 @@ struct v4l2_subdev *msm_buf_mngr_get_subdev(void)
return &msm_buf_mngr_dev->subdev.sd;
}
-static int msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev,
+static int32_t msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev,
void __user *argp)
{
unsigned long flags;
@@ -48,12 +48,12 @@ static int msm_buf_mngr_get_buf(struct msm_buf_mngr_device *buf_mngr_dev,
return 0;
}
-static int msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
+static int32_t msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
struct msm_buf_mngr_info *buf_info)
{
unsigned long flags;
struct msm_get_bufs *bufs, *save;
- int ret = -EINVAL;
+ int32_t ret = -EINVAL;
spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
@@ -77,12 +77,12 @@ static int msm_buf_mngr_buf_done(struct msm_buf_mngr_device *buf_mngr_dev,
}
-static int msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
+static int32_t msm_buf_mngr_put_buf(struct msm_buf_mngr_device *buf_mngr_dev,
struct msm_buf_mngr_info *buf_info)
{
unsigned long flags;
struct msm_get_bufs *bufs, *save;
- int ret = -EINVAL;
+ int32_t ret = -EINVAL;
spin_lock_irqsave(&buf_mngr_dev->buf_q_spinlock, flags);
list_for_each_entry_safe(bufs, save, &buf_mngr_dev->buf_qhead, entry) {
@@ -109,8 +109,9 @@ static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *buf_mngr_dev)
if (!list_empty(&buf_mngr_dev->buf_qhead)) {
list_for_each_entry_safe(bufs,
save, &buf_mngr_dev->buf_qhead, entry) {
- pr_err("%s: Error: Delete invalid bufs =%x\n", __func__,
- (unsigned int)bufs);
+ pr_err("%s: Error delete invalid bufs =%x, ses_id=%d, str_id=%d, idx=%d\n",
+ __func__, (unsigned int)bufs, bufs->session_id,
+ bufs->stream_id, bufs->vb2_buf->v4l2_buf.index);
list_del_init(&bufs->entry);
kfree(bufs);
}
@@ -118,10 +119,40 @@ static void msm_buf_mngr_sd_shutdown(struct msm_buf_mngr_device *buf_mngr_dev)
spin_unlock_irqrestore(&buf_mngr_dev->buf_q_spinlock, flags);
}
+static int msm_generic_buf_mngr_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ buf_mngr_dev->msm_buf_mngr_open_cnt++;
+ return rc;
+}
+
+static int msm_generic_buf_mngr_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ int rc = 0;
+ struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
+ if (!buf_mngr_dev) {
+ pr_err("%s buf manager device NULL\n", __func__);
+ rc = -ENODEV;
+ return rc;
+ }
+ buf_mngr_dev->msm_buf_mngr_open_cnt--;
+ if (buf_mngr_dev->msm_buf_mngr_open_cnt == 0)
+ msm_buf_mngr_sd_shutdown(buf_mngr_dev);
+ return rc;
+}
+
static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int cmd, void *arg)
{
- int rc = 0;
+ int32_t rc = 0;
struct msm_buf_mngr_device *buf_mngr_dev = v4l2_get_subdevdata(sd);
void __user *argp = (void __user *)arg;
@@ -141,6 +172,12 @@ static long msm_buf_mngr_subdev_ioctl(struct v4l2_subdev *sd,
case VIDIOC_MSM_BUF_MNGR_PUT_BUF:
rc = msm_buf_mngr_put_buf(buf_mngr_dev, argp);
break;
+ case VIDIOC_MSM_BUF_MNGR_INIT:
+ rc = msm_generic_buf_mngr_open(sd, NULL);
+ break;
+ case VIDIOC_MSM_BUF_MNGR_DEINIT:
+ rc = msm_generic_buf_mngr_close(sd, NULL);
+ break;
case MSM_SD_SHUTDOWN:
msm_buf_mngr_sd_shutdown(buf_mngr_dev);
break;
@@ -154,6 +191,12 @@ static struct v4l2_subdev_core_ops msm_buf_mngr_subdev_core_ops = {
.ioctl = msm_buf_mngr_subdev_ioctl,
};
+static const struct v4l2_subdev_internal_ops
+ msm_generic_buf_mngr_subdev_internal_ops = {
+ .open = msm_generic_buf_mngr_open,
+ .close = msm_generic_buf_mngr_close,
+};
+
static const struct v4l2_subdev_ops msm_buf_mngr_subdev_ops = {
.core = &msm_buf_mngr_subdev_core_ops,
};
@@ -163,9 +206,27 @@ static const struct of_device_id msm_buf_mngr_dt_match[] = {
{}
};
-static int __init msm_buf_mngr_init(void)
+static struct v4l2_file_operations msm_buf_v4l2_subdev_fops;
+
+static long msm_bmgr_subdev_do_ioctl(
+ struct file *file, unsigned int cmd, void *arg)
{
- int rc = 0;
+ struct video_device *vdev = video_devdata(file);
+ struct v4l2_subdev *sd = vdev_to_v4l2_subdev(vdev);
+
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+}
+
+
+static long msm_buf_subdev_fops_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return video_usercopy(file, cmd, arg, msm_bmgr_subdev_do_ioctl);
+}
+
+static int32_t __init msm_buf_mngr_init(void)
+{
+ int32_t rc = 0;
msm_buf_mngr_dev = kzalloc(sizeof(*msm_buf_mngr_dev),
GFP_KERNEL);
if (WARN_ON(!msm_buf_mngr_dev)) {
@@ -175,6 +236,13 @@ static int __init msm_buf_mngr_init(void)
/* Sub-dev */
v4l2_subdev_init(&msm_buf_mngr_dev->subdev.sd,
&msm_buf_mngr_subdev_ops);
+
+ msm_buf_v4l2_subdev_fops.owner = v4l2_subdev_fops.owner;
+ msm_buf_v4l2_subdev_fops.open = v4l2_subdev_fops.open;
+ msm_buf_v4l2_subdev_fops.unlocked_ioctl = msm_buf_subdev_fops_ioctl;
+ msm_buf_v4l2_subdev_fops.release = v4l2_subdev_fops.release;
+ msm_buf_v4l2_subdev_fops.poll = v4l2_subdev_fops.poll;
+
snprintf(msm_buf_mngr_dev->subdev.sd.name,
ARRAY_SIZE(msm_buf_mngr_dev->subdev.sd.name), "msm_buf_mngr");
msm_buf_mngr_dev->subdev.sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -184,6 +252,8 @@ static int __init msm_buf_mngr_init(void)
msm_buf_mngr_dev->subdev.sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
msm_buf_mngr_dev->subdev.sd.entity.group_id =
MSM_CAMERA_SUBDEV_BUF_MNGR;
+ msm_buf_mngr_dev->subdev.sd.internal_ops =
+ &msm_generic_buf_mngr_subdev_internal_ops;
msm_buf_mngr_dev->subdev.close_seq = MSM_SD_CLOSE_4TH_CATEGORY;
rc = msm_sd_register(&msm_buf_mngr_dev->subdev);
if (rc != 0) {
@@ -191,6 +261,8 @@ static int __init msm_buf_mngr_init(void)
goto end;
}
+ msm_buf_mngr_dev->subdev.sd.devnode->fops = &msm_buf_v4l2_subdev_fops;
+
v4l2_subdev_notify(&msm_buf_mngr_dev->subdev.sd, MSM_SD_NOTIFY_REQ_CB,
&msm_buf_mngr_dev->vb2_ops);
diff --git a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
index 56886cd94843..49fad229ca0c 100644
--- a/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
+++ b/drivers/media/platform/msm/camera_v2/msm_buf_mgr/msm_generic_buf_mgr.h
@@ -36,5 +36,6 @@ struct msm_buf_mngr_device {
spinlock_t buf_q_spinlock;
struct msm_sd_subdev subdev;
struct msm_sd_req_vb2_q vb2_ops;
+ uint32_t msm_buf_mngr_open_cnt;
};
#endif
diff --git a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
index cfb9321eb30e..a93f02b7f892 100644
--- a/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
+++ b/drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
@@ -70,6 +70,9 @@
#define CPP_CLK_INFO_MAX 16
+static int msm_cpp_buffer_ops(struct cpp_device *cpp_dev,
+ uint32_t buff_mgr_ops, struct msm_buf_mngr_info *buff_mgr_info);
+
#if CONFIG_MSM_CPP_DBG
#define CPP_DBG(fmt, args...) pr_err(fmt, ##args)
#else
@@ -739,6 +742,14 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev)
goto req_irq_fail;
}
cpp_dev->buf_mgr_subdev = msm_buf_mngr_get_subdev();
+
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_INIT, NULL);
+ if (rc < 0) {
+ pr_err("buf mngr init failed\n");
+ free_irq(cpp_dev->irq->start, cpp_dev);
+ goto req_irq_fail;
+ }
}
cpp_dev->hw_info.cpp_hw_version =
@@ -783,7 +794,14 @@ bus_scale_register_failed:
static void cpp_release_hardware(struct cpp_device *cpp_dev)
{
+ int32_t rc;
if (cpp_dev->state != CPP_STATE_BOOT) {
+ rc = msm_cpp_buffer_ops(cpp_dev,
+ VIDIOC_MSM_BUF_MNGR_DEINIT, NULL);
+ if (rc < 0) {
+ pr_err("error in buf mngr deinit\n");
+ rc = -EINVAL;
+ }
free_irq(cpp_dev->irq->start, cpp_dev);
tasklet_kill(&cpp_dev->cpp_tasklet);
atomic_set(&cpp_dev->irq_cnt, 0);
@@ -1249,7 +1267,7 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev,
uint32_t *cpp_frame_msg;
unsigned long in_phyaddr, out_phyaddr0, out_phyaddr1;
uint16_t num_stripes = 0;
- struct msm_buf_mngr_info buff_mgr_info;
+ struct msm_buf_mngr_info buff_mgr_info, dup_buff_mgr_info;
struct msm_cpp_frame_info_t *u_frame_info =
(struct msm_cpp_frame_info_t *)ioctl_ptr->ioctl_ptr;
int32_t status = 0;
@@ -1341,19 +1359,20 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev,
new_frame->duplicate_identity);
memset(&new_frame->output_buffer_info[1], 0,
sizeof(struct msm_cpp_buffer_info_t));
- memset(&buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
- buff_mgr_info.session_id =
+ memset(&dup_buff_mgr_info, 0, sizeof(struct msm_buf_mngr_info));
+ dup_buff_mgr_info.session_id =
((new_frame->duplicate_identity >> 16) & 0xFFFF);
- buff_mgr_info.stream_id =
+ dup_buff_mgr_info.stream_id =
(new_frame->duplicate_identity & 0xFFFF);
rc = msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_GET_BUF,
- &buff_mgr_info);
+ &dup_buff_mgr_info);
if (rc < 0) {
rc = -EAGAIN;
pr_debug("error getting buffer rc:%d\n", rc);
- goto ERROR2;
+ goto ERROR3;
}
- new_frame->output_buffer_info[1].index = buff_mgr_info.index;
+ new_frame->output_buffer_info[1].index =
+ dup_buff_mgr_info.index;
out_phyaddr1 = msm_cpp_fetch_buffer_info(cpp_dev,
&new_frame->output_buffer_info[1],
((new_frame->duplicate_identity >> 16) & 0xFFFF),
@@ -1362,6 +1381,8 @@ static int msm_cpp_cfg(struct cpp_device *cpp_dev,
if (!out_phyaddr1) {
pr_err("error gettting output physical address\n");
rc = -EINVAL;
+ msm_cpp_buffer_ops(cpp_dev, VIDIOC_MSM_BUF_MNGR_PUT_BUF,
+ &dup_buff_mgr_info);
goto ERROR3;
}
/* set duplicate enable bit */
@@ -1780,7 +1801,7 @@ long msm_cpp_subdev_ioctl(struct v4l2_subdev *sd,
break;
}
default:
- pr_err("invalid value: cmd=0x%x\n", cmd);
+ pr_err_ratelimited("invalid value: cmd=0x%x\n", cmd);
break;
}
mutex_unlock(&cpp_dev->mutex);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
index 89bb6bf6684f..08148362882d 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/cci/msm_cci.c
@@ -634,7 +634,11 @@ ERROR:
static int msm_cci_subdev_g_chip_ident(struct v4l2_subdev *sd,
struct v4l2_dbg_chip_ident *chip)
{
- BUG_ON(!chip);
+ if (!chip) {
+ pr_err("%s:%d: NULL pointer supplied for chip ident\n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
chip->ident = V4L2_IDENT_CCI;
chip->revision = 0;
return 0;
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
index a92fc7875e98..4841370f12c9 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csid/msm_csid.c
@@ -285,56 +285,16 @@ static int msm_csid_release(struct csid_device *csid_dev)
disable_irq(csid_dev->irq->start);
- if (csid_dev->hw_version == CSID_VERSION_V20) {
- msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
- csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0);
+ msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
+ csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0);
- msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
-
- msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
- } else if (csid_dev->hw_version == CSID_VERSION_V22) {
- msm_cam_clk_enable(&csid_dev->pdev->dev,
- csid_clk_info,
- csid_dev->csid_clk,
- ARRAY_SIZE(csid_clk_info), 0);
-
- msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
-
- msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
- } else if ((csid_dev->hw_version >= CSID_VERSION_V30 &&
- csid_dev->hw_version < CSID_VERSION_V31) ||
- csid_dev->hw_version == CSID_VERSION_V40) {
- msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
- csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0);
- msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
- msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
- } else if (csid_dev->hw_version == CSID_VERSION_V31) {
- msm_cam_clk_enable(&csid_dev->pdev->dev, csid_clk_info,
- csid_dev->csid_clk, ARRAY_SIZE(csid_clk_info), 0);
- msm_camera_enable_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
+ msm_camera_enable_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
- msm_camera_config_vreg(&csid_dev->pdev->dev,
- csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
- NULL, 0, &csid_dev->csi_vdd, 0);
- } else {
- pr_err("%s:%d, invalid hw version : 0x%x", __func__, __LINE__,
- csid_dev->hw_version);
- return -EINVAL;
- }
+ msm_camera_config_vreg(&csid_dev->pdev->dev,
+ csid_vreg_info, ARRAY_SIZE(csid_vreg_info),
+ NULL, 0, &csid_dev->csi_vdd, 0);
iounmap(csid_dev->base);
csid_dev->base = NULL;
@@ -448,7 +408,7 @@ static long msm_csid_subdev_ioctl(struct v4l2_subdev *sd,
rc = msm_csid_release(csid_dev);
break;
default:
- pr_err("%s: command not found\n", __func__);
+ pr_err_ratelimited("%s: command not found\n", __func__);
}
CDBG("%s:%d\n", __func__, __LINE__);
mutex_unlock(&csid_dev->mutex);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
index b5abc43d5b9a..aa2c534aaefc 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/csiphy/msm_csiphy.c
@@ -608,7 +608,7 @@ static long msm_csiphy_subdev_ioctl(struct v4l2_subdev *sd,
rc = msm_csiphy_release(csiphy_dev, arg);
break;
default:
- pr_err("%s: command not found\n", __func__);
+ pr_err_ratelimited("%s: command not found\n", __func__);
}
mutex_unlock(&csiphy_dev->mutex);
CDBG("%s:%d\n", __func__, __LINE__);
diff --git a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
index 2de17c95cba8..149d00cd8d36 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/flash/msm_led_flash.c
@@ -46,7 +46,7 @@ static long msm_led_flash_subdev_ioctl(struct v4l2_subdev *sd,
*(int *)argp = MSM_CAMERA_LED_RELEASE;
return fctrl->func_tbl->flash_led_config(fctrl, argp);
default:
- pr_err("invalid cmd %d\n", cmd);
+ pr_err_ratelimited("invalid cmd %d\n", cmd);
return -ENOIOCTLCMD;
}
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
index 5e2cc9e9a17b..ba65c202e26f 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/io/msm_camera_dt_util.c
@@ -378,6 +378,7 @@ int msm_camera_get_dt_power_setting_data(struct device_node *of_node,
return -ENOMEM;
}
power_setting = ps;
+ power_info->power_setting = ps;
for (i = 0; i < count; i++) {
rc = of_property_read_string_index(of_node,
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
index 38a47a8dd69b..5007e076daee 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor.c
@@ -17,7 +17,7 @@
#include "msm_camera_io_util.h"
#include "msm_camera_i2c_mux.h"
#include <mach/rpm-regulator.h>
-#include <mach/rpm-regulator-smd.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/regulator/consumer.h>
/*#define CONFIG_MSMB_CAMERA_DEBUG*/
@@ -426,6 +426,7 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
struct msm_camera_i2c_client *sensor_i2c_client;
struct msm_camera_slave_info *slave_info;
const char *sensor_name;
+ uint32_t retry = 0;
if (!s_ctrl) {
pr_err("%s:%d failed: %p\n",
@@ -446,14 +447,21 @@ int msm_sensor_power_up(struct msm_sensor_ctrl_t *s_ctrl)
return -EINVAL;
}
- rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type,
- sensor_i2c_client);
- if (rc < 0)
- return rc;
- rc = msm_sensor_check_id(s_ctrl);
- if (rc < 0)
- msm_camera_power_down(power_info, s_ctrl->sensor_device_type,
- sensor_i2c_client);
+ for (retry = 0; retry < 3; retry++) {
+ rc = msm_camera_power_up(power_info, s_ctrl->sensor_device_type,
+ sensor_i2c_client);
+ if (rc < 0)
+ return rc;
+ rc = msm_sensor_check_id(s_ctrl);
+ if (rc < 0) {
+ msm_camera_power_down(power_info,
+ s_ctrl->sensor_device_type, sensor_i2c_client);
+ msleep(20);
+ continue;
+ } else {
+ break;
+ }
+ }
return rc;
}
diff --git a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
index ae44f2d484df..c252e3d6bdd7 100644
--- a/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
+++ b/drivers/media/platform/msm/camera_v2/sensor/msm_sensor_init.c
@@ -123,7 +123,7 @@ static long msm_sensor_init_subdev_ioctl(struct v4l2_subdev *sd,
break;
default:
- pr_err("default");
+ pr_err_ratelimited("default\n");
break;
}
diff --git a/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c
index 6e123c70fa6a..819476fa6807 100644
--- a/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c
+++ b/drivers/media/platform/msm/dvb/adapter/mpq_stream_buffer.c
@@ -51,6 +51,7 @@ int mpq_streambuffer_init(
sbuff->buffers = data_buffers;
sbuff->pending_buffers_count = 0;
sbuff->buffers_num = data_buff_num;
+ sbuff->cb = NULL;
dvb_ringbuffer_init(&sbuff->packet_data, packet_buff, packet_buff_size);
return 0;
@@ -182,7 +183,7 @@ int mpq_streambuffer_pkt_dispose(
(dispose_data)) {
/* Advance the read pointer in the raw-data buffer first */
ret = mpq_streambuffer_data_read_dispose(sbuff,
- packet.raw_data_len);
+ packet.raw_data_len);
if (ret != 0)
return ret;
}
@@ -222,9 +223,6 @@ int mpq_streambuffer_pkt_dispose(
spin_unlock(&sbuff->raw_data.lock);
spin_unlock(&sbuff->packet_data.lock);
- if (sbuff->cb)
- sbuff->cb(sbuff, &packet, sbuff->cb_user_data);
-
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_dispose);
@@ -288,7 +286,7 @@ int mpq_streambuffer_pkt_write(
spin_unlock(&sbuff->packet_data.lock);
wake_up_all(&sbuff->packet_data.queue);
- return 0;
+ return idx;
}
EXPORT_SYMBOL(mpq_streambuffer_pkt_write);
@@ -414,6 +412,7 @@ ssize_t mpq_streambuffer_data_read(
u8 *buf, size_t len)
{
ssize_t actual_len = 0;
+ u32 offset;
if ((NULL == sbuff) || (NULL == buf))
return -EINVAL;
@@ -436,6 +435,7 @@ ssize_t mpq_streambuffer_data_read(
return -EPERM;
}
+ offset = sbuff->raw_data.pread;
actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
if (actual_len < len)
len = actual_len;
@@ -463,10 +463,15 @@ ssize_t mpq_streambuffer_data_read(
if (actual_len < len)
len = actual_len;
memcpy(buf, desc->base + desc->read_ptr, len);
+ offset = desc->read_ptr;
desc->read_ptr += len;
}
spin_unlock(&sbuff->raw_data.lock);
+
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
return len;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read);
@@ -477,6 +482,7 @@ ssize_t mpq_streambuffer_data_read_user(
u8 __user *buf, size_t len)
{
ssize_t actual_len = 0;
+ u32 offset;
if ((NULL == sbuff) || (NULL == buf))
return -EINVAL;
@@ -493,6 +499,7 @@ ssize_t mpq_streambuffer_data_read_user(
if (NULL == sbuff->raw_data.data)
return -EPERM;
+ offset = sbuff->raw_data.pread;
actual_len = dvb_ringbuffer_avail(&sbuff->raw_data);
if (actual_len < len)
len = actual_len;
@@ -519,9 +526,13 @@ ssize_t mpq_streambuffer_data_read_user(
if (copy_to_user(buf, desc->base + desc->read_ptr, len))
return -EFAULT;
+ offset = desc->read_ptr;
desc->read_ptr += len;
}
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
return len;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read_user);
@@ -530,6 +541,8 @@ int mpq_streambuffer_data_read_dispose(
struct mpq_streambuffer *sbuff,
size_t len)
{
+ u32 offset;
+
if (NULL == sbuff)
return -EINVAL;
@@ -547,6 +560,7 @@ int mpq_streambuffer_data_read_dispose(
return -EINVAL;
}
+ offset = sbuff->raw_data.pread;
DVB_RINGBUFFER_SKIP(&sbuff->raw_data, len);
wake_up_all(&sbuff->raw_data.queue);
} else {
@@ -554,6 +568,8 @@ int mpq_streambuffer_data_read_dispose(
desc = (struct mpq_streambuffer_buffer_desc *)
&sbuff->raw_data.data[sbuff->raw_data.pread];
+ offset = desc->read_ptr;
+
if ((desc->read_ptr + len) > desc->size)
desc->read_ptr = desc->size;
else
@@ -562,6 +578,9 @@ int mpq_streambuffer_data_read_dispose(
spin_unlock(&sbuff->raw_data.lock);
+ if (sbuff->cb)
+ sbuff->cb(sbuff, offset, len, sbuff->cb_user_data);
+
return 0;
}
EXPORT_SYMBOL(mpq_streambuffer_data_read_dispose);
@@ -604,9 +623,9 @@ int mpq_streambuffer_get_buffer_handle(
EXPORT_SYMBOL(mpq_streambuffer_get_buffer_handle);
-int mpq_streambuffer_register_pkt_dispose(
+int mpq_streambuffer_register_data_dispose(
struct mpq_streambuffer *sbuff,
- mpq_streambuffer_pkt_dispose_cb cb_func,
+ mpq_streambuffer_dispose_cb cb_func,
void *user_data)
{
if ((NULL == sbuff) || (NULL == cb_func))
@@ -617,7 +636,7 @@ int mpq_streambuffer_register_pkt_dispose(
return 0;
}
-EXPORT_SYMBOL(mpq_streambuffer_register_pkt_dispose);
+EXPORT_SYMBOL(mpq_streambuffer_register_data_dispose);
ssize_t mpq_streambuffer_data_free(
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
index 1d7a1509a48e..8675ac15c464 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.c
@@ -807,7 +807,7 @@ void mpq_dmx_plugin_exit(void)
&mpq_demux->demux.dmx,
&mpq_demux->fe_memory);
- if (mpq_sdmx_is_loaded())
+ if (mpq_dmx_info.secure_demux_app_loaded)
mpq_sdmx_close_session(mpq_demux);
mutex_destroy(&mpq_demux->mutex);
dvb_dmxdev_release(&mpq_demux->dmxdev);
@@ -1477,7 +1477,6 @@ int mpq_dmx_init_video_feed(struct mpq_feed *mpq_feed)
feed_data->continuity_errs = 0;
feed_data->ts_packets_num = 0;
feed_data->ts_dropped_bytes = 0;
- feed_data->last_pkt_index = -1;
mpq_demux->decoder_stat[feed_data->stream_interface].drop_count = 0;
mpq_demux->decoder_stat[feed_data->stream_interface].out_count = 0;
@@ -2199,9 +2198,9 @@ static inline void mpq_dmx_prepare_es_event_data(
struct mpq_adapter_video_meta_data *meta_data,
struct mpq_video_feed_info *feed_data,
struct mpq_streambuffer *stream_buffer,
- struct dmx_data_ready *data)
+ struct dmx_data_ready *data,
+ int cookie)
{
- size_t len = 0;
struct dmx_pts_dts_info *pts_dts;
if (meta_data->packet_type == DMX_PES_PACKET) {
@@ -2218,15 +2217,7 @@ static inline void mpq_dmx_prepare_es_event_data(
data->data_length = 0;
data->buf.handle = packet->raw_data_handle;
-
- /* this has to succeed when called here, after packet was written */
- data->buf.cookie = mpq_streambuffer_pkt_next(stream_buffer,
- feed_data->last_pkt_index, &len);
- if (data->buf.cookie < 0)
- MPQ_DVB_DBG_PRINT(
- "%s: received invalid packet index %d\n",
- __func__, data->buf.cookie);
-
+ data->buf.cookie = cookie;
data->buf.offset = packet->raw_data_offset;
data->buf.len = packet->raw_data_len;
data->buf.pts_exists = pts_dts->pts_exist;
@@ -2239,9 +2230,6 @@ static inline void mpq_dmx_prepare_es_event_data(
data->buf.ts_dropped_bytes = feed_data->ts_dropped_bytes;
data->status = DMX_OK_DECODER_BUF;
- /* save for next time: */
- feed_data->last_pkt_index = data->buf.cookie;
-
MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data->buf.cookie);
/* reset counters */
@@ -2301,6 +2289,7 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux,
struct mpq_video_feed_info *feed_data;
struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
struct dmx_data_ready data;
+ int cookie;
feed_data = &mpq_feed->video_info;
@@ -2349,14 +2338,17 @@ static void mpq_dmx_decoder_frame_closure(struct mpq_demux *mpq_demux,
mpq_dmx_update_decoder_stat(mpq_feed);
/* Writing meta-data that includes the framing information */
- if (mpq_streambuffer_pkt_write(stream_buffer, &packet,
- (u8 *)&meta_data) < 0)
- MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n",
- __func__);
-
- mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data,
- stream_buffer, &data);
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie >= 0) {
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data,
+ feed_data, stream_buffer, &data, cookie);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ }
}
spin_unlock(&feed_data->video_buffer_lock);
@@ -2378,6 +2370,7 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux,
struct mpq_video_feed_info *feed_data;
struct dvb_demux_feed *feed = mpq_feed->dvb_demux_feed;
struct dmx_data_ready data;
+ int cookie;
feed_data = &mpq_feed->video_info;
@@ -2416,18 +2409,20 @@ static void mpq_dmx_decoder_pes_closure(struct mpq_demux *mpq_demux,
mpq_dmx_update_decoder_stat(mpq_feed);
- if (mpq_streambuffer_pkt_write(stream_buffer, &packet,
- (u8 *)&meta_data) < 0)
- MPQ_DVB_ERR_PRINT("%s: Couldn't write packet\n",
- __func__);
-
- /* Save write offset where new PES will begin */
- mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL,
- &feed_data->frame_offset);
-
- mpq_dmx_prepare_es_event_data(&packet, &meta_data, feed_data,
- stream_buffer, &data);
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ cookie = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie >= 0) {
+ /* Save write offset where new PES will begin */
+ mpq_streambuffer_get_data_rw_offset(stream_buffer, NULL,
+ &feed_data->frame_offset);
+ mpq_dmx_prepare_es_event_data(&packet, &meta_data,
+ feed_data, stream_buffer, &data, cookie);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ }
}
/* Reset PES info */
feed->peslen = 0;
@@ -2809,26 +2804,24 @@ static int mpq_dmx_process_video_packet_framing(
* writing meta-data that includes
* the framing information
*/
- if (mpq_streambuffer_pkt_write(stream_buffer,
- &packet,
- (u8 *)&meta_data) < 0) {
+ ret = mpq_streambuffer_pkt_write(stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (ret < 0) {
MPQ_DVB_ERR_PRINT(
- "%s: "
- "Couldn't write packet. "
- "Should never happen\n",
- __func__);
- }
-
- mpq_dmx_prepare_es_event_data(
- &packet, &meta_data, feed_data,
- stream_buffer, &data);
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, ret);
+ } else {
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data, feed_data,
+ stream_buffer, &data, ret);
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
- mpq_streambuffer_get_data_rw_offset(
- feed_data->video_buffer,
- NULL,
- &feed_data->frame_offset);
+ mpq_streambuffer_get_data_rw_offset(
+ feed_data->video_buffer,
+ NULL,
+ &feed_data->frame_offset);
+ }
/*
* In linear buffers, after writing the packet
@@ -2936,6 +2929,7 @@ static int mpq_dmx_process_video_packet_no_framing(
struct mpq_feed *mpq_feed;
int discontinuity_indicator = 0;
struct dmx_data_ready data;
+ int cookie;
mpq_demux = feed->demux->priv;
mpq_feed = feed->priv;
@@ -3006,34 +3000,34 @@ static int mpq_dmx_process_video_packet_no_framing(
mpq_dmx_update_decoder_stat(mpq_feed);
- if (mpq_streambuffer_pkt_write(
- stream_buffer,
- &packet,
- (u8 *)&meta_data) < 0)
+ cookie = mpq_streambuffer_pkt_write(
+ stream_buffer, &packet,
+ (u8 *)&meta_data);
+ if (cookie < 0) {
MPQ_DVB_ERR_PRINT(
- "%s: "
- "Couldn't write packet. "
- "Should never happen\n",
- __func__);
-
- /* Save write offset where new PES will begin */
- mpq_streambuffer_get_data_rw_offset(
- stream_buffer,
- NULL,
- &feed_data->frame_offset);
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, cookie);
+ } else {
+ /*
+ * Save write offset where new PES
+ * will begin
+ */
+ mpq_streambuffer_get_data_rw_offset(
+ stream_buffer,
+ NULL,
+ &feed_data->frame_offset);
- mpq_dmx_prepare_es_event_data(
- &packet, &meta_data,
- feed_data,
- stream_buffer, &data);
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data,
+ feed_data,
+ stream_buffer, &data, cookie);
- feed->data_ready_cb.ts(
- &feed->feed.ts, &data);
+ feed->data_ready_cb.ts(&feed->feed.ts,
+ &data);
+ }
} else {
MPQ_DVB_ERR_PRINT(
- "%s: received PUSI"
- "while handling PES header"
- "of previous PES\n",
+ "%s: received PUSI while handling PES header of previous PES\n",
__func__);
}
@@ -3318,7 +3312,7 @@ int mpq_dmx_decoder_eos_cmd(struct mpq_feed *mpq_feed)
(u8 *)&oob_meta_data);
spin_unlock(&feed_data->video_buffer_lock);
- return ret;
+ return (ret < 0) ? ret : 0;
}
void mpq_dmx_convert_tts(struct dvb_demux_feed *feed,
@@ -4577,18 +4571,19 @@ static void mpq_sdmx_decoder_filter_results(struct mpq_demux *mpq_demux,
__func__, ret);
}
mpq_dmx_update_decoder_stat(mpq_feed);
- if (mpq_streambuffer_pkt_write(sbuf,
- &packet,
- (u8 *)&meta_data) < 0)
+ ret = mpq_streambuffer_pkt_write(sbuf, &packet,
+ (u8 *)&meta_data);
+ if (ret < 0) {
MPQ_DVB_ERR_PRINT(
- "%s: Couldn't write packet. Should never happen\n",
- __func__);
-
- mpq_dmx_prepare_es_event_data(
- &packet, &meta_data, &mpq_feed->video_info,
- sbuf, &data);
- MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__);
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ "%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
+ __func__, ret);
+ } else {
+ mpq_dmx_prepare_es_event_data(
+ &packet, &meta_data, &mpq_feed->video_info,
+ sbuf, &data, ret);
+ MPQ_DVB_DBG_PRINT("%s: Notify ES Event\n", __func__);
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ }
spin_unlock(&mpq_feed->video_info.video_buffer_lock);
}
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
index d5ba93ad511f..b8e3685e4bd8 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_common.h
@@ -297,8 +297,6 @@ struct mpq_decoder_buffers_desc {
* @ts_packets_num: TS packets counter.
* @ts_dropped_bytes: counts the number of bytes dropped due to insufficient
* buffer space.
- * @last_pkt_index: used to save the last streambuffer packet index reported in
- * a new elementary stream data event.
* @prev_stc: STC attached to the previous video TS packet
*/
struct mpq_video_feed_info {
@@ -332,7 +330,6 @@ struct mpq_video_feed_info {
u32 continuity_errs;
u32 ts_packets_num;
u32 ts_dropped_bytes;
- int last_pkt_index;
u64 prev_stc;
};
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
index ec37b7d97120..17701179b717 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.c
@@ -77,7 +77,6 @@ static struct
struct dentry *debugfs_filters_file;
struct dentry *debugfs_sources_file;
struct dentry *debugfs_index_tables_file;
-
} mpq_dmx_tspp2_info;
/**
@@ -92,7 +91,6 @@ static void pipe_work_queue_init(struct pipe_work_queue *queue)
spin_lock_init(&queue->lock);
INIT_LIST_HEAD(&queue->work_list);
INIT_LIST_HEAD(&queue->free_list);
- init_waitqueue_head(&queue->wait_queue);
for (i = 0; i < TSPP2_DMX_PIPE_WORK_POOL_SIZE; i++)
list_add_tail(&queue->work_pool[i].next, &queue->free_list);
@@ -144,7 +142,6 @@ static void pipe_work_queue_push(struct pipe_work_queue *queue,
spin_lock_irqsave(&queue->lock, flags);
list_add_tail(&work->next, &queue->work_list);
- wake_up_all(&queue->wait_queue);
spin_unlock_irqrestore(&queue->lock, flags);
}
@@ -704,13 +701,13 @@ static int mpq_dmx_tspp2_ts_event_check(struct dvb_demux_feed *feed,
ret = feed->demux->buffer_ctrl.ts(&feed->feed.ts, 0, 1);
mutex_lock(&pipe_info->mutex);
if (ret) {
- MPQ_DVB_ERR_PRINT(
+ MPQ_DVB_DBG_PRINT(
"%s: buffer_ctrl.ts callback failed, ret=%d!\n",
__func__, ret);
return ret;
}
if (pipe_info->session_id != session_id || !pipe_info->ref_count) {
- MPQ_DVB_ERR_PRINT(
+ MPQ_DVB_DBG_PRINT(
"%s: pipe was closed / reopened: ref. count=%u, session_id=%u (expected %u)\n",
__func__, pipe_info->ref_count,
pipe_info->session_id, session_id);
@@ -720,6 +717,48 @@ static int mpq_dmx_tspp2_ts_event_check(struct dvb_demux_feed *feed,
return 0;
}
+static int mpq_dmx_tspp2_stream_buffer_event_check(struct dvb_demux_feed *feed,
+ struct pipe_info *pipe_info)
+{
+ int ret;
+ u32 session_id;
+
+ if (feed->demux->playback_mode != DMX_PB_MODE_PULL)
+ return 0;
+
+ if (!mutex_is_locked(&pipe_info->mutex))
+ return -EINVAL;
+
+ /*
+ * For pull mode need to wait for sufficient room to write the
+ * meta-data packet in the mpq_streambuffer object.
+ * Data itself was already written by TSPPv2 hardware (so required_space
+ * argument is 0).
+ * Since this may block waiting for the metadata buffer, pipe mutex
+ * needs to be released, and when returning verify the pipe was not
+ * closed / re-opened in the meantime.
+ */
+ session_id = pipe_info->session_id;
+ mutex_unlock(&pipe_info->mutex);
+ ret = mpq_dmx_decoder_fullness_wait(feed, 0);
+ mutex_lock(&pipe_info->mutex);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_decoder_fullness_wait failed, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (pipe_info->session_id != session_id || !pipe_info->ref_count) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: pipe was closed / re-opened: ref. count=%u, session_id=%u (expected %u)\n",
+ __func__, pipe_info->ref_count,
+ pipe_info->session_id, session_id);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
/**
* mpq_dmx_tspp2_filter_event_cb() - filter event notification handler
*
@@ -1259,8 +1298,8 @@ static int mpq_dmx_tspp2_terminate_filter(struct dvb_demux_feed *feed,
mpq_tspp2_feed->op_count = 0;
/* Remove PES analysis operation */
- if ((feed->ts_type & (TS_PAYLOAD_ONLY | TS_DECODER)) ||
- feed->idx_params.enable)
+ if ((feed->ts_type & (TS_PAYLOAD_ONLY | TS_DECODER) &&
+ !dvb_dmx_is_pcr_feed(feed)) || feed->idx_params.enable)
mpq_dmx_tspp2_del_pes_analysis_op(mpq_tspp2_feed->filter);
/* Remove indexing operation */
@@ -1667,6 +1706,8 @@ static int mpq_dmx_init_out_pipe(struct mpq_demux *mpq_demux,
pipe_info->source_info = source_info;
pipe_info->eos_pending = 0;
pipe_info->session_id++;
+ pipe_info->hw_missed_notif = 0;
+ pipe_info->handler_count = 0;
return 0;
close_pipe:
@@ -1732,8 +1773,7 @@ static int mpq_dmx_terminate_out_pipe(struct pipe_info *pipe_info)
tspp2_pipe_close(pipe_info->handle);
pipe_info->handle = TSPP2_INVALID_HANDLE;
- pipe_work_queue_cancel_work(&source_info->demux_src.work_queue,
- pipe_info);
+ pipe_work_queue_cancel_work(&pipe_info->work_queue, pipe_info);
if (pipe_info->buffer.kernel_map)
ion_unmap_kernel(mpq_demux->ion_client,
@@ -2084,25 +2124,51 @@ static void mpq_dmx_tspp2_update_pipe_stats(struct pipe_info *pipe_info)
pipe_info->hw_notif_count++;
}
-static void mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info,
+static int mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info,
enum mpq_dmx_tspp2_pipe_event event)
{
- struct source_info *src;
+ struct source_info *src = pipe_info->source_info;
+ struct pipe_work_queue *queue = &pipe_info->work_queue;
struct pipe_work *pipe_work;
+ unsigned long flags;
+
+ /* Try to merge data events into 1 pipe work object */
+ spin_lock_irqsave(&queue->lock, flags);
+ if (!list_empty(&queue->work_list)) {
+ pipe_work = list_first_entry(&queue->work_list,
+ struct pipe_work, next);
+ if (event == PIPE_DATA_EVENT &&
+ pipe_work->event == PIPE_DATA_EVENT &&
+ pipe_work->event_count &&
+ pipe_work->pipe_info == pipe_info &&
+ pipe_work->session_id == pipe_info->session_id) {
+ pipe_work->event_count++;
+ spin_unlock_irqrestore(&queue->lock, flags);
+ wake_up_all(&src->demux_src.wait_queue);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&queue->lock, flags);
- src = pipe_info->source_info;
- pipe_work = pipe_work_queue_allocate(&src->demux_src.work_queue);
+ pipe_work = pipe_work_queue_allocate(&pipe_info->work_queue);
if (pipe_work == NULL) {
+ int pipe_idx = (pipe_info - mpq_dmx_tspp2_info.pipes) /
+ sizeof(*pipe_info);
MPQ_DVB_ERR_PRINT(
- "%s: Cannot allocate pipe work for pipe %d\n",
- __func__, pipe_info->type);
- return;
+ "%s: Cannot allocate pipe work for pipe %d, type %d\n",
+ __func__, pipe_idx, pipe_info->type);
+ return -ENOSPC;
}
pipe_work->pipe_info = pipe_info;
pipe_work->event = event;
pipe_work->session_id = pipe_info->session_id;
- pipe_work_queue_push(&src->demux_src.work_queue, pipe_work);
+ pipe_work->event_count = 1;
+
+ pipe_work_queue_push(&pipe_info->work_queue, pipe_work);
+ wake_up_all(&src->demux_src.wait_queue);
+
+ return 0;
}
/**
@@ -2113,9 +2179,7 @@ static void mpq_dmx_tspp2_queue_pipe_handler(struct pipe_info *pipe_info,
static void mpq_dmx_sps_producer_cb(struct sps_event_notify *notify)
{
struct pipe_info *pipe_info = notify->user;
-
- MPQ_DVB_DBG_PRINT("%s: Notification event id=%d, handle=%p, type=%d\n",
- __func__, notify->event_id, pipe_info, pipe_info->type);
+ int ret;
if (unlikely(pipe_info == NULL))
return;
@@ -2123,12 +2187,17 @@ static void mpq_dmx_sps_producer_cb(struct sps_event_notify *notify)
mpq_dmx_tspp2_update_pipe_stats(pipe_info);
/* Schedule a new work to relevant source workqueue */
- if (notify->event_id == SPS_EVENT_OUT_OF_DESC)
- mpq_dmx_tspp2_queue_pipe_handler(pipe_info,
+ if (notify->event_id == SPS_EVENT_OUT_OF_DESC) {
+ MPQ_DVB_ERR_PRINT("%s: SPS_EVENT_OUT_OF_DESC!\n", __func__);
+ ret = mpq_dmx_tspp2_queue_pipe_handler(pipe_info,
PIPE_OVERFLOW_EVENT);
- else
- mpq_dmx_tspp2_queue_pipe_handler(pipe_info,
+ } else {
+ ret = mpq_dmx_tspp2_queue_pipe_handler(pipe_info,
PIPE_DATA_EVENT);
+ }
+
+ if (ret)
+ pipe_info->hw_missed_notif++;
}
/**
@@ -2173,10 +2242,10 @@ static void mpq_dmx_timer_cb(unsigned long param)
pipe_info = &mpq_dmx_tspp2_info.pipes[i];
spin_lock(&pipe_info->lock);
- if ((!pipe_info->ref_count) ||
- ((pipe_info->type != CLEAR_SECTION_PIPE) &&
- (pipe_info->type != SCRAMBLED_SECTION_PIPE) &&
- (pipe_info->type != REC_PIPE))) {
+ if (!pipe_info->ref_count ||
+ (pipe_info->type != CLEAR_SECTION_PIPE &&
+ pipe_info->type != SCRAMBLED_SECTION_PIPE &&
+ pipe_info->type != REC_PIPE)) {
spin_unlock(&pipe_info->lock);
continue;
}
@@ -2190,13 +2259,14 @@ static void mpq_dmx_timer_cb(unsigned long param)
/* Schedule a new work to relevant source workqueue */
pipe_work = pipe_work_queue_allocate(
- &source_info->demux_src.work_queue);
+ &pipe_info->work_queue);
if (pipe_work != NULL) {
pipe_work->session_id = pipe_info->session_id;
pipe_work->pipe_info = pipe_info;
pipe_work->event = PIPE_DATA_EVENT;
- pipe_work_queue_push(&source_info->demux_src.work_queue,
- pipe_work);
+ pipe_work->event_count = 1;
+ pipe_work_queue_push(&pipe_info->work_queue, pipe_work);
+ wake_up_all(&source_info->demux_src.wait_queue);
} else {
MPQ_DVB_ERR_PRINT(
"%s: Cannot allocate pipe work\n", __func__);
@@ -2569,7 +2639,6 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info,
u16 curr_pid;
ssize_t data_size;
size_t packet_offset;
- u32 tspp_write_offset;
u32 tspp_last_addr = 0;
struct dvb_demux *dvb_demux =
&pipe_info->source_info->demux_src.mpq_demux->demux;
@@ -2585,6 +2654,13 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info,
tspp2_pipe_last_address_used_get(pipe_info->handle, &tspp_last_addr);
data_size = mpq_dmx_tspp2_calc_pipe_data(pipe_info, tspp_last_addr);
+ if (data_size) {
+ pipe_info->tspp_write_offset += data_size;
+ if (pipe_info->tspp_write_offset >= pipe_info->buffer.size)
+ pipe_info->tspp_write_offset -= pipe_info->buffer.size;
+ }
+ data_size = mpq_dmx_calc_fullness(pipe_info->tspp_write_offset,
+ pipe_info->tspp_read_offset, pipe_info->buffer.size);
if (data_size == 0)
return 0;
@@ -2598,8 +2674,8 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info,
* will stall because there are no free descriptors.
*/
spin_lock(&dvb_demux->lock);
- for (i = 0; i < num_packets && !should_stall; i++) {
- packet_offset = pipe_info->tspp_write_offset +
+ for (i = 0; i < num_packets && !should_stall;) {
+ packet_offset = pipe_info->tspp_read_offset +
i * TSPP2_DMX_SPS_SECTION_DESC_SIZE;
if (packet_offset >= pipe_info->buffer.size)
packet_offset -= pipe_info->buffer.size;
@@ -2619,17 +2695,16 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info,
break;
}
}
+ if (!should_stall)
+ i++;
}
spin_unlock(&dvb_demux->lock);
pipe_info->tspp_last_addr = tspp_last_addr;
- tspp_write_offset = pipe_info->tspp_write_offset;
- pipe_info->tspp_write_offset += i * TSPP2_DMX_SPS_SECTION_DESC_SIZE;
/* Release the data for the packets that were processed */
- data_size = mpq_dmx_calc_fullness(tspp_write_offset,
- pipe_info->tspp_read_offset, pipe_info->buffer.size);
- ret = mpq_dmx_release_data(pipe_info, data_size);
+ ret = mpq_dmx_release_data(pipe_info,
+ i * TSPP2_DMX_SPS_SECTION_DESC_SIZE);
if (event == PIPE_EOS_EVENT && should_stall)
pipe_info->eos_pending = 1;
@@ -2640,6 +2715,94 @@ static int mpq_dmx_tspp2_section_pipe_handler(struct pipe_info *pipe_info,
return ret;
}
+static int mpq_dmx_tspp2_process_full_pes_desc(struct pipe_info *pipe_info,
+ struct dvb_demux_feed *feed, struct sps_iovec *iovec)
+{
+ u8 pes_status;
+ u8 *data_buffer = NULL;
+ struct dmx_data_ready data;
+ int ret;
+
+ data_buffer = mpq_dmx_get_kernel_addr(pipe_info, iovec->addr);
+ if (unlikely(!data_buffer)) {
+ /* Should NEVER happen! */
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_get_kernel_addr failed\n",
+ __func__);
+ return -EFAULT;
+ }
+
+ /*
+ * Extract STC value for this PES:
+ * Descriptor data starts with 8 bytes of STC:
+ * 7 bytes STC@27MHz and 1 zero byte for padding.
+ */
+ if (feed->peslen < PES_STC_FIELD_LENGTH) {
+ if (iovec->size < PES_STC_FIELD_LENGTH) {
+ /* Descriptor too small to hold STC */
+ MPQ_DVB_ERR_PRINT(
+ "%s: descriptor size %d is too small (peslen=%d)\n",
+ __func__, iovec->size, feed->peslen);
+ return -EINVAL;
+ }
+
+ feed->prev_stc = mpq_dmx_tspp2_get_stc(&data_buffer[0], 7);
+ }
+
+ /*
+ * Report the whole descriptor allocated size to dmxdev so that
+ * DMX_OK_PES_END event total size will be correct.
+ */
+ data.status = DMX_OK;
+ data.data_length = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ if (iovec->flags & SPS_IOVEC_FLAG_EOT) {
+ /* PES assembly status is the last byte in the descriptor */
+ pes_status =
+ data_buffer[iovec->size - PES_ASM_STATUS_FIELD_LENGTH];
+
+ /*
+ * EOT descriptor might not have been used completely.
+ * The next PES will begin on the next descriptor and
+ * not immediately following this PES. Need to account
+ * for this gap when PES is reported.
+ */
+ feed->peslen += iovec->size;
+
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = PES_STC_FIELD_LENGTH;
+ data.pes_end.actual_length =
+ feed->peslen - PES_STC_FIELD_LENGTH -
+ PES_ASM_STATUS_FIELD_LENGTH;
+ data.pes_end.stc = feed->prev_stc;
+ data.pes_end.disc_indicator_set =
+ pes_status & PES_ASM_STATUS_DCI;
+ data.pes_end.pes_length_mismatch =
+ pes_status & PES_ASM_STATUS_SIZE_MISMATCH;
+ /* TSPPv2 does not report the following */
+ data.pes_end.tei_counter = 0;
+ data.pes_end.cont_err_counter = 0;
+ data.pes_end.ts_packets_num = 0;
+
+ ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info);
+ if (ret)
+ return ret;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ /* Reset accumulated PES length for next iteration */
+ feed->peslen = 0;
+
+ return 0;
+ }
+
+ /* DESC_DONE case */
+ feed->peslen += iovec->size;
+
+ return 0;
+}
+
/**
* mpq_dmx_tspp2_pes_pipe_handler() - Handler for non-video full PES
* pipe notifications.
@@ -2653,17 +2816,13 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info,
enum mpq_dmx_tspp2_pipe_event event)
{
int ret;
- u64 stc = 0;
- u32 pes_length;
u32 tspp_write_offset = 0;
u32 tspp_last_addr;
size_t pes_leftover = 0;
struct sps_iovec iovec;
u8 *data_buffer = NULL;
- u8 pes_status;
struct dmx_data_ready data;
struct dvb_demux_feed *feed;
- int found_pes;
feed = pipe_info->parent->mpq_feed->dvb_demux_feed;
if (unlikely(!feed)) {
@@ -2686,13 +2845,11 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info,
}
/*
- * Read all filled up descriptors (DESC_DONE) until EOT.
+ * Read pending descriptors (DESC_DONE and EOT).
* In case PES is very short and fits in 1 descriptor, only EOT will
* be received.
*/
- found_pes = 0;
- pes_length = 0;
- while (!found_pes) {
+ while (1) {
ret = tspp2_pipe_descriptor_get(pipe_info->handle, &iovec);
if (ret) {
/* should NEVER happen! */
@@ -2702,132 +2859,35 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info,
return -EINVAL;
}
+ /* No more descriptors */
if (!iovec.size)
break;
- if (iovec.flags & SPS_IOVEC_FLAG_EOT) {
- if (!data_buffer) {
- /*
- * PES start address in payload buffer. This is
- * also where STC is located.
- */
- data_buffer = mpq_dmx_get_kernel_addr(
- pipe_info, iovec.addr);
- }
-
- if (unlikely(!data_buffer)) {
- /* Should NEVER happen! */
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_dmx_get_kernel_addr failed\n",
- __func__);
- continue;
- }
-
- /*
- * At this point data_buffer always points to beginning
- * of payload data, which starts with 8 bytes of STC:
- * 7 bytes STC@27MHz and 1 zero byte for padding.
- */
- stc = mpq_dmx_tspp2_get_stc(&data_buffer[0], 7);
-
- /*
- * Now data_buffer will point to the beginning of the
- * last descriptor. The last byte there is the PES
- * assembly status flags.
- */
- data_buffer = mpq_dmx_get_kernel_addr(pipe_info,
- iovec.addr);
- if (unlikely(!data_buffer)) {
- /* should NEVER happen! */
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_dmx_get_kernel_addr failed\n",
- __func__);
- continue;
- }
-
- pes_status = data_buffer[iovec.size
- - PES_ASM_STATUS_FIELD_LENGTH];
-
- /*
- * EOT descriptor might not have been used completely.
- * The next PES will begin on the next descriptor and
- * not immediately following this PES. Need to account
- * for this gap when PES is reported.
- */
- pes_leftover = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE -
- iovec.size;
- pes_length += iovec.size;
- pipe_info->tspp_write_offset +=
- TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE;
- if (pipe_info->tspp_write_offset >=
- pipe_info->buffer.size)
- pipe_info->tspp_write_offset -=
- pipe_info->buffer.size;
-
- /*
- * Must first report the last chunk of data for
- * DMX_OK_PES_END event total size to be correct.
- */
- data.status = DMX_OK;
- data.data_length = TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
-
- data.status = DMX_OK_PES_END;
- data.data_length = 0;
- data.pes_end.start_gap = PES_STC_FIELD_LENGTH;
- data.pes_end.actual_length =
- pes_length - PES_STC_FIELD_LENGTH -
- PES_ASM_STATUS_FIELD_LENGTH;
- data.pes_end.stc = stc;
- data.pes_end.disc_indicator_set =
- pes_status & PES_ASM_STATUS_DCI;
- data.pes_end.pes_length_mismatch =
- pes_status & PES_ASM_STATUS_SIZE_MISMATCH;
- /* TSPPv2 does not report the following */
- data.pes_end.tei_counter = 0;
- data.pes_end.cont_err_counter = 0;
- data.pes_end.ts_packets_num = 0;
-
- ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info);
- if (ret)
- return ret;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
- found_pes = 1;
- } else {
- if (!data_buffer) {
- /*
- * PES start address in payload buffer. This is
- * also where STC is located.
- */
- data_buffer = mpq_dmx_get_kernel_addr(
- pipe_info, iovec.addr);
- }
+ pipe_info->tspp_write_offset +=
+ TSPP2_DMX_SPS_NON_VID_PES_DESC_SIZE;
+ if (pipe_info->tspp_write_offset >= pipe_info->buffer.size)
+ pipe_info->tspp_write_offset -= pipe_info->buffer.size;
- pes_length += iovec.size;
- pipe_info->tspp_write_offset += iovec.size;
- if (pipe_info->tspp_write_offset >=
- pipe_info->buffer.size)
- pipe_info->tspp_write_offset -=
- pipe_info->buffer.size;
-
- data.status = DMX_OK;
- data.data_length = iovec.size;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
+ ret = mpq_dmx_tspp2_process_full_pes_desc(pipe_info, feed,
+ &iovec);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_tspp2_process_full_pes_desc failed, ret=%d\n",
+ __func__, ret);
+ return ret;
}
}
if (event == PIPE_EOS_EVENT) {
/*
* There are 3 cases for End-of-Stream:
- * 1. No new data so no need to notify on PES end
- * (pes_leftover is 0)
- * 2. New data is in a single partial descriptor so no
- * descriptor done events were received and stc is in this
- * partial descriptor
- * (pes_length = 0, pes_leftover < 256)
- * 3. New data spreads across several descriptors and a
- * partial descriptor.
- * (pes_length > 0, pes_leftover < 256)
+ * 1. No new data so just need to notify on end of partial PES
+ * consisting of previous DESC_DONE descriptors.
+ * (feed->peslen > 0, pes_leftover = 0)
+ * 2. New PES begins in this partial descriptor with stc
+ * (feed->peslen = 0, pes_leftover < 256)
+ * 3. New PES began is a previous descriptors, stc is valid
+ * (feed->peslen > 0, pes_leftover < 256)
*/
tspp2_pipe_last_address_used_get(pipe_info->handle,
&tspp_last_addr);
@@ -2840,41 +2900,60 @@ static int mpq_dmx_tspp2_pes_pipe_handler(struct pipe_info *pipe_info,
pipe_info->buffer.size);
}
+
+ if (feed->peslen < PES_STC_FIELD_LENGTH &&
+ pes_leftover < PES_STC_FIELD_LENGTH) {
+ /* Insufficient data to report, just report EOS event */
+ MPQ_DVB_DBG_PRINT(
+ "%s: PES leftover too small = %d bytes\n",
+ __func__, pes_leftover);
+
+ data.status = DMX_OK_EOS;
+ data.data_length = 0;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ return 0;
+ }
+
if (pes_leftover) {
MPQ_DVB_DBG_PRINT("%s: PES leftover %d bytes\n",
__func__, pes_leftover);
-
- if (!data_buffer)
- data_buffer = pipe_info->buffer.mem +
- tspp_write_offset;
+ data_buffer = pipe_info->buffer.mem + tspp_write_offset;
/* Notify there is more data */
data.status = DMX_OK;
data.data_length = pes_leftover;
feed->data_ready_cb.ts(&feed->feed.ts, &data);
-
- /* Notify PES has ended */
- data.status = DMX_OK_PES_END;
- data.data_length = 0;
- data.pes_end.start_gap = PES_STC_FIELD_LENGTH;
- /* In EOS case there is no PES assembly status byte */
- data.pes_end.actual_length = pes_length + pes_leftover
- - PES_STC_FIELD_LENGTH;
- data.pes_end.stc =
- mpq_dmx_tspp2_get_stc(data_buffer, 7);
- data.pes_end.disc_indicator_set = 0;
- data.pes_end.pes_length_mismatch = 0;
- data.pes_end.tei_counter = 0;
- data.pes_end.cont_err_counter = 0;
- data.pes_end.ts_packets_num = 0;
- pipe_info->tspp_write_offset = tspp_write_offset;
-
- ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info);
- if (ret)
- return ret;
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
}
+ if (feed->peslen < PES_STC_FIELD_LENGTH)
+ feed->prev_stc =
+ mpq_dmx_tspp2_get_stc(&data_buffer[0], 7);
+
+ feed->peslen += pes_leftover;
+
+ /* Notify PES has ended */
+ data.status = DMX_OK_PES_END;
+ data.data_length = 0;
+ data.pes_end.start_gap = PES_STC_FIELD_LENGTH;
+ /* In EOS case there is no PES assembly status byte */
+ data.pes_end.actual_length =
+ feed->peslen - PES_STC_FIELD_LENGTH;
+ data.pes_end.stc = feed->prev_stc;
+ data.pes_end.disc_indicator_set = 0;
+ data.pes_end.pes_length_mismatch = 0;
+ data.pes_end.tei_counter = 0;
+ data.pes_end.cont_err_counter = 0;
+ data.pes_end.ts_packets_num = 0;
+ pipe_info->tspp_write_offset = tspp_write_offset;
+
+ ret = mpq_dmx_tspp2_ts_event_check(feed, pipe_info);
+ if (ret)
+ return ret;
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
+
+ feed->peslen = 0;
+
data.status = DMX_OK_EOS;
data.data_length = 0;
feed->data_ready_cb.ts(&feed->feed.ts, &data);
@@ -2955,6 +3034,16 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed,
mpq_dmx_parse_video_header_suffix(buffer, partial_header,
&stc, &pes_payload_sa, &pes_payload_ea,
&status_flags);
+ if (status_flags)
+ MPQ_DVB_DBG_PRINT(
+ "%s: status_flags=0x%x, sa=%u, ea=%u\n", __func__,
+ status_flags, pes_payload_sa, pes_payload_ea);
+
+ if (pes_payload_sa == ULONG_MAX || pes_payload_sa == ULONG_MAX) {
+ MPQ_DVB_DBG_PRINT("%s: Data was not written to payload pipe\n",
+ __func__);
+ return 0;
+ }
ts_header = (struct ts_packet_header *)buffer;
@@ -3012,8 +3101,7 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed,
* Verify we have received at least the mandatory
* fields within the PES header.
*/
- if (unlikely(feed_data->pes_header_offset <
- PES_MANDATORY_FIELDS_LEN)) {
+ if (unlikely(feed_data->pes_header_offset < PES_MANDATORY_FIELDS_LEN)) {
MPQ_DVB_ERR_PRINT(
"%s: Invalid header size %d\n",
__func__, feed_data->pes_header_offset);
@@ -3043,7 +3131,7 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed,
return ret;
}
- packet.raw_data_offset = payload_pipe->tspp_write_offset;
+ packet.raw_data_offset = pes_payload_sa - payload_pipe->buffer.iova;
if (partial_header) {
tspp2_pipe_last_address_used_get(payload_pipe->handle,
@@ -3056,28 +3144,34 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed,
packet.raw_data_len++;
packet.user_data_len = sizeof(meta_data);
- payload_pipe->tspp_write_offset += packet.raw_data_len;
- if (payload_pipe->tspp_write_offset >= payload_pipe->buffer.size)
- payload_pipe->tspp_write_offset -= payload_pipe->buffer.size;
+ payload_pipe->tspp_write_offset =
+ mpq_dmx_tspp2_addr_to_offset(payload_pipe, pes_payload_ea);
ret = mpq_streambuffer_data_write_deposit(stream_buffer,
packet.raw_data_len);
if (ret) {
- MPQ_DVB_ERR_PRINT(
+ MPQ_DVB_DBG_PRINT(
"%s: mpq_streambuffer_data_write_deposit failed, ret=%d\n",
__func__, ret);
return ret;
}
+ ret = mpq_dmx_tspp2_stream_buffer_event_check(feed, header_pipe);
+ if (ret) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: mpq_dmx_tspp2_stream_buffer_event_check failed, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
ret = mpq_streambuffer_pkt_write(stream_buffer, &packet,
(u8 *)&meta_data);
- if (ret) {
+ if (ret < 0) {
MPQ_DVB_ERR_PRINT(
"%s: mpq_streambuffer_pkt_write failed, ret=%d\n",
__func__, ret);
} else {
struct dmx_data_ready data;
- size_t len = 0;
mpq_dmx_update_decoder_stat(mpq_feed);
@@ -3088,35 +3182,24 @@ static int mpq_dmx_tspp2_process_video_headers(struct mpq_feed *mpq_feed,
* The following has to succeed when called here,
* after packet was written
*/
- data.buf.cookie = mpq_streambuffer_pkt_next(stream_buffer,
- feed_data->last_pkt_index, &len);
- if (data.buf.cookie < 0) {
- MPQ_DVB_ERR_PRINT(
- "%s: received invalid packet index %d\n",
- __func__, data.buf.cookie);
- } else {
- data.buf.offset = packet.raw_data_offset;
- data.buf.len = packet.raw_data_len;
- data.buf.pts_exists = pts_dts_info->pts_exist;
- data.buf.pts = pts_dts_info->pts;
- data.buf.dts_exists = pts_dts_info->dts_exist;
- data.buf.dts = pts_dts_info->dts;
- data.buf.tei_counter = 0;
- data.buf.cont_err_counter = 0;
- data.buf.ts_packets_num = 0;
- data.buf.ts_dropped_bytes = 0;
- data.status = DMX_OK_DECODER_BUF;
-
- /* save for next time: */
- feed_data->last_pkt_index = data.buf.cookie;
-
- MPQ_DVB_DBG_PRINT("%s: cookie=%d\n",
- __func__, data.buf.cookie);
-
- ret = mpq_dmx_tspp2_ts_event_check(feed, header_pipe);
- if (!ret)
- feed->data_ready_cb.ts(&feed->feed.ts, &data);
- }
+ data.buf.cookie = ret;
+ data.buf.offset = packet.raw_data_offset;
+ data.buf.len = packet.raw_data_len;
+ data.buf.pts_exists = pts_dts_info->pts_exist;
+ data.buf.pts = pts_dts_info->pts;
+ data.buf.dts_exists = pts_dts_info->dts_exist;
+ data.buf.dts = pts_dts_info->dts;
+ data.buf.tei_counter = 0;
+ data.buf.cont_err_counter = 0;
+ data.buf.ts_packets_num = 0;
+ data.buf.ts_dropped_bytes = 0;
+ data.status = DMX_OK_DECODER_BUF;
+
+ MPQ_DVB_DBG_PRINT("%s: cookie=%d\n", __func__, data.buf.cookie);
+
+ ret = mpq_dmx_tspp2_ts_event_check(feed, header_pipe);
+ if (!ret)
+ feed->data_ready_cb.ts(&feed->feed.ts, &data);
}
return ret ? ret : 1;
@@ -3180,11 +3263,18 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info,
feed = mpq_feed->dvb_demux_feed;
feed_data = &mpq_feed->video_info;
- found_pes = 0;
- feed->peslen = 0;
- feed_data->pes_header_offset = 0;
- feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
- while (!found_pes) {
+ /*
+ * Read all pending header descriptors. Typically only one descriptor
+ * will be read for each call of the pipe handler, but producer
+ * notifications might be missed so need to pick up the lost
+ * descriptors.
+ */
+ while (1) {
+ found_pes = 0;
+ feed->peslen = 0;
+ feed_data->pes_header_offset = 0;
+ feed_data->pes_header_left_bytes = PES_MANDATORY_FIELDS_LEN;
+
ret = tspp2_pipe_descriptor_get(pipe_info->handle, &iovec);
if (ret) {
/* should NEVER happen! */
@@ -3194,36 +3284,36 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info,
return -EINVAL;
}
+ /* No more descriptors */
if (iovec.size == 0)
break;
- if (unlikely(iovec.size !=
- TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE)) {
- /* should NEVER happen! */
- MPQ_DVB_ERR_PRINT(
- "%s: invalid VPES header desc size %d\n",
- __func__, iovec.size);
- return -EINVAL;
- }
-
data_buffer = mpq_dmx_get_kernel_addr(pipe_info, iovec.addr);
- if (unlikely(!data_buffer)) {
+ if (unlikely(!data_buffer || iovec.size !=
+ TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE)) {
/* should NEVER happen! */
- MPQ_DVB_ERR_PRINT(
- "%s: mpq_dmx_get_kernel_addr failed\n",
- __func__);
- return -EFAULT;
+ if (!data_buffer) {
+ MPQ_DVB_ERR_PRINT(
+ "%s: mpq_dmx_get_kernel_addr failed\n",
+ __func__);
+ ret = -EFAULT;
+ } else {
+ MPQ_DVB_ERR_PRINT(
+ "%s: invalid VPES header desc size %d, expected %d\n",
+ __func__, iovec.size,
+ TSPP2_DMX_SPS_VPES_HEADER_DESC_SIZE);
+ ret = -EINVAL;
+ }
+ return ret;
}
ret = mpq_dmx_tspp2_process_video_headers(mpq_feed,
data_buffer, 0, pipe_info, main_pipe);
- if (ret < 0) {
- MPQ_DVB_ERR_PRINT(
+ found_pes = (ret == 1);
+ if (ret < 0)
+ MPQ_DVB_DBG_PRINT(
"%s: mpq_dmx_tspp2_process_video_pes failed, ret=%d\n",
__func__, ret);
- return ret;
- }
- found_pes = (ret == 1);
/* re-queue buffer holding TS packet of PES header */
ret = tspp2_pipe_descriptor_put(pipe_info->handle,
@@ -3234,15 +3324,11 @@ static int mpq_dmx_tspp2_video_pipe_handler(struct pipe_info *pipe_info,
__func__, ret);
pipe_info->tspp_write_offset += iovec.size;
- if (pipe_info->tspp_write_offset >=
- pipe_info->buffer.size)
- pipe_info->tspp_write_offset -=
- pipe_info->buffer.size;
+ if (pipe_info->tspp_write_offset >= pipe_info->buffer.size)
+ pipe_info->tspp_write_offset -= pipe_info->buffer.size;
- pipe_info->tspp_read_offset =
- pipe_info->tspp_write_offset;
- pipe_info->bam_read_offset =
- pipe_info->tspp_write_offset;
+ pipe_info->tspp_read_offset = pipe_info->tspp_write_offset;
+ pipe_info->bam_read_offset = pipe_info->tspp_write_offset;
}
if (event == PIPE_EOS_EVENT) {
@@ -4558,8 +4644,7 @@ static int mpq_dmx_tspp2_notify_data_read(struct dmx_ts_feed *ts_feed,
}
static void mpq_dmx_tspp2_streambuffer_cb(struct mpq_streambuffer *sbuff,
- struct mpq_streambuffer_packet_header *packet,
- void *user_data)
+ u32 offset, size_t len, void *user_data)
{
int ret;
struct pipe_info *pipe_info = user_data;
@@ -4573,7 +4658,7 @@ static void mpq_dmx_tspp2_streambuffer_cb(struct mpq_streambuffer *sbuff,
return;
}
- ret = mpq_dmx_release_data(pipe_info, packet->raw_data_len);
+ ret = mpq_dmx_release_data(pipe_info, len);
if (ret)
MPQ_DVB_ERR_PRINT("%s: mpq_dmx_release_data failed, ret=%d\n",
__func__, ret);
@@ -4596,8 +4681,7 @@ static int mpq_dmx_tspp2_eos_cmd(struct mpq_tspp2_feed *tspp2_feed)
source_info = pipe_info->source_info;
- pipe_work = pipe_work_queue_allocate(
- &source_info->demux_src.work_queue);
+ pipe_work = pipe_work_queue_allocate(&pipe_info->work_queue);
if (pipe_work == NULL) {
MPQ_DVB_ERR_PRINT("%s: Cannot allocate pipe work\n", __func__);
mutex_unlock(&mpq_dmx_tspp2_info.mutex);
@@ -4606,9 +4690,11 @@ static int mpq_dmx_tspp2_eos_cmd(struct mpq_tspp2_feed *tspp2_feed)
pipe_work->pipe_info = pipe_info;
pipe_work->event = PIPE_EOS_EVENT;
+ pipe_work->event_count = 1;
pipe_work->session_id = pipe_info->session_id;
- pipe_work_queue_push(&source_info->demux_src.work_queue, pipe_work);
+ pipe_work_queue_push(&pipe_info->work_queue, pipe_work);
+ wake_up_all(&source_info->demux_src.wait_queue);
mutex_unlock(&mpq_dmx_tspp2_info.mutex);
@@ -5367,7 +5453,7 @@ static int mpq_dmx_tspp2_start_filtering(struct dvb_demux_feed *feed)
}
if (dvb_dmx_is_video_feed(feed)) {
- ret = mpq_streambuffer_register_pkt_dispose(
+ ret = mpq_streambuffer_register_data_dispose(
mpq_feed->video_info.video_buffer,
mpq_dmx_tspp2_streambuffer_cb,
tspp2_feed->main_pipe);
@@ -5669,6 +5755,8 @@ static int mpq_dmx_tspp2_write(struct dmx_demux *demux,
/* Process only whole TS packets */
data_length = (count / dvbdemux->ts_packet_size) *
dvbdemux->ts_packet_size;
+ if (!data_length)
+ return 0;
if ((!demux->frontend) || (demux->frontend->source != DMX_MEMORY_FE))
return -EINVAL;
@@ -5793,6 +5881,9 @@ static int mpq_dmx_tspp2_write(struct dmx_demux *demux,
pipe_info->bam_read_offset = 0;
pipe_info->eos_pending = 0;
pipe_info->session_id++;
+ pipe_info->handler_count = 0;
+ pipe_info->hw_notif_count = 0;
+ pipe_info->hw_missed_notif = 0;
if (!source_info->enabled) {
MPQ_DVB_DBG_PRINT(
@@ -5853,6 +5944,21 @@ static int mpq_dmx_tspp2_write_cancel(struct dmx_demux *demux)
return 0;
}
+static bool mpq_dmx_tspp2_pipe_do_work(struct source_info *source_info)
+{
+ struct pipe_info *pipe_info;
+ int i;
+
+ for (i = 0; i < TSPP2_NUM_PIPES; i++) {
+ pipe_info = &mpq_dmx_tspp2_info.pipes[i];
+ if (pipe_info->source_info == source_info &&
+ (!pipe_work_queue_empty(&pipe_info->work_queue)))
+ return true;
+ }
+
+ return false;
+}
+
static int mpq_dmx_tspp2_thread(void *arg)
{
struct source_info *source_info = arg;
@@ -5860,12 +5966,13 @@ static int mpq_dmx_tspp2_thread(void *arg)
struct pipe_info *pipe_info;
int ret;
unsigned long flags;
+ int i;
+ int j;
while (1) {
ret = wait_event_interruptible(
- source_info->demux_src.work_queue.wait_queue,
- !pipe_work_queue_empty(
- &source_info->demux_src.work_queue) ||
+ source_info->demux_src.wait_queue,
+ mpq_dmx_tspp2_pipe_do_work(source_info) ||
kthread_should_stop());
if (ret) {
MPQ_DVB_ERR_PRINT(
@@ -5878,40 +5985,56 @@ static int mpq_dmx_tspp2_thread(void *arg)
break;
}
- pipe_work = pipe_work_queue_pop(
- &source_info->demux_src.work_queue);
+ for (i = 0; i < TSPP2_NUM_PIPES; i++) {
+ pipe_info = &mpq_dmx_tspp2_info.pipes[i];
- /*
- * The pipe work might have been flushed
- * if filter was stopped
- */
- if (pipe_work == NULL) {
- MPQ_DVB_DBG_PRINT("%s: pipe was flushed\n", __func__);
- continue;
- }
+ /*
+ * Lock pipe mutex to protect against pipe being closed
+ * during its processing
+ */
+ if (mutex_lock_interruptible(&pipe_info->mutex))
+ continue;
- pipe_info = pipe_work->pipe_info;
+ if (pipe_info->source_info != source_info ||
+ pipe_work_queue_empty(&pipe_info->work_queue)) {
+ mutex_unlock(&pipe_info->mutex);
+ continue;
+ }
- /*
- * Lock pipe mutex to protect against pipe being closed
- * during its processing
- */
- if (mutex_lock_interruptible(&pipe_info->mutex))
- continue;
+ pipe_work = pipe_work_queue_pop(&pipe_info->work_queue);
- spin_lock_irqsave(&pipe_info->lock, flags);
- if (pipe_info->ref_count && pipe_info->pipe_handler &&
- pipe_work->session_id == pipe_info->session_id) {
- spin_unlock_irqrestore(&pipe_info->lock, flags);
- pipe_info->pipe_handler(pipe_info, pipe_work->event);
- } else {
- spin_unlock_irqrestore(&pipe_info->lock, flags);
- }
+ /*
+ * The pipe work might have been flushed
+ * if filter was stopped
+ */
+ if (pipe_work == NULL) {
+ MPQ_DVB_DBG_PRINT(
+ "%s: pipe was flushed\n", __func__);
+ mutex_unlock(&pipe_info->mutex);
+ continue;
+ }
- mutex_unlock(&pipe_work->pipe_info->mutex);
+ spin_lock_irqsave(&pipe_info->lock, flags);
+ if (pipe_info->ref_count && pipe_info->pipe_handler &&
+ pipe_work->session_id ==
+ pipe_info->session_id) {
+ spin_unlock_irqrestore(&pipe_info->lock, flags);
+ MPQ_DVB_DBG_PRINT(
+ "%s: calling pipe %d handler %d times\n",
+ __func__, i, pipe_work->event_count);
+ for (j = 0; j < pipe_work->event_count; j++)
+ pipe_info->pipe_handler(pipe_info,
+ pipe_work->event);
+ pipe_info->handler_count += j;
+ } else {
+ spin_unlock_irqrestore(&pipe_info->lock, flags);
+ }
- pipe_work_queue_release(&source_info->demux_src.work_queue,
- pipe_work);
+ mutex_unlock(&pipe_info->mutex);
+
+ pipe_work_queue_release(&pipe_info->work_queue,
+ pipe_work);
+ }
}
/* Terminate thread gracefully */
@@ -6143,6 +6266,10 @@ static int mpq_dmx_tspp2_pipes_print(struct seq_file *s, void *p)
pipe_info->hw_notif_rate_hz);
seq_printf(s, "interrupt count: %d\n",
pipe_info->hw_notif_count);
+ seq_printf(s, "int. miss count: %d\n",
+ pipe_info->hw_missed_notif);
+ seq_printf(s, "handler count : %d\n",
+ pipe_info->handler_count);
seq_printf(s, "buffer address : 0x%p(0x%p)\n",
pipe_info->buffer.mem,
(void *)pipe_info->buffer.iova);
@@ -6346,6 +6473,7 @@ static const struct file_operations dbgfs_index_tables_fops = {
.release = single_release,
.owner = THIS_MODULE,
};
+
static const struct file_operations dbgfs_sources_fops = {
.open = mpq_dmx_tspp2_sources_open,
.read = seq_read,
@@ -6354,6 +6482,7 @@ static const struct file_operations dbgfs_sources_fops = {
.owner = THIS_MODULE,
};
+
/**
* Initialize a single demux device.
*
@@ -6533,7 +6662,7 @@ static int __init mpq_dmx_tspp2_plugin_init(void)
init_completion(&source_info->completion);
- pipe_work_queue_init(&source_info->demux_src.work_queue);
+ init_waitqueue_head(&source_info->demux_src.wait_queue);
/* Initialize source processing thread */
source_info->demux_src.thread =
@@ -6564,6 +6693,7 @@ static int __init mpq_dmx_tspp2_plugin_init(void)
for (i = 0; i < TSPP2_NUM_PIPES; i++) {
mpq_dmx_tspp2_info.pipes[i].handle = TSPP2_INVALID_HANDLE;
mutex_init(&mpq_dmx_tspp2_info.pipes[i].mutex);
+ pipe_work_queue_init(&mpq_dmx_tspp2_info.pipes[i].work_queue);
spin_lock_init(&mpq_dmx_tspp2_info.pipes[i].lock);
}
mpq_dmx_tspp2_info.user_count = 0;
@@ -6619,7 +6749,6 @@ static int __init mpq_dmx_tspp2_plugin_init(void)
mpq_dmx_tspp2_info.debugfs_dmx_dir,
NULL,
&dbgfs_sources_fops);
-
}
return ret;
diff --git a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h
index 2353dc1433c4..075a730f46d1 100644
--- a/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h
+++ b/drivers/media/platform/msm/dvb/demux/mpq_dmx_plugin_tspp_v2.h
@@ -34,7 +34,7 @@
#define TSPP2_DMX_MAX_FEED_OPS 4
-#define TSPP2_DMX_PIPE_WORK_POOL_SIZE 100
+#define TSPP2_DMX_PIPE_WORK_POOL_SIZE 500
/* Max number of section filters */
#define TSPP2_DMX_MAX_SECTION_FILTER_NUM 64
@@ -218,15 +218,18 @@ enum mpq_dmx_tspp2_pipe_event {
/**
* struct pipe_work - Work scheduled each time we receive data from a pipe
*
- * @pipe_info: Associated pipe
- * @event: Source event type for this work item
- * @session_id: pipe_info.session_id cached value at time of pipe work creation
- * @next: List node field for pipe_work_queue lists
+ * @pipe_info: Associated pipe
+ * @event: Source event type for this work item
+ * @session_id: pipe_info.session_id cached value at time of pipe
+ * work creation.
+ * @event_count: Number of events included in this work
+ * @next: List node field for pipe_work_queue lists
*/
struct pipe_work {
struct pipe_info *pipe_info;
enum mpq_dmx_tspp2_pipe_event event;
u32 session_id;
+ u32 event_count;
struct list_head next;
};
@@ -237,14 +240,12 @@ struct pipe_work {
* @work_list: Queue of pipe_work element source thread should process
* @free_list: List of free pipe_work objects
* @lock: Lock to protect modifications to lists
- * @wait_queue: Processing thread wait queue
*/
struct pipe_work_queue {
struct pipe_work work_pool[TSPP2_DMX_PIPE_WORK_POOL_SIZE];
struct list_head work_list;
struct list_head free_list;
spinlock_t lock;
- wait_queue_head_t wait_queue;
};
/**
@@ -315,6 +316,7 @@ struct mpq_dmx_tspp2_pipe_buffer {
* @mutex: Mutex for protecting access to pipe info
* @eos_pending: Flag specifying whether the pipe handler has an
* end of stream notification that should be handled.
+ * @work_queue: pipe_work queue of work pending for this pipe
* @hw_notif_count: Total number of HW notifications
* @hw_notif_rate_hz: Rate of HW notifications in unit of Hz
* @hw_notif_last_time: Time at which previous HW notification was received
@@ -337,10 +339,13 @@ struct pipe_info {
enum mpq_dmx_tspp2_pipe_event event);
struct mutex mutex;
int eos_pending;
+ struct pipe_work_queue work_queue;
/* debug-fs */
u32 hw_notif_count;
u32 hw_notif_rate_hz;
+ u32 hw_missed_notif;
+ u32 handler_count;
struct timespec hw_notif_last_time;
};
@@ -490,7 +495,7 @@ struct buffer_insertion_source {
* struct demuxing_source - demuxing source related resources
*
* @thread: Source processing thread
- * @work_queue: Pipe work queue object
+ * @wait_queue: Processing thread wait queue
* @mpq_demux: Pointer to the demux connected to this source
* @clear_section_pipe: Pipe opened to hold clear TS packets of sections
* @scrambled_section_pipe: Pipe opened to hold scrambled TS packets of
@@ -498,7 +503,7 @@ struct buffer_insertion_source {
*/
struct demuxing_source {
struct task_struct *thread;
- struct pipe_work_queue work_queue;
+ wait_queue_head_t wait_queue;
struct mpq_demux *mpq_demux;
struct pipe_info *clear_section_pipe;
struct pipe_info *scrambled_section_pipe;
diff --git a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h
index d70555ad101d..a03c94ecc0cc 100644
--- a/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h
+++ b/drivers/media/platform/msm/dvb/include/mpq_stream_buffer.h
@@ -103,19 +103,19 @@
* - Disposal of packets:
* mpq_streambuffer_pkt_dispose(...)
*
- * For linear buffer mode, disposing of a packet with data size > 0, causes
- * the current buffer to be marked as free for writing, and triggers moving to
+ * For linear buffer mode, disposing of a packet with data size > 0,
+ * regardless of the 'dispose_data' parameter, causes the current buffer's
+ * data to be disposed and marked as free for writing, and triggers moving to
* the next available buffer, that shall now be the current read buffer.
-
- *
*/
struct mpq_streambuffer;
struct mpq_streambuffer_packet_header;
-typedef void (*mpq_streambuffer_pkt_dispose_cb) (
+typedef void (*mpq_streambuffer_dispose_cb) (
struct mpq_streambuffer *sbuff,
- struct mpq_streambuffer_packet_header *packet,
+ u32 offset,
+ size_t len,
void *user_data);
enum mpq_streambuffer_mode {
@@ -143,7 +143,7 @@ struct mpq_streambuffer {
enum mpq_streambuffer_mode mode;
u32 buffers_num;
u32 pending_buffers_count;
- mpq_streambuffer_pkt_dispose_cb cb;
+ mpq_streambuffer_dispose_cb cb;
void *cb_user_data;
};
@@ -357,8 +357,8 @@ ssize_t mpq_streambuffer_data_read_user(
* mpq_streambuffer_data_read_dispose - Advances the raw-buffer read pointer.
* Assumes the raw-data was read by the user directly.
*
- * @sbuff: The stream buffer
- * @len: The length of the raw-data to be disposed
+ * @sbuff: The stream buffer
+ * @len: The length of the raw-data to be disposed
*
* Return error status, -EINVAL if buffer there's no enough data to
* be disposed
@@ -422,9 +422,9 @@ ssize_t mpq_streambuffer_data_avail(
* Returns error status
* -EINVAL if arguments are invalid
*/
-int mpq_streambuffer_register_pkt_dispose(
+int mpq_streambuffer_register_data_dispose(
struct mpq_streambuffer *sbuff,
- mpq_streambuffer_pkt_dispose_cb cb_func,
+ mpq_streambuffer_dispose_cb cb_func,
void *user_data);
/**
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
index d469d8f92c0b..a8df20c039fb 100644
--- a/drivers/media/platform/msm/vidc/hfi_packetization.c
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -267,7 +267,7 @@ int create_pkt_set_cmd_sys_resource(
break;
}
default:
- dprintk(VIDC_ERR, "Invalid resource_id %d",
+ dprintk(VIDC_ERR, "Invalid resource_id %d\n",
resource_hdr->resource_id);
rc = -EINVAL;
}
@@ -912,7 +912,7 @@ int create_pkt_cmd_session_set_property(
HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT;
hfi = (struct hfi_nal_stream_format_select *)
&pkt->rg_property_data[1];
- dprintk(VIDC_DBG, "data is :%d",
+ dprintk(VIDC_DBG, "data is :%d\n",
prop->nal_stream_format_select);
hfi->nal_stream_format_select = hal_to_hfi_type(
HAL_PARAM_NAL_STREAM_FORMAT_SELECT,
@@ -934,7 +934,7 @@ int create_pkt_cmd_session_set_property(
pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DISPLAY;
break;
default:
- dprintk(VIDC_ERR, "invalid output order: 0x%x",
+ dprintk(VIDC_ERR, "invalid output order: 0x%x\n",
*data);
break;
}
@@ -1023,7 +1023,7 @@ int create_pkt_cmd_session_set_property(
pkt->rg_property_data[1] = HFI_DIVX_FORMAT_6;
break;
default:
- dprintk(VIDC_ERR, "Invalid divx format: 0x%x", *data);
+ dprintk(VIDC_ERR, "Invalid divx format: 0x%x\n", *data);
break;
}
pkt->size += sizeof(u32) * 2;
@@ -1118,14 +1118,14 @@ int create_pkt_cmd_session_set_property(
if (hfi->profile <= 0) {
hfi->profile = HFI_H264_PROFILE_HIGH;
dprintk(VIDC_WARN,
- "Profile %d not supported, falling back to high",
+ "Profile %d not supported, falling back to high\n",
prop->profile);
}
if (!hfi->level) {
hfi->level = 1;
dprintk(VIDC_WARN,
- "Level %d not supported, falling back to high",
+ "Level %d not supported, falling back to high\n",
prop->level);
}
@@ -1175,8 +1175,9 @@ int create_pkt_cmd_session_set_property(
pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_VFR;
break;
default:
- dprintk(VIDC_ERR, "Invalid Rate control setting: 0x%x",
- (int) pdata);
+ dprintk(VIDC_ERR,
+ "Invalid Rate control setting: 0x%x\n",
+ (int)pdata);
break;
}
pkt->size += sizeof(u32) * 2;
@@ -1225,7 +1226,7 @@ int create_pkt_cmd_session_set_property(
hfi->mode = HFI_H264_DB_MODE_ALL_BOUNDARY;
break;
default:
- dprintk(VIDC_ERR, "Invalid deblocking mode: 0x%x",
+ dprintk(VIDC_ERR, "Invalid deblocking mode: 0x%x\n",
prop->mode);
break;
}
@@ -1358,7 +1359,7 @@ int create_pkt_cmd_session_set_property(
hfi->rotation = HFI_ROTATE_270;
break;
default:
- dprintk(VIDC_ERR, "Invalid rotation setting: 0x%x",
+ dprintk(VIDC_ERR, "Invalid rotation setting: 0x%x\n",
prop->rotate);
rc = -EINVAL;
break;
@@ -1374,7 +1375,7 @@ int create_pkt_cmd_session_set_property(
hfi->flip = HFI_FLIP_VERTICAL;
break;
default:
- dprintk(VIDC_ERR, "Invalid flip setting: 0x%x",
+ dprintk(VIDC_ERR, "Invalid flip setting: 0x%x\n",
prop->flip);
rc = -EINVAL;
break;
@@ -1407,8 +1408,9 @@ int create_pkt_cmd_session_set_property(
hfi->mode = HFI_INTRA_REFRESH_RANDOM;
break;
default:
- dprintk(VIDC_ERR, "Invalid intra refresh setting: 0x%x",
- prop->mode);
+ dprintk(VIDC_ERR,
+ "Invalid intra refresh setting: 0x%x\n",
+ prop->mode);
break;
}
hfi->air_mbs = prop->air_mbs;
@@ -1440,7 +1442,7 @@ int create_pkt_cmd_session_set_property(
hfi->multi_slice = HFI_MULTI_SLICE_BY_BYTE_COUNT;
break;
default:
- dprintk(VIDC_ERR, "Invalid slice settings: 0x%x",
+ dprintk(VIDC_ERR, "Invalid slice settings: 0x%x\n",
prop->multi_slice);
break;
}
@@ -1630,7 +1632,7 @@ int create_pkt_cmd_session_set_property(
case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
case HAL_PARAM_VENC_LOW_LATENCY:
default:
- dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x", ptype);
+ dprintk(VIDC_ERR, "DEFAULT: Calling 0x%x\n", ptype);
rc = -ENOTSUPP;
break;
}
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
index e7c0afebe1da..8b98c4be10db 100644
--- a/drivers/media/platform/msm/vidc/hfi_response_handler.c
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -14,7 +14,7 @@
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <mach/msm_smem.h>
+#include <soc/qcom/smem.h>
#include "vidc_hfi_helper.h"
#include "vidc_hfi_io.h"
#include "msm_vidc_debug.h"
@@ -112,10 +112,11 @@ static void hfi_process_sess_evt_seq_changed(
struct hfi_profile_level *profile_level;
u8 *data_ptr;
int prop_id;
- dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY");
+ dprintk(VIDC_DBG, "RECEIVED: EVENT_NOTIFY\n");
if (sizeof(struct hfi_msg_event_notify_packet)
> pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_init_done: bad_pkt_size\n");
return;
}
@@ -152,7 +153,7 @@ static void hfi_process_sess_evt_seq_changed(
(struct hfi_frame_size *) data_ptr;
event_notify.width = frame_sz->width;
event_notify.height = frame_sz->height;
- dprintk(VIDC_DBG, "height:%d width:%d\n",
+ dprintk(VIDC_DBG, "height: %d width: %d\n",
frame_sz->height, frame_sz->width);
data_ptr +=
sizeof(struct hfi_frame_size);
@@ -161,14 +162,15 @@ static void hfi_process_sess_evt_seq_changed(
data_ptr = data_ptr + sizeof(u32);
profile_level =
(struct hfi_profile_level *) data_ptr;
- dprintk(VIDC_DBG, "profile:%d level:%d\n",
+ dprintk(VIDC_DBG, "profile: %d level: %d\n",
profile_level->profile,
profile_level->level);
data_ptr +=
sizeof(struct hfi_profile_level);
break;
default:
- dprintk(VIDC_ERR, "%s cmd:0x%x not supported\n",
+ dprintk(VIDC_ERR,
+ "%s cmd: 0x%x not supported\n",
__func__, prop_id);
break;
}
@@ -188,10 +190,12 @@ static void hfi_process_evt_release_buffer_ref(
struct hfi_msg_release_buffer_ref_event_packet *data;
- dprintk(VIDC_DBG, "RECEIVED:EVENT_NOTIFY - release_buffer_reference");
+ dprintk(VIDC_DBG,
+ "RECEIVED: EVENT_NOTIFY - release_buffer_reference\n");
if (sizeof(struct hfi_msg_event_notify_packet)
> pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_init_done: bad_pkt_size\n");
return;
}
@@ -248,11 +252,11 @@ static void hfi_process_event_notify(
struct list_head *sessions, struct mutex *session_lock)
{
struct hal_session *sess = NULL;
- dprintk(VIDC_DBG, "RECVD:EVENT_NOTIFY");
+ dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n");
if (!callback || !pkt ||
pkt->size < sizeof(struct hfi_msg_event_notify_packet)) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return;
}
sess = (struct hal_session *)pkt->session_id;
@@ -264,25 +268,28 @@ static void hfi_process_event_notify(
hfi_process_sys_error(callback, device_id);
break;
case HFI_EVENT_SESSION_ERROR:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR");
+ dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR\n");
if (!validate_session_pkt(sessions, sess, session_lock))
hfi_process_session_error(callback, device_id, pkt);
break;
case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED");
+ dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED\n");
if (!validate_session_pkt(sessions, sess, session_lock))
hfi_process_sess_evt_seq_changed(callback,
device_id, pkt);
break;
case HFI_EVENT_SESSION_PROPERTY_CHANGED:
- dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED");
+ dprintk(VIDC_INFO, "HFI_EVENT_SESSION_PROPERTY_CHANGED\n");
break;
case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE\n");
- hfi_process_evt_release_buffer_ref(callback, device_id, pkt);
+ if (!validate_session_pkt(sessions, sess, session_lock))
+ hfi_process_evt_release_buffer_ref(callback,
+ device_id, pkt);
break;
default:
- dprintk(VIDC_WARN, "hal_process_event_notify:unkown_event_id");
+ dprintk(VIDC_WARN,
+ "hal_process_event_notify: unknown_event_id\n");
break;
}
}
@@ -297,9 +304,10 @@ static void hfi_process_sys_init_done(
int prop_id;
enum vidc_status status = VIDC_ERR_NONE;
- dprintk(VIDC_DBG, "RECEIVED:SYS_INIT_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SYS_INIT_DONE\n");
if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) {
- dprintk(VIDC_ERR, "hal_process_sys_init_done:bad_pkt_size: %d",
+ dprintk(VIDC_ERR,
+ "hal_process_sys_init_done: bad_pkt_size: %d\n",
pkt->size);
return;
}
@@ -308,8 +316,8 @@ static void hfi_process_sys_init_done(
if (!status) {
if (pkt->num_properties == 0) {
- dprintk(VIDC_ERR, "hal_process_sys_init_done:"
- "no_properties");
+ dprintk(VIDC_ERR,
+ "hal_process_sys_init_done: no_properties\n");
status = VIDC_ERR_FAIL;
goto err_no_prop;
}
@@ -318,8 +326,8 @@ static void hfi_process_sys_init_done(
hfi_msg_sys_init_done_packet) + sizeof(u32);
if (rem_bytes == 0) {
- dprintk(VIDC_ERR, "hal_process_sys_init_done:"
- "missing_prop_info");
+ dprintk(VIDC_ERR,
+ "hal_process_sys_init_done: missing_prop_info\n");
status = VIDC_ERR_FAIL;
goto err_no_prop;
}
@@ -351,8 +359,8 @@ static void hfi_process_sys_init_done(
break;
}
default:
- dprintk(VIDC_ERR, "hal_process_sys_init_done:"
- "bad_prop_id");
+ dprintk(VIDC_ERR,
+ "hal_process_sys_init_done: bad_prop_id\n");
status = VIDC_ERR_BAD_PARAM;
break;
}
@@ -380,11 +388,11 @@ static void hfi_process_sys_rel_resource_done(
enum vidc_status status = VIDC_ERR_NONE;
u32 pkt_size;
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
- dprintk(VIDC_DBG, "RECEIVED:SYS_RELEASE_RESOURCE_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SYS_RELEASE_RESOURCE_DONE\n");
pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet);
if (pkt_size > pkt->size) {
dprintk(VIDC_ERR,
- "hal_process_sys_rel_resource_done:bad size:%d",
+ "hal_process_sys_rel_resource_done: bad size: %d\n",
pkt->size);
return;
}
@@ -460,7 +468,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read(
if (rem_bytes == 0) {
dprintk(VIDC_ERR,
- "hfi_msg_sys_session_init_done:missing_prop_info");
+ "hfi_msg_sys_session_init_done: missing_prop_info\n");
return VIDC_ERR_FAIL;
}
@@ -567,7 +575,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read(
(struct hfi_profile_level_supported *)
(data_ptr + next_offset);
ptr = (char *) &prop->rg_profile_level[0];
- dprintk(VIDC_DBG, "prop->profile_count:%d\n",
+ dprintk(VIDC_DBG, "prop->profile_count: %d\n",
prop->profile_count);
prop_count = prop->profile_count;
while (prop_count) {
@@ -652,7 +660,7 @@ enum vidc_status hfi_process_sess_init_done_prop_read(
}
default:
dprintk(VIDC_DBG,
- "%s default case - 0x%x", __func__, prop_id);
+ "%s default case - 0x%x\n", __func__, prop_id);
}
rem_bytes -= next_offset;
data_ptr += next_offset;
@@ -669,7 +677,7 @@ static void hfi_process_sess_get_prop_profile_level(
dprintk(VIDC_DBG, "Entered %s\n", __func__);
if (!prop) {
dprintk(VIDC_ERR,
- "hal_process_sess_get_profile_level:bad_prop: %p",
+ "hal_process_sess_get_profile_level: bad_prop: %p\n",
prop);
return;
}
@@ -678,7 +686,7 @@ static void hfi_process_sess_get_prop_profile_level(
if (!req_bytes || (req_bytes % sizeof(struct hfi_profile_level))) {
dprintk(VIDC_ERR,
- "hal_process_sess_get_profile_level:bad_pkt: %d",
+ "hal_process_sess_get_profile_level: bad_pkt: %d\n",
req_bytes);
return;
}
@@ -686,7 +694,7 @@ static void hfi_process_sess_get_prop_profile_level(
&prop->rg_property_data[1];
profile_level->profile = hfi_profile_level->profile;
profile_level->level = hfi_profile_level->level;
- dprintk(VIDC_DBG, "%s profile:%d level:%d\n",
+ dprintk(VIDC_DBG, "%s profile: %d level: %d\n",
__func__, profile_level->profile,
profile_level->level);
}
@@ -698,10 +706,9 @@ static void hfi_process_sess_get_prop_buf_req(
struct hfi_buffer_requirements *hfi_buf_req;
u32 req_bytes;
- dprintk(VIDC_DBG, "Entered ");
if (!prop) {
dprintk(VIDC_ERR,
- "hal_process_sess_get_prop_buf_req:bad_prop: %p",
+ "hal_process_sess_get_prop_buf_req: bad_prop: %p\n",
prop);
return;
}
@@ -712,7 +719,7 @@ static void hfi_process_sess_get_prop_buf_req(
struct hfi_buffer_requirements)) ||
(!prop->rg_property_data[1])) {
dprintk(VIDC_ERR,
- "hal_process_sess_get_prop_buf_req:bad_pkt: %d",
+ "hal_process_sess_get_prop_buf_req: bad_pkt: %d\n",
req_bytes);
return;
}
@@ -726,9 +733,9 @@ static void hfi_process_sess_get_prop_buf_req(
buffer_count_actual)))
dprintk(VIDC_WARN,
"hal_process_sess_get_prop_buf_req:"
- "bad_buf_req");
+ "bad_buf_req\n");
- dprintk(VIDC_DBG, "got buffer requirements for: %d",
+ dprintk(VIDC_DBG, "got buffer requirements for: %d\n",
hfi_buf_req->buffer_type);
switch (hfi_buf_req->buffer_type) {
case HFI_BUFFER_INPUT:
@@ -796,7 +803,7 @@ static void hfi_process_sess_get_prop_buf_req(
break;
default:
dprintk(VIDC_ERR,
- "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d",
+ "hal_process_sess_get_prop_buf_req: bad_buffer_type: %d\n",
hfi_buf_req->buffer_type);
break;
}
@@ -816,13 +823,14 @@ static void hfi_process_session_prop_info(
dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO\n");
if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_prop_info:bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_prop_info: bad_pkt_size\n");
return;
}
if (pkt->num_properties == 0) {
dprintk(VIDC_ERR,
- "hal_process_session_prop_info:no_properties");
+ "hal_process_session_prop_info: no_properties\n");
return;
}
@@ -849,8 +857,8 @@ static void hfi_process_session_prop_info(
callback(SESSION_PROPERTY_INFO, &cmd_done);
break;
default:
- dprintk(VIDC_ERR, "hal_process_session_prop_info:"
- "unknown_prop_id: %d",
+ dprintk(VIDC_ERR,
+ "hal_process_session_prop_info: unknown_prop_id: %d\n",
pkt->rg_property_data[0]);
break;
}
@@ -863,10 +871,11 @@ static void hfi_process_session_init_done(
struct msm_vidc_cb_cmd_done cmd_done;
struct vidc_hal_session_init_done session_init_done;
struct hal_session *sess_close = NULL;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_INIT_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE\n");
if (sizeof(struct hfi_msg_sys_session_init_done_packet)
> pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_init_done:bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_init_done: bad_pkt_size\n");
return;
}
@@ -886,7 +895,7 @@ static void hfi_process_session_init_done(
sess_close = (struct hal_session *)pkt->session_id;
if (sess_close) {
dprintk(VIDC_INFO,
- "Sess init failed: Deleting session: 0x%x 0x%p",
+ "Sess init failed: Deleting session: 0x%x 0x%p\n",
sess_close->session_id, sess_close);
list_del(&sess_close->list);
kfree(sess_close);
@@ -902,12 +911,13 @@ static void hfi_process_session_load_res_done(
struct hfi_msg_session_load_resources_done_packet *pkt)
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_LOAD_RESOURCES_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE\n");
if (sizeof(struct hfi_msg_session_load_resources_done_packet) !=
pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_load_res_done:"
- " bad packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_load_res_done: bad packet size: %d\n",
+ pkt->size);
return;
}
@@ -928,11 +938,12 @@ static void hfi_process_session_flush_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_FLUSH_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE\n");
if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_flush_done: "
- "bad packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_flush_done: bad packet size: %d\n",
+ pkt->size);
return;
}
@@ -952,11 +963,12 @@ static void hfi_process_session_etb_done(
{
struct msm_vidc_cb_data_done data_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_ETB_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE\n");
if (!pkt || pkt->size <
sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_etb_done:bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_etb_done: bad_pkt_size\n");
return;
}
@@ -988,13 +1000,13 @@ static void hfi_process_session_ftb_done(
struct hal_session *session;
if (!msg_hdr) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return;
}
session = (struct hal_session *)
((struct hal_session *) pack->session_id)->session_id;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_FTB_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE\n");
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
@@ -1006,11 +1018,11 @@ static void hfi_process_session_ftb_done(
hfi_msg_session_fill_buffer_done_compressed_packet)
> pkt->size) {
dprintk(VIDC_ERR,
- "hal_process_session_ftb_done: bad_pkt_size");
+ "hal_process_session_ftb_done: bad_pkt_size\n");
return;
} else if (pkt->error_type != HFI_ERR_NONE) {
dprintk(VIDC_ERR,
- "got buffer back with error %x",
+ "got buffer back with error %x\n",
pkt->error_type);
/* Proceed with the FBD */
}
@@ -1045,8 +1057,8 @@ static void hfi_process_session_ftb_done(
if (sizeof(struct
hfi_msg_session_fbd_uncompressed_plane0_packet)
> pkt->size) {
- dprintk(VIDC_ERR, "hal_process_session_ftb_done:"
- "bad_pkt_size");
+ dprintk(VIDC_ERR,
+ "hal_process_session_ftb_done: bad_pkt_size\n");
return;
}
@@ -1092,12 +1104,13 @@ static void hfi_process_session_start_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_START_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE\n");
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_start_done_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_start_done:"
- "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_start_done: bad packet/packet size: %d\n",
+ pkt->size);
return;
}
@@ -1117,12 +1130,13 @@ static void hfi_process_session_stop_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_STOP_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE\n");
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_stop_done_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_stop_done:"
- "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_stop_done: bad packet/packet size: %d\n",
+ pkt->size);
return;
}
@@ -1142,12 +1156,13 @@ static void hfi_process_session_rel_res_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_RESOURCES_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE\n");
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_session_release_resources_done_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_rel_res_done:"
- "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_rel_res_done: bad packet/packet size: %d\n",
+ pkt->size);
return;
}
@@ -1169,7 +1184,7 @@ static void hfi_process_session_rel_buf_done(
if (!pkt || pkt->size !=
sizeof(struct
hfi_msg_session_release_buffers_done_packet)) {
- dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR, "bad packet/packet size: %d\n", pkt->size);
return;
}
memset(&cmd_done, 0, sizeof(struct msm_vidc_cb_cmd_done));
@@ -1193,12 +1208,13 @@ static void hfi_process_session_end_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_END_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE\n");
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_sys_session_end_done_packet)) {
- dprintk(VIDC_ERR, "hal_process_session_end_done: "
- "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR,
+ "hal_process_session_end_done: bad packet/packet size: %d\n",
+ pkt->size);
return;
}
@@ -1218,11 +1234,11 @@ static void hfi_process_session_abort_done(
{
struct msm_vidc_cb_cmd_done cmd_done;
- dprintk(VIDC_DBG, "RECEIVED:SESSION_ABORT_DONE");
+ dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE\n");
if (!pkt || pkt->size !=
sizeof(struct hfi_msg_sys_session_abort_done_packet)) {
- dprintk(VIDC_ERR, "%s: bad packet/packet size: %d",
+ dprintk(VIDC_ERR, "%s: bad packet/packet size: %d\n",
__func__, pkt ? pkt->size : 0);
return;
}
@@ -1245,7 +1261,7 @@ static void hfi_process_session_get_seq_hdr_done(
if (!pkt || pkt->size !=
sizeof(struct
hfi_msg_session_get_sequence_header_done_packet)) {
- dprintk(VIDC_ERR, "bad packet/packet size: %d", pkt->size);
+ dprintk(VIDC_ERR, "bad packet/packet size: %d\n", pkt->size);
return;
}
memset(&data_done, 0, sizeof(struct msm_vidc_cb_data_done));
@@ -1256,7 +1272,7 @@ static void hfi_process_session_get_seq_hdr_done(
data_done.status = hfi_map_err_status((u32)pkt->error_type);
data_done.output_done.packet_buffer1 = pkt->sequence_header;
data_done.output_done.filled_len1 = pkt->header_len;
- dprintk(VIDC_INFO, "seq_hdr: %p, Length: %d",
+ dprintk(VIDC_INFO, "seq_hdr: %p, Length: %d\n",
pkt->sequence_header, pkt->header_len);
callback(SESSION_GET_SEQ_HDR_DONE, &data_done);
}
@@ -1278,7 +1294,7 @@ static void hfi_process_sys_get_prop_image_version(
!pkt->rg_property_data[1] ||
pkt->num_properties > 1) {
dprintk(VIDC_ERR,
- "hfi_process_sys_get_prop_image_version:bad_pkt: %d",
+ "hfi_process_sys_get_prop_image_version: bad_pkt: %d\n",
req_bytes);
return;
}
@@ -1330,7 +1346,7 @@ static void hfi_process_sys_property_info(
break;
default:
dprintk(VIDC_ERR,
- "hfi_process_sys_property_info:unknown_prop_id: %d\n",
+ "hfi_process_sys_property_info: unknown_prop_id: %d\n",
pkt->rg_property_data[0]);
}
}
@@ -1344,13 +1360,14 @@ u32 hfi_process_msg_packet(
struct hal_session *sess = NULL;
if (!callback || !msg_hdr || msg_hdr->size <
VIDC_IFACEQ_MIN_PKT_SIZE) {
- dprintk(VIDC_ERR, "hal_process_msg_packet:bad"
- "packet/packet size: %d", msg_hdr->size);
+ dprintk(VIDC_ERR,
+ "hal_process_msg_packet: bad packet/packet size: %d\n",
+ msg_hdr->size);
rc = -EINVAL;
return rc;
}
- dprintk(VIDC_INFO, "Received: 0x%x in ", msg_hdr->packet);
+ dprintk(VIDC_INFO, "Received: 0x%x\n", msg_hdr->packet);
rc = (u32) msg_hdr->packet;
sess = (struct hal_session *)((struct
vidc_hal_session_cmd_pkt*) msg_hdr)->session_id;
@@ -1458,7 +1475,7 @@ u32 hfi_process_msg_packet(
msg_hdr);
break;
default:
- dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d", msg_hdr->packet);
+ dprintk(VIDC_DBG, "UNKNOWN_MSG_TYPE : %d\n", msg_hdr->packet);
break;
}
return rc;
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
index 8ef6f99aacf7..f4eb59b267a8 100644
--- a/drivers/media/platform/msm/vidc/msm_smem.c
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -63,14 +63,15 @@ static int get_device_address(struct smem_client *smem_client,
clnt = smem_client->clnt;
if (!clnt) {
- dprintk(VIDC_ERR, "Invalid client");
+ dprintk(VIDC_ERR, "Invalid client\n");
return -EINVAL;
}
rc = msm_smem_get_domain_partition(smem_client, flags, buffer_type,
&domain, &partition);
if (rc) {
- dprintk(VIDC_ERR, "Failed to get domain and partition: %d", rc);
+ dprintk(VIDC_ERR, "Failed to get domain and partition: %d\n",
+ rc);
goto mem_domain_get_failed;
}
@@ -84,16 +85,16 @@ static int get_device_address(struct smem_client *smem_client,
}
if (is_iommu_present(smem_client->res)) {
dprintk(VIDC_DBG,
- "Calling ion_map_iommu - domain: %d, partition: %d",
+ "Calling ion_map_iommu - domain: %d, partition: %d\n",
domain, partition);
rc = ion_map_iommu(clnt, hndl, domain, partition, align,
0, iova, buffer_size, 0, 0);
} else {
- dprintk(VIDC_DBG, "Using physical memory address");
+ dprintk(VIDC_DBG, "Using physical memory address\n");
rc = ion_phys(clnt, hndl, iova, (size_t *)buffer_size);
}
if (rc) {
- dprintk(VIDC_ERR, "ion memory map failed - %d", rc);
+ dprintk(VIDC_ERR, "ion memory map failed - %d\n", rc);
goto mem_map_failed;
}
@@ -118,12 +119,12 @@ static void put_device_address(struct smem_client *smem_client,
clnt = smem_client->clnt;
if (!clnt) {
- dprintk(VIDC_WARN, "Invalid client");
+ dprintk(VIDC_WARN, "Invalid client\n");
return;
}
if (is_iommu_present(smem_client->res)) {
dprintk(VIDC_DBG,
- "Calling ion_unmap_iommu - domain: %d, parition: %d",
+ "Calling ion_unmap_iommu - domain: %d, parition: %d\n",
domain_num, partition_num);
ion_unmap_iommu(clnt, hndl, domain_num, partition_num);
}
@@ -270,7 +271,7 @@ static void free_ion_mem(struct smem_client *client, struct msm_smem *mem)
rc = msm_smem_get_domain_partition((void *)client, mem->flags,
mem->buffer_type, &domain, &partition);
if (rc) {
- dprintk(VIDC_ERR, "Failed to get domain, partition: %d", rc);
+ dprintk(VIDC_ERR, "Failed to get domain, partition: %d\n", rc);
return;
}
@@ -514,7 +515,7 @@ int msm_smem_get_domain_partition(void *clt, u32 flags, enum hal_buffer
bool is_secure = (flags & SMEM_SECURE);
struct iommu_info *iommu_map;
if (!domain_num || !partition_num) {
- dprintk(VIDC_DBG, "passed null to get domain partition!");
+ dprintk(VIDC_DBG, "passed null to get domain partition!\n");
return -EINVAL;
}
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
index 65164a8b0be5..a46095682a9f 100644
--- a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -35,7 +35,7 @@
struct msm_vidc_drv *vidc_driver;
-uint32_t msm_vidc_pwr_collapse_delay = INT_MAX;
+uint32_t msm_vidc_pwr_collapse_delay = 2000;
static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
{
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
index 7e91efabae17..2f8465996a55 100644
--- a/drivers/media/platform/msm/vidc/msm_vdec.c
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -21,7 +21,7 @@
#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
#define MIN_NUM_OUTPUT_BUFFERS 4
#define MAX_NUM_OUTPUT_BUFFERS VIDEO_MAX_FRAME
-#define DEFAULT_CONCEAL_COLOR 0x0
+#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8080
#define TZ_INFO_GET_FEATURE_VERSION_ID 0x3
#define TZ_DYNAMIC_BUFFER_FEATURE_ID 12
@@ -508,6 +508,16 @@ static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
.qmenu = mpeg_vidc_video_h264_mvc_layout,
.cluster = 0,
},
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR,
+ .name = "Picture concealed color",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 0x0,
+ .maximum = 0xffffff,
+ .default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK,
+ .step = 1,
+ .cluster = 0,
+ },
};
#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
@@ -531,7 +541,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDC_VIDEO_MVC_BUFFER_LAYOUT:
if (inst->fmts[OUTPUT_PORT]->fourcc != V4L2_PIX_FMT_H264_MVC) {
- dprintk(VIDC_ERR, "Control 0x%x only valid for MVC",
+ dprintk(VIDC_ERR, "Control 0x%x only valid for MVC\n",
ctrl->id);
rc = -ENOTSUPP;
break;
@@ -540,7 +550,8 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC &&
ctrl->val != V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) {
- dprintk(VIDC_ERR, "Profile 0x%x not supported for MVC",
+ dprintk(VIDC_ERR,
+ "Profile 0x%x not supported for MVC\n",
ctrl->val);
rc = -ENOTSUPP;
break;
@@ -549,7 +560,7 @@ static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
if (inst->fmts[OUTPUT_PORT]->fourcc == V4L2_PIX_FMT_H264_MVC &&
ctrl->val >= V4L2_MPEG_VIDEO_H264_LEVEL_5_2) {
- dprintk(VIDC_ERR, "Level 0x%x not supported for MVC",
+ dprintk(VIDC_ERR, "Level 0x%x not supported for MVC\n",
ctrl->val);
rc = -ENOTSUPP;
break;
@@ -717,7 +728,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -765,7 +776,7 @@ int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
+ "vidc_hal_session_set_buffers failed\n");
break;
default:
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
@@ -785,7 +796,7 @@ int msm_vdec_release_buf(struct msm_vidc_inst *inst,
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -844,7 +855,7 @@ int msm_vdec_release_buf(struct msm_vidc_inst *inst,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
- "vidc_hal_session_release_buffers failed");
+ "vidc_hal_session_release_buffers failed\n");
break;
default:
dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
@@ -1105,7 +1116,7 @@ int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
int max_input_size = 0;
if (!inst || !f) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
@@ -1312,7 +1323,7 @@ static int msm_vdec_queue_setup(struct vb2_queue *q,
inst = q->drv_priv;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -1470,7 +1481,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst)
HAL_VIDEO_DECODER_SECONDARY)
rc = msm_vidc_check_scaling_supported(inst);
if (rc) {
- dprintk(VIDC_ERR, "H/w scaling is not in valid range");
+ dprintk(VIDC_ERR, "H/w scaling is not in valid range\n");
return -EINVAL;
}
rc = msm_comm_set_scratch_buffers(inst);
@@ -1548,7 +1559,6 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct msm_vidc_inst *inst;
int rc = 0;
- int pdata = DEFAULT_CONCEAL_COLOR;
struct hfi_device *hdev;
if (!q || !q->drv_priv) {
dprintk(VIDC_ERR, "Invalid input, q = %p\n", q);
@@ -1556,7 +1566,7 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
}
inst = q->drv_priv;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1566,10 +1576,6 @@ static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming)
rc = start_streaming(inst);
- rc = call_hfi_op(hdev, session_set_property,
- (void *) inst->session,
- HAL_PARAM_VDEC_CONCEAL_COLOR,
- (void *) &pdata);
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming)
@@ -1634,12 +1640,24 @@ int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec)
struct msm_vidc_core *core = inst->core;
if (!dec || !inst || !inst->core) {
- dprintk(VIDC_ERR, "%s invalid params", __func__);
+ dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
switch (dec->cmd) {
case V4L2_DEC_QCOM_CMD_FLUSH:
+ if (core->state != VIDC_CORE_INVALID &&
+ inst->state == MSM_VIDC_CORE_INVALID) {
+ rc = msm_comm_recover_from_session_error(inst);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed to recover from session_error: %d\n",
+ rc);
+ }
rc = msm_comm_flush(inst, dec->flags);
+ if (rc) {
+ dprintk(VIDC_ERR,
+ "Failed to flush buffers: %d\n", rc);
+ }
break;
case V4L2_DEC_CMD_STOP:
if (core->state != VIDC_CORE_INVALID &&
@@ -1733,7 +1751,7 @@ static inline enum buffer_mode_type get_buf_type(int val)
case V4L2_MPEG_VIDC_VIDEO_DYNAMIC:
return HAL_BUFFER_MODE_DYNAMIC;
default:
- dprintk(VIDC_ERR, "%s: invalid buf type: %d", __func__, val);
+ dprintk(VIDC_ERR, "%s: invalid buf type: %d\n", __func__, val);
}
return 0;
}
@@ -1765,7 +1783,7 @@ static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
union hal_get_property hprop;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -1831,7 +1849,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct hal_mvc_buffer_layout layout;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1914,7 +1932,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
inst->flags |= VIDC_TURBO;
break;
default:
- dprintk(VIDC_ERR, "Perf mode %x not supported",
+ dprintk(VIDC_ERR, "Perf mode %x not supported\n",
ctrl->val);
rc = -ENOTSUPP;
break;
@@ -1963,7 +1981,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE:
if (ctrl->val && !(inst->capability.pixelprocess_capabilities &
HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) {
- dprintk(VIDC_ERR, "Downscaling not supported: 0x%x",
+ dprintk(VIDC_ERR, "Downscaling not supported: 0x%x\n",
ctrl->id);
rc = -ENOTSUPP;
break;
@@ -2038,6 +2056,11 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &layout;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR:
+ property_id = HAL_PARAM_VDEC_CONCEAL_COLOR;
+ property_val = ctrl->val;
+ pdata = &property_val;
+ break;
default:
break;
}
@@ -2059,7 +2082,7 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
struct msm_vidc_inst *inst = container_of(ctrl->handler,
struct msm_vidc_inst, ctrl_handler);
if (!inst) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
@@ -2073,7 +2096,7 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
if (ctrl->cluster[c]->is_new) {
rc = try_set_ctrl(inst, ctrl->cluster[c]);
if (rc) {
- dprintk(VIDC_ERR, "Failed setting %x",
+ dprintk(VIDC_ERR, "Failed setting %x\n",
ctrl->cluster[c]->id);
break;
}
@@ -2082,7 +2105,8 @@ static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
failed_open_done:
if (rc)
- dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
+ dprintk(VIDC_ERR, "Failed setting control: %x (%s)",
+ ctrl->id, v4l2_ctrl_get_name(ctrl->id));
return rc;
}
@@ -2103,7 +2127,7 @@ static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
if (master->cluster[c]->id == ctrl->id) {
rc = try_get_ctrl(inst, ctrl);
if (rc) {
- dprintk(VIDC_ERR, "Failed getting %x",
+ dprintk(VIDC_ERR, "Failed getting %x\n",
ctrl->id);
return rc;
}
@@ -2246,7 +2270,8 @@ int msm_vdec_ctrl_init(struct msm_vidc_inst *inst)
cluster = get_cluster(idx, &cluster_size);
if (!cluster || !cluster_size) {
- dprintk(VIDC_WARN, "Failed to setup cluster of type %d",
+ dprintk(VIDC_WARN,
+ "Failed to setup cluster of type %d\n",
idx);
continue;
}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
index 664cea0567a1..3c99756874e9 100644
--- a/drivers/media/platform/msm/vidc/msm_venc.c
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -478,6 +478,26 @@ static struct msm_vidc_ctrl msm_venc_ctrls[] = {
.cluster = MSM_VENC_CTRL_CLUSTER_QP,
},
{
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP,
+ .name = "VP8 Minimum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 128,
+ .default_value = 1,
+ .step = 1,
+ .cluster = MSM_VENC_CTRL_CLUSTER_QP,
+ },
+ {
+ .id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP,
+ .name = "VP8 Maximum QP",
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .minimum = 1,
+ .maximum = 128,
+ .default_value = 128,
+ .step = 1,
+ .cluster = MSM_VENC_CTRL_CLUSTER_QP,
+ },
+ {
.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
.name = "Slice Mode",
.type = V4L2_CTRL_TYPE_MENU,
@@ -885,7 +905,7 @@ static int msm_venc_queue_setup(struct vb2_queue *q,
inst = q->drv_priv;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -990,7 +1010,7 @@ static inline int start_streaming(struct msm_vidc_inst *inst)
HAL_VIDEO_ENCODER_SCALING_CAPABILITY)
rc = msm_vidc_check_scaling_supported(inst);
if (rc) {
- dprintk(VIDC_ERR, "H/w scaling is not in valid range");
+ dprintk(VIDC_ERR, "H/w scaling is not in valid range\n");
return -EINVAL;
}
rc = msm_comm_try_get_bufreqs(inst);
@@ -1323,7 +1343,7 @@ static inline int venc_v4l2_to_hal(int id, int value)
}
unknown_value:
- dprintk(VIDC_WARN, "Unknown control (%x, %d)", id, value);
+ dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value);
return -EINVAL;
}
@@ -1354,7 +1374,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct hal_mpeg4_time_resolution time_res;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1366,7 +1386,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
__ctrl_id, \
ctrl->cluster, ctrl->ncontrols); \
if (!__temp) { \
- dprintk(VIDC_ERR, "Can't find %s (%x) in cluster", \
+ dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \
#__ctrl_id, __ctrl_id); \
/* Clusters are hardcoded, if we can't find */ \
/* something then things are massively screwed up */ \
@@ -1393,7 +1413,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
inst->fmts[CAPTURE_PORT]->fourcc != V4L2_PIX_FMT_H264 &&
inst->fmts[CAPTURE_PORT]->fourcc !=
V4L2_PIX_FMT_H264_NO_SC) {
- dprintk(VIDC_ERR, "Control 0x%x only valid for H264",
+ dprintk(VIDC_ERR, "Control 0x%x only valid for H264\n",
ctrl->id);
rc = -ENOTSUPP;
break;
@@ -1438,7 +1458,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
(void *)inst->session, property_id, pdata);
if (rc) {
dprintk(VIDC_ERR,
- "Failed : Setprop MAX_NUM_B_FRAMES %d",
+ "Failed : Setprop MAX_NUM_B_FRAMES %d\n",
rc);
break;
}
@@ -1522,13 +1542,13 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
if (ctrl->val < avg_bitrate->val) {
dprintk(VIDC_ERR,
- "Peak bitrate (%d) is lower than average bitrate (%d)",
+ "Peak bitrate (%d) is lower than average bitrate (%d)\n",
ctrl->val, avg_bitrate->val);
rc = -EINVAL;
break;
} else if (ctrl->val < avg_bitrate->val * 2) {
dprintk(VIDC_WARN,
- "Peak bitrate (%d) ideally should be twice the average bitrate (%d)",
+ "Peak bitrate (%d) ideally should be twice the average bitrate (%d)\n",
ctrl->val, avg_bitrate->val);
}
@@ -1653,7 +1673,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct v4l2_ctrl *deinterlace = NULL;
if (!(inst->capability.pixelprocess_capabilities &
HAL_VIDEO_ENCODER_ROTATION_CAPABILITY)) {
- dprintk(VIDC_ERR, "Rotation not supported: 0x%x",
+ dprintk(VIDC_ERR, "Rotation not supported: 0x%x\n",
ctrl->id);
rc = -ENOTSUPP;
break;
@@ -1663,7 +1683,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
if (ctrl->val && deinterlace && deinterlace->val !=
V4L2_CID_MPEG_VIDC_VIDEO_DEINTERLACE_DISABLED) {
dprintk(VIDC_ERR,
- "Rotation not supported with deinterlacing");
+ "Rotation not supported with deinterlacing\n");
rc = -EINVAL;
break;
}
@@ -1729,7 +1749,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP);
if (ctrl->val >= qp_max->val) {
- dprintk(VIDC_ERR, "Bad range: Min QP (%d) > Max QP(%d)",
+ dprintk(VIDC_ERR,
+ "Bad range: Min QP (%d) > Max QP(%d)\n",
ctrl->val, qp_max->val);
rc = -ERANGE;
break;
@@ -1748,7 +1769,8 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP);
if (ctrl->val <= qp_min->val) {
- dprintk(VIDC_ERR, "Bad range: Max QP (%d) < Min QP(%d)",
+ dprintk(VIDC_ERR,
+ "Bad range: Max QP (%d) < Min QP(%d)\n",
ctrl->val, qp_min->val);
rc = -ERANGE;
break;
@@ -1762,6 +1784,26 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
pdata = &qp_range;
break;
}
+ case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP: {
+ struct v4l2_ctrl *qp_max;
+ qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP);
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = qp_max->val;
+ qp_range.min_qp = ctrl->val;
+ pdata = &qp_range;
+ break;
+ }
+ case V4L2_CID_MPEG_VIDC_VIDEO_VP8_MAX_QP: {
+ struct v4l2_ctrl *qp_min;
+ qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_VP8_MIN_QP);
+ property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+ qp_range.layer_id = 0;
+ qp_range.max_qp = ctrl->val;
+ qp_range.min_qp = qp_min->val;
+ pdata = &qp_range;
+ break;
+ }
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: {
int temp = 0;
@@ -1816,7 +1858,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
} else {
dprintk(VIDC_WARN,
"Failed : slice delivery mode is valid "\
- "only for H264 encoder and MB based slicing");
+ "only for H264 encoder and MB based slicing\n");
enable.enable = false;
}
pdata = &enable;
@@ -2011,7 +2053,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
inst->flags |= VIDC_TURBO;
break;
default:
- dprintk(VIDC_ERR, "Perf mode %x not supported",
+ dprintk(VIDC_ERR, "Perf mode %x not supported\n",
ctrl->val);
rc = -ENOTSUPP;
break;
@@ -2039,7 +2081,7 @@ static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
struct v4l2_ctrl *rotation = NULL;
if (!(inst->capability.pixelprocess_capabilities &
HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY)) {
- dprintk(VIDC_ERR, "Deinterlace not supported: 0x%x",
+ dprintk(VIDC_ERR, "Deinterlace not supported: 0x%x\n",
ctrl->id);
rc = -ENOTSUPP;
break;
@@ -2094,7 +2136,7 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
struct msm_vidc_inst, ctrl_handler);
if (!inst) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2112,7 +2154,7 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
rc = try_set_ctrl(inst, temp);
if (rc) {
- dprintk(VIDC_ERR, "Failed setting %s (%x)",
+ dprintk(VIDC_ERR, "Failed setting %s (%x)\n",
v4l2_ctrl_get_name(temp->id),
temp->id);
break;
@@ -2121,7 +2163,8 @@ static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
}
failed_open_done:
if (rc)
- dprintk(VIDC_ERR, "Failed to set hal property\n");
+ dprintk(VIDC_ERR, "Failed setting control: %x (%s)",
+ ctrl->id, v4l2_ctrl_get_name(ctrl->id));
return rc;
}
@@ -2264,7 +2307,7 @@ int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2337,7 +2380,7 @@ int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
}
if (!inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -2570,7 +2613,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
int extra_idx = 0;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2614,7 +2657,7 @@ int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
+ "vidc_hal_session_set_buffers failed\n");
break;
default:
dprintk(VIDC_ERR,
@@ -2632,7 +2675,7 @@ int msm_venc_release_buf(struct msm_vidc_inst *inst,
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2854,7 +2897,8 @@ int msm_venc_ctrl_init(struct msm_vidc_inst *inst)
cluster = get_cluster(idx, &cluster_size);
if (!cluster || !cluster_size) {
- dprintk(VIDC_WARN, "Failed to setup cluster of type %d",
+ dprintk(VIDC_WARN,
+ "Failed to setup cluster of type %d\n",
idx);
continue;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
index f99c3b980bcc..b85b0c729e37 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -686,7 +686,8 @@ int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
return -EINVAL;
}
} else
- dprintk(VIDC_ERR, "%s: WARN: NULL handle", __func__);
+ dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n",
+ __func__, i);
}
return 0;
}
@@ -732,6 +733,8 @@ int msm_vidc_release_buffers(void *instance, int buffer_type)
return -EINVAL;
list_for_each_safe(ptr, next, &inst->registered_bufs) {
+ bool release_buf = false;
+ mutex_lock(&inst->lock);
bi = list_entry(ptr, struct buffer_info, list);
if (bi->type == buffer_type) {
buffer_info.type = bi->type;
@@ -749,19 +752,28 @@ int msm_vidc_release_buffers(void *instance, int buffer_type)
buffer_info.m.planes[i].length);
}
buffer_info.length = bi->num_planes;
- if (inst->session_type == MSM_VIDC_DECODER)
- rc = msm_vdec_release_buf(instance,
- &buffer_info);
- if (inst->session_type == MSM_VIDC_ENCODER)
- rc = msm_venc_release_buf(instance,
- &buffer_info);
- if (rc)
- dprintk(VIDC_ERR,
- "Failed Release buffer: %d, %d, %d\n",
- buffer_info.m.planes[0].reserved[0],
- buffer_info.m.planes[0].reserved[1],
- buffer_info.m.planes[0].length);
-
+ release_buf = true;
+ }
+ mutex_unlock(&inst->lock);
+ if (!release_buf)
+ continue;
+ if (inst->session_type == MSM_VIDC_DECODER)
+ rc = msm_vdec_release_buf(instance,
+ &buffer_info);
+ if (inst->session_type == MSM_VIDC_ENCODER)
+ rc = msm_venc_release_buf(instance,
+ &buffer_info);
+ if (rc)
+ dprintk(VIDC_ERR,
+ "Failed Release buffer: %d, %d, %d\n",
+ buffer_info.m.planes[0].reserved[0],
+ buffer_info.m.planes[0].reserved[1],
+ buffer_info.m.planes[0].length);
+ }
+ mutex_lock(&inst->lock);
+ list_for_each_safe(ptr, next, &inst->registered_bufs) {
+ bi = list_entry(ptr, struct buffer_info, list);
+ if (bi->type == buffer_type) {
list_del(&bi->list);
for (i = 0; i < bi->num_planes; i++) {
if (bi->handle[i] && bi->mapped[i]) {
@@ -777,6 +789,7 @@ int msm_vidc_release_buffers(void *instance, int buffer_type)
kfree(bi);
}
}
+ mutex_unlock(&inst->lock);
return rc;
}
EXPORT_SYMBOL(msm_vidc_release_buffers);
@@ -1183,7 +1196,7 @@ void *msm_vidc_open(int core_id, int session_type)
}
pr_info(VIDC_DBG_TAG "Opening video instance: %p, %d\n",
- VIDC_INFO, inst, session_type);
+ VIDC_MSG_PRIO2STRING(VIDC_INFO), inst, session_type);
mutex_init(&inst->sync_lock);
mutex_init(&inst->bufq[CAPTURE_PORT].lock);
mutex_init(&inst->bufq[OUTPUT_PORT].lock);
@@ -1379,7 +1392,8 @@ int msm_vidc_close(void *instance)
dprintk(VIDC_ERR,
"Failed to move video instance to uninit state\n");
- pr_info(VIDC_DBG_TAG "Closed video instance: %p\n", VIDC_INFO, inst);
+ pr_info(VIDC_DBG_TAG "Closed video instance: %p\n",
+ VIDC_MSG_PRIO2STRING(VIDC_INFO), inst);
kfree(inst);
return 0;
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
index 7c39a1f600db..5101a0c463ab 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_common.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -173,7 +173,7 @@ static void msm_comm_unvote_buses(struct msm_vidc_core *core,
struct hfi_device *hdev;
if (!core || !core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return;
}
hdev = core->device;
@@ -317,7 +317,7 @@ static void handle_session_release_buf_done(enum command_response cmd,
list_for_each_safe(ptr, next, &inst->internalbufs) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == buf->handle->device_addr) {
- dprintk(VIDC_DBG, "releasing scratch: 0x%x",
+ dprintk(VIDC_DBG, "releasing scratch: 0x%x\n",
(u32) buf->handle->device_addr);
buf_found = true;
}
@@ -326,14 +326,14 @@ static void handle_session_release_buf_done(enum command_response cmd,
list_for_each_safe(ptr, next, &inst->persistbufs) {
buf = list_entry(ptr, struct internal_buf, list);
if (address == (u32) buf->handle->device_addr) {
- dprintk(VIDC_DBG, "releasing persist: 0x%x",
+ dprintk(VIDC_DBG, "releasing persist: 0x%x\n",
(u32) buf->handle->device_addr);
buf_found = true;
}
}
if (!buf_found)
- dprintk(VIDC_ERR, "invalid buffer received from firmware");
+ dprintk(VIDC_ERR, "invalid buffer received from firmware\n");
complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
}
@@ -359,13 +359,13 @@ static void change_inst_state(struct msm_vidc_inst *inst,
enum instance_state state)
{
if (!inst) {
- dprintk(VIDC_ERR, "Invalid parameter %s", __func__);
+ dprintk(VIDC_ERR, "Invalid parameter %s\n", __func__);
return;
}
mutex_lock(&inst->lock);
if (inst->state == MSM_VIDC_CORE_INVALID) {
dprintk(VIDC_DBG,
- "Inst: %p is in bad state can't change state",
+ "Inst: %p is in bad state can't change state\n",
inst);
goto exit;
}
@@ -395,7 +395,8 @@ static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
&inst->completions[SESSION_MSG_INDEX(cmd)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
+ dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
+ SESSION_MSG_INDEX(cmd));
msm_comm_recover_from_session_error(inst);
rc = -EIO;
} else {
@@ -433,7 +434,7 @@ void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
{
if (!inst) {
- dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
mutex_lock(&inst->lock);
@@ -455,7 +456,7 @@ static void handle_session_init_done(enum command_response cmd, void *data)
response->data;
inst = (struct msm_vidc_inst *)response->session_id;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return;
}
@@ -475,7 +476,7 @@ static void handle_session_init_done(enum command_response cmd, void *data)
session_init_done->alloc_mode_out;
} else {
dprintk(VIDC_ERR,
- "Session init response from FW : 0x%x",
+ "Session init response from FW : 0x%x\n",
response->status);
msm_comm_generate_session_error(inst);
}
@@ -524,6 +525,14 @@ static void handle_event_change(enum command_response cmd, void *data)
event_notify->packet_buffer,
event_notify->exra_data_buffer);
+ if (inst->state == MSM_VIDC_CORE_INVALID ||
+ inst->core->state ==
+ VIDC_CORE_INVALID) {
+ dprintk(VIDC_DBG,
+ "Event release buf ref received in invalid state - discard\n");
+ return;
+ }
+
/*
* Get the buffer_info entry for the
* device address.
@@ -635,7 +644,7 @@ static void handle_load_resource_done(enum command_response cmd, void *data)
inst = (struct msm_vidc_inst *)response->session_id;
if (response->status) {
dprintk(VIDC_ERR,
- "Load resource response from FW : 0x%x",
+ "Load resource response from FW : 0x%x\n",
response->status);
msm_comm_generate_session_error(inst);
}
@@ -710,7 +719,7 @@ void validate_output_buffers(struct msm_vidc_inst *inst)
}
if (buffers_owned_by_driver != output_buf->buffer_count_actual)
dprintk(VIDC_ERR,
- "OUTPUT Buffer count mismatch %d of %d",
+ "OUTPUT Buffer count mismatch %d of %d\n",
buffers_owned_by_driver,
output_buf->buffer_count_actual);
@@ -779,12 +788,12 @@ static void handle_sys_error(enum command_response cmd, void *data)
hdev = inst->core->device;
if (hdev && inst->session) {
dprintk(VIDC_DBG,
- "cleaning up inst: 0x%p", inst);
+ "cleaning up inst: 0x%p\n", inst);
rc = call_hfi_op(hdev, session_clean,
(void *) inst->session);
if (rc)
dprintk(VIDC_ERR,
- "Sess clean failed :%p",
+ "Sess clean failed :%p\n",
inst);
}
inst->session = NULL;
@@ -795,7 +804,7 @@ static void handle_sys_error(enum command_response cmd, void *data)
mutex_unlock(&core->sync_lock);
} else {
dprintk(VIDC_ERR,
- "Got SYS_ERR but unable to identify core");
+ "Got SYS_ERR but unable to identify core\n");
}
} else {
dprintk(VIDC_ERR,
@@ -834,7 +843,7 @@ static void handle_sys_watchdog_timeout(enum command_response cmd, void *data)
(void *) inst->session);
if (rc)
dprintk(VIDC_ERR,
- "Sess clean failed :%p",
+ "Sess clean failed :%p\n",
inst);
}
@@ -860,7 +869,7 @@ static void handle_session_close(enum command_response cmd, void *data)
hdev = inst->core->device;
mutex_lock(&inst->lock);
if (inst->session) {
- dprintk(VIDC_DBG, "cleaning up inst: 0x%p", inst);
+ dprintk(VIDC_DBG, "cleaning up inst: 0x%p\n", inst);
call_hfi_op(hdev, session_clean,
(void *) inst->session);
}
@@ -1057,7 +1066,7 @@ static void handle_dynamic_buffer(struct msm_vidc_inst *inst,
}
if (flags & HAL_BUFFERFLAG_READONLY) {
dprintk(VIDC_DBG,
- "_F_B_D_ fd[0] = %d -> Reference with f/w, addr: 0x%x",
+ "FBD fd[0] = %d -> Reference with f/w, addr: 0x%x\n",
binfo->fd[0], device_addr);
} else {
dprintk(VIDC_DBG,
@@ -1293,7 +1302,7 @@ static void handle_seq_hdr_done(enum command_response cmd, void *data)
(u32)fill_buf_done->packet_buffer1);
if (!vb) {
dprintk(VIDC_ERR,
- "Failed to find video buffer for seq_hdr_done");
+ "Failed to find video buffer for seq_hdr_done\n");
return;
}
@@ -1444,7 +1453,7 @@ static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
struct hfi_device *hdev;
if (!core || !core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = core->device;
@@ -1467,7 +1476,8 @@ static int msm_comm_unset_ocmem(struct msm_vidc_core *core)
&core->completions[SYS_MSG_INDEX(RELEASE_RESOURCE_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
+ dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
+ SYS_MSG_INDEX(RELEASE_RESOURCE_DONE));
rc = -EIO;
}
release_ocmem_failed:
@@ -1489,7 +1499,8 @@ static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
&core->completions[SYS_MSG_INDEX(SYS_INIT_DONE)],
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
+ dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n",
+ SYS_MSG_INDEX(SYS_INIT_DONE));
rc = -EIO;
goto exit;
} else {
@@ -1569,7 +1580,7 @@ static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -1638,7 +1649,7 @@ static enum hal_domain get_hal_domain(int session_type)
enum hal_video_codec get_hal_codec_type(int fourcc)
{
enum hal_video_codec codec;
- dprintk(VIDC_DBG, "codec is 0x%x", fourcc);
+ dprintk(VIDC_DBG, "codec is 0x%x\n", fourcc);
switch (fourcc) {
case V4L2_PIX_FMT_H264:
case V4L2_PIX_FMT_H264_NO_SC:
@@ -1693,7 +1704,7 @@ static int msm_comm_session_init(int flipped_state,
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1759,13 +1770,13 @@ static int msm_vidc_load_resources(int flipped_state,
int height, width;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
- "Core is in bad state can't do load res");
+ "Core is in bad state can't do load res\n");
return -EINVAL;
}
@@ -1831,13 +1842,13 @@ static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
if (inst->state == MSM_VIDC_CORE_INVALID ||
inst->core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
- "Core is in bad state can't do start");
+ "Core is in bad state can't do start\n");
return -EINVAL;
}
@@ -1868,7 +1879,7 @@ static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1898,7 +1909,7 @@ static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -1931,7 +1942,7 @@ static int msm_comm_session_close(int flipped_state,
struct hfi_device *hdev;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid params", __func__);
+ dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -2063,16 +2074,16 @@ static int set_output_buffers(struct msm_vidc_inst *inst,
buffer_info.extradata_addr = handle->device_addr +
output_buf->buffer_size;
buffer_info.extradata_size = extradata_buf->buffer_size;
- dprintk(VIDC_DBG, "Output buffer address: %x",
+ dprintk(VIDC_DBG, "Output buffer address: %x\n",
buffer_info.align_device_addr);
- dprintk(VIDC_DBG, "Output extradata address: %x",
+ dprintk(VIDC_DBG, "Output extradata address: %x\n",
buffer_info.extradata_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
mutex_unlock(&inst->lock);
if (rc) {
dprintk(VIDC_ERR,
- "%s : session_set_buffers failed",
+ "%s : session_set_buffers failed\n",
__func__);
goto fail_set_buffers;
}
@@ -2149,13 +2160,13 @@ static int set_scratch_buffers(struct msm_vidc_inst *inst,
binfo->buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Scratch buffer address: %x",
+ dprintk(VIDC_DBG, "Scratch buffer address: %x\n",
buffer_info.align_device_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
+ "vidc_hal_session_set_buffers failed\n");
goto fail_set_buffers;
}
mutex_lock(&inst->lock);
@@ -2236,13 +2247,13 @@ static int set_persist_buffers(struct msm_vidc_inst *inst,
binfo->buffer_type = buffer_type;
buffer_info.num_buffers = 1;
buffer_info.align_device_addr = handle->device_addr;
- dprintk(VIDC_DBG, "Persist buffer address: %x",
+ dprintk(VIDC_DBG, "Persist buffer address: %x\n",
buffer_info.align_device_addr);
rc = call_hfi_op(hdev, session_set_buffers,
(void *) inst->session, &buffer_info);
if (rc) {
dprintk(VIDC_ERR,
- "vidc_hal_session_set_buffers failed");
+ "vidc_hal_session_set_buffers failed\n");
goto fail_set_buffers;
}
mutex_lock(&inst->lock);
@@ -2282,7 +2293,7 @@ int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
- "Core is in bad state can't change the state");
+ "Core is in bad state can't change the state\n");
rc = -EINVAL;
goto exit;
}
@@ -2396,7 +2407,7 @@ int msm_comm_qbuf(struct vb2_buffer *vb)
}
hdev = core->device;
if (!hdev) {
- dprintk(VIDC_ERR, "Invalid input: %p", hdev);
+ dprintk(VIDC_ERR, "Invalid input: %p\n", hdev);
return -EINVAL;
}
@@ -2448,7 +2459,13 @@ int msm_comm_qbuf(struct vb2_buffer *vb)
dprintk(VIDC_DBG,
"Received EOS on output capability\n");
}
-
+ if (vb->v4l2_buf.flags &
+ V4L2_MSM_BUF_FLAG_YUV_601_709_CLAMP) {
+ frame_data.flags |=
+ HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP;
+ dprintk(VIDC_DBG,
+ "Received buff with 601to709 clamp\n");
+ }
if (vb->v4l2_buf.flags &
V4L2_QCOM_BUF_FLAG_CODECCONFIG) {
frame_data.flags |= HAL_BUFFERFLAG_CODECCONFIG;
@@ -2564,7 +2581,7 @@ int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
HAL_PARAM_GET_BUFFER_REQUIREMENTS,
&hprop);
if (rc) {
- dprintk(VIDC_ERR, "%s Error rc:%d", __func__, rc);
+ dprintk(VIDC_ERR, "%s Error rc:%d\n", __func__, rc);
return rc;
}
buf_req = hprop.buf_req;
@@ -2591,7 +2608,7 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype,
struct getprop_buf *buf;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2641,7 +2658,8 @@ int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype,
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR,
- "Wait interrupted or timeout: %d\n", rc);
+ "Wait interrupted or timeout: %d\n",
+ SESSION_MSG_INDEX(SESSION_PROPERTY_INFO));
inst->state = MSM_VIDC_CORE_INVALID;
msm_comm_recover_from_session_error(inst);
rc = -EIO;
@@ -2707,7 +2725,7 @@ int msm_comm_release_output_buffers(struct msm_vidc_inst *inst)
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
- "Rel output buf fail:0x%x, %d",
+ "Rel output buf fail:0x%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
}
@@ -2767,7 +2785,7 @@ int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst)
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
- "Rel scrtch buf fail:0x%x, %d",
+ "Rel scrtch buf fail:0x%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
mutex_unlock(&inst->lock);
@@ -2838,7 +2856,7 @@ int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst)
(void *)inst->session, &buffer_info);
if (rc)
dprintk(VIDC_WARN,
- "Rel prst buf fail:0x%x, %d",
+ "Rel prst buf fail:0x%x, %d\n",
buffer_info.align_device_addr,
buffer_info.buffer_size);
mutex_unlock(&inst->lock);
@@ -2875,7 +2893,7 @@ int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
}
if (!inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
hdev = inst->core->device;
@@ -2899,7 +2917,7 @@ int msm_comm_set_output_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2919,7 +2937,7 @@ int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -2948,7 +2966,7 @@ int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
{
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
@@ -3096,7 +3114,7 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
}
hdev = core->device;
if (!hdev) {
- dprintk(VIDC_ERR, "Invalid device pointer = %p", hdev);
+ dprintk(VIDC_ERR, "Invalid device pointer = %p\n", hdev);
return -EINVAL;
}
@@ -3107,6 +3125,9 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
dprintk(VIDC_INFO, "Input only flush not supported\n");
return 0;
}
+ mutex_lock(&inst->sync_lock);
+ msm_comm_flush_dynamic_buffers(inst);
+ mutex_unlock(&inst->sync_lock);
if (inst->state == MSM_VIDC_CORE_INVALID ||
core->state == VIDC_CORE_INVALID) {
dprintk(VIDC_ERR,
@@ -3117,7 +3138,6 @@ int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
}
mutex_lock(&inst->sync_lock);
- msm_comm_flush_dynamic_buffers(inst);
if (inst->in_reconfig && !ip_flush && op_flush) {
if (!list_empty(&inst->pendingq)) {
/*Execution can never reach here since port reconfig
@@ -3339,7 +3359,7 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
if (!inst->capability.scale_x.min || !inst->capability.scale_x.max ||
!inst->capability.scale_y.min ||
!inst->capability.scale_y.max) {
- dprintk(VIDC_ERR, "%s : Invalid scaling ratios",
+ dprintk(VIDC_ERR, "%s : Invalid scaling ratios\n",
__func__);
return -ENOTSUPP;
}
@@ -3355,8 +3375,8 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
if (!input_height || !input_width || !output_height || !output_width) {
dprintk(VIDC_ERR,
- "Invalid : Input Height = %d Width = %d"
- " Output Height = %d Width = %d",
+ "Invalid : Input height = %d width = %d"
+ " output height = %d width = %d\n",
input_height, input_width, output_height,
output_width);
return -ENOTSUPP;
@@ -3365,14 +3385,14 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
if (input_height > output_height) {
if (input_height/output_height > x_min) {
dprintk(VIDC_ERR,
- "Unsupported Height Downscale ratio %d Vs %d",
+ "Unsupported height downscale ratio %d vs %d\n",
input_height/output_height, x_min);
return -ENOTSUPP;
}
} else {
if (input_height/output_height > x_max) {
dprintk(VIDC_ERR,
- "Unsupported Height Upscale ratio %d Vs %d",
+ "Unsupported height upscale ratio %d vs %d\n",
input_height/output_height, x_max);
return -ENOTSUPP;
}
@@ -3380,14 +3400,14 @@ int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
if (input_width > output_width) {
if (input_width/output_width > y_min) {
dprintk(VIDC_ERR,
- "Unsupported Width Downscale ratio %d Vs %d",
+ "Unsupported width downscale ratio %d vs %d\n",
input_width/output_width, y_min);
return -ENOTSUPP;
}
} else {
if (input_width/output_width > y_max) {
dprintk(VIDC_ERR,
- "Unsupported Width Upscale ratio %d Vs %d",
+ "Unsupported width upscale ratio %d vs %d\n",
input_width/output_width, y_max);
return -ENOTSUPP;
}
@@ -3418,7 +3438,7 @@ int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
* inst->prop.width[CAPTURE_PORT] >
capability->width.max * capability->height.max)) {
dprintk(VIDC_ERR,
- "Unsupported WxH = (%u)x(%u), Max supported is - (%u)x(%u)",
+ "Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n",
inst->prop.width[CAPTURE_PORT],
inst->prop.height[CAPTURE_PORT],
capability->width.max, capability->height.max);
@@ -3441,7 +3461,7 @@ static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
enum command_response cmd = SYS_ERROR;
struct msm_vidc_cb_cmd_done response = {0};
if (!inst || !inst->core) {
- dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return;
}
core = inst->core;
@@ -3455,12 +3475,12 @@ int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst)
int rc = 0;
if (!inst || !inst->core || !inst->core->device) {
- dprintk(VIDC_ERR, "%s: invalid input parameters", __func__);
+ dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
return -EINVAL;
}
if (!inst->session || inst->state < MSM_VIDC_OPEN_DONE) {
dprintk(VIDC_WARN,
- "No corresponding FW session. No need to send Abort");
+ "No corresponding FW session. No need to send Abort\n");
return rc;
}
hdev = inst->core->device;
@@ -3481,7 +3501,7 @@ int msm_comm_recover_from_session_error(struct msm_vidc_inst *inst)
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
dprintk(VIDC_ERR, "%s: Wait interrupted or timeout: %d\n",
- __func__, rc);
+ __func__, SESSION_MSG_INDEX(SESSION_ABORT_DONE));
msm_comm_generate_sys_error(inst);
} else
change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
index 575111340261..8c4d30aa04b1 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -20,7 +20,6 @@ int msm_vidc_debug_out = VIDC_OUT_PRINTK;
int msm_fw_debug = 0x18;
int msm_fw_debug_mode = 0x1;
int msm_fw_low_power_mode = 0x1;
-int msm_vp8_low_tier = 0x1;
int msm_vidc_hw_rsp_timeout = 1000;
struct debug_buffer {
@@ -155,11 +154,6 @@ struct dentry *msm_vidc_debugfs_init_drv(void)
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
goto failed_create_dir;
}
- if (!debugfs_create_u32("vp8_low_tier", S_IRUGO | S_IWUSR,
- dir, &msm_vp8_low_tier)) {
- dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
- goto failed_create_dir;
- }
if (!debugfs_create_u32("debug_output", S_IRUGO | S_IWUSR,
dir, &msm_vidc_debug_out)) {
dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
index de55166ca41c..74139c78ccfc 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_debug.h
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -17,7 +17,7 @@
#include <linux/delay.h>
#include "msm_vidc_internal.h"
-#define VIDC_DBG_TAG "msm_vidc: %d: "
+#define VIDC_DBG_TAG "msm_vidc: %4s: "
/* To enable messages OR these values and
* echo the result to debugfs file.
@@ -52,23 +52,58 @@ extern int msm_vidc_debug_out;
extern int msm_fw_debug;
extern int msm_fw_debug_mode;
extern int msm_fw_low_power_mode;
-extern int msm_vp8_low_tier;
extern int msm_vidc_hw_rsp_timeout;
+#define VIDC_MSG_PRIO2STRING(__level) ({ \
+ char *__str; \
+ \
+ switch (__level) { \
+ case VIDC_ERR: \
+ __str = "err"; \
+ break; \
+ case VIDC_WARN: \
+ __str = "warn"; \
+ break; \
+ case VIDC_INFO: \
+ __str = "info"; \
+ break; \
+ case VIDC_DBG: \
+ __str = "dbg"; \
+ break; \
+ case VIDC_PROF: \
+ __str = "prof"; \
+ break; \
+ case VIDC_PKT: \
+ __str = "pkt"; \
+ break; \
+ case VIDC_FW: \
+ __str = "fw"; \
+ break; \
+ default: \
+ __str = "????"; \
+ break; \
+ } \
+ \
+ __str; \
+ })
+
#define dprintk(__level, __fmt, arg...) \
do { \
if (msm_vidc_debug & __level) { \
if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \
- printk(KERN_DEBUG VIDC_DBG_TAG \
- __fmt, __level, ## arg); \
+ pr_info(VIDC_DBG_TAG __fmt, \
+ VIDC_MSG_PRIO2STRING(__level), \
+ ## arg); \
} else if (msm_vidc_debug_out == VIDC_OUT_FTRACE) { \
- trace_printk(KERN_DEBUG VIDC_DBG_TAG \
- __fmt, __level, ## arg); \
+ trace_printk(KERN_DEBUG VIDC_DBG_TAG __fmt, \
+ VIDC_MSG_PRIO2STRING(__level), \
+ ## arg); \
} \
} \
} while (0)
+
struct dentry *msm_vidc_debugfs_init_drv(void);
struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
struct dentry *parent);
@@ -116,7 +151,7 @@ static inline void show_stats(struct msm_vidc_inst *i)
i->debug.pdata[x].name,
i->debug.pdata[x].cumulative /
i->debug.samples);
- dprintk(VIDC_PROF, "%s Samples: %d",
+ dprintk(VIDC_PROF, "%s Samples: %d\n",
i->debug.pdata[x].name,
i->debug.samples);
}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
index 2eea58d10dd6..6687628b7d5b 100644
--- a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -494,7 +494,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)
domain_idx,
iommu_map->name);
- if (!of_get_property(ctx_node, "qti,virtual-addr-pool",
+ if (!of_get_property(ctx_node, "qcom,virtual-addr-pool",
&array_size)) {
dprintk(VIDC_ERR,
"Could not find any addr pool for group : %s\n",
@@ -511,7 +511,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)
domain_idx);
rc = of_property_read_u32_array(ctx_node,
- "qti,virtual-addr-pool",
+ "qcom,virtual-addr-pool",
(u32 *)iommu_map->addr_range,
iommu_map->npartitions * 2);
if (rc) {
@@ -523,7 +523,7 @@ static int msm_vidc_load_iommu_groups(struct msm_vidc_platform_resources *res)
}
iommu_map->is_secure =
- of_property_read_bool(ctx_node, "qti,secure-domain");
+ of_property_read_bool(ctx_node, "qcom,secure-domain");
dprintk(VIDC_DBG,
"domain %s : secure = %d\n",
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.c b/drivers/media/platform/msm/vidc/q6_hfi.c
index e53e48ca53f2..7f029c696748 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.c
+++ b/drivers/media/platform/msm/vidc/q6_hfi.c
@@ -14,7 +14,7 @@
#include <linux/slab.h>
#include <linux/iommu.h>
#include <linux/msm_iommu_domains.h>
-#include <mach/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr.h>
#include <mach/subsystem_restart.h>
#include "hfi_packetization.h"
#include "msm_vidc_debug.h"
@@ -33,7 +33,7 @@ static int write_queue(void *info, u8 *packet)
u32 *write_ptr;
if (!info || !packet) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
@@ -42,7 +42,7 @@ static int write_queue(void *info, u8 *packet)
packet_size_in_words = (*(u32 *)packet) >> 2;
if (packet_size_in_words == 0) {
- dprintk(VIDC_ERR, "Zero packet size");
+ dprintk(VIDC_ERR, "Zero packet size\n");
return -ENODATA;
}
@@ -52,7 +52,7 @@ static int write_queue(void *info, u8 *packet)
(qinfo->q_size - (qinfo->write_idx - read_idx)) :
(read_idx - qinfo->write_idx);
if (empty_space <= packet_size_in_words) {
- dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)",
+ dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n",
empty_space, packet_size_in_words);
return -ENOTEMPTY;
}
@@ -80,7 +80,7 @@ static int read_queue(void *info, u8 *packet)
struct q6_iface_q_info *qinfo;
if (!info || !packet) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
@@ -92,7 +92,7 @@ static int read_queue(void *info, u8 *packet)
read_ptr = (u32 *)(qinfo->buffer + (qinfo->read_idx << 2));
packet_size_in_words = (*read_ptr) >> 2;
if (packet_size_in_words == 0) {
- dprintk(VIDC_ERR, "Zero packet size");
+ dprintk(VIDC_ERR, "Zero packet size\n");
return -ENODATA;
}
@@ -121,13 +121,13 @@ static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt)
unsigned long flags = 0;
if (!device || !pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
q_info = &device->event_queue;
if (!q_info->buffer) {
- dprintk(VIDC_ERR, "cannot write to shared Q");
+ dprintk(VIDC_ERR, "cannot write to shared Q\n");
rc = -ENODATA;
goto err_q_write;
}
@@ -135,7 +135,7 @@ static int q6_hfi_iface_eventq_write(struct q6_hfi_device *device, void *pkt)
spin_lock_irqsave(&q_info->lock, flags);
rc = write_queue(q_info, (u8 *)pkt);
if (rc)
- dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full");
+ dprintk(VIDC_ERR, "q6_hfi_iface_eventq_write: queue_full\n");
spin_unlock_irqrestore(&q_info->lock, flags);
err_q_write:
@@ -149,14 +149,14 @@ static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt)
unsigned long flags = 0;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
q_info = &device->event_queue;
if (!q_info->buffer) {
- dprintk(VIDC_ERR, "cannot read from shared Q");
+ dprintk(VIDC_ERR, "cannot read from shared Q\n");
rc = -ENODATA;
goto read_error;
}
@@ -164,7 +164,7 @@ static int q6_hfi_iface_eventq_read(struct q6_hfi_device *device, void *pkt)
spin_lock_irqsave(&q_info->lock, flags);
rc = read_queue(q_info, (u8 *)pkt);
if (rc) {
- dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty");
+ dprintk(VIDC_INFO, "q6_hfi_iface_eventq_read:queue_empty\n");
rc = -ENODATA;
}
spin_unlock_irqrestore(&q_info->lock, flags);
@@ -191,7 +191,7 @@ static void q6_hfi_core_work_handler(struct work_struct *work)
} while (!rc);
if (rc != -ENODATA)
- dprintk(VIDC_ERR, "Failed to read from event queue");
+ dprintk(VIDC_ERR, "Failed to read from event queue\n");
}
static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device)
@@ -202,7 +202,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device)
struct iommu_info *iommu_map;
if (!device || !device->res) {
- dprintk(VIDC_ERR, "Invalid parameter: %p", device);
+ dprintk(VIDC_ERR, "Invalid parameter: %p\n", device);
return -EINVAL;
}
@@ -220,7 +220,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device)
domain = iommu_group_get_iommudata(iommu_map->group);
if (IS_ERR_OR_NULL(domain)) {
dprintk(VIDC_ERR,
- "Failed to get domain data for group %p",
+ "Failed to get domain data for group %p\n",
iommu_map->group);
rc = -EINVAL;
goto fail_group;
@@ -228,7 +228,7 @@ static int q6_hfi_register_iommu_domains(struct q6_hfi_device *device)
iommu_map->domain = msm_find_domain_no(domain);
if (iommu_map->domain < 0) {
dprintk(VIDC_ERR,
- "Failed to get domain index for domain %p",
+ "Failed to get domain index for domain %p\n",
domain);
rc = -EINVAL;
goto fail_group;
@@ -254,7 +254,7 @@ static void q6_hfi_deregister_iommu_domains(struct q6_hfi_device *device)
int i = 0;
if (!device || !device->res) {
- dprintk(VIDC_ERR, "Invalid parameter: %p", device);
+ dprintk(VIDC_ERR, "Invalid parameter: %p\n", device);
return;
}
@@ -274,14 +274,18 @@ static int q6_hfi_init_resources(struct q6_hfi_device *device,
int rc = 0;
if (!device || !res) {
- dprintk(VIDC_ERR, "Invalid device or resources");
+ dprintk(VIDC_ERR, "Invalid device or resources\n");
return -EINVAL;
}
device->res = res;
rc = q6_hfi_register_iommu_domains(device);
- if (rc)
- dprintk(VIDC_ERR, "Failed to register iommu domains: %d\n", rc);
+ if (rc) {
+ if (rc != -EPROBE_DEFER) {
+ dprintk(VIDC_ERR,
+ "Failed to register iommu domains: %d\n", rc);
+ }
+ }
return rc;
}
@@ -297,14 +301,14 @@ static void *q6_hfi_add_device(u32 device_id,
struct q6_hfi_device *hdevice = NULL;
if (!callback) {
- dprintk(VIDC_ERR, "Invalid Paramters");
+ dprintk(VIDC_ERR, "Invalid Paramters\n");
return NULL;
}
hdevice = (struct q6_hfi_device *)
kzalloc(sizeof(struct q6_hfi_device), GFP_KERNEL);
if (!hdevice) {
- dprintk(VIDC_ERR, "failed to allocate new device");
+ dprintk(VIDC_ERR, "failed to allocate new device\n");
goto err_alloc;
}
@@ -356,14 +360,15 @@ static void *q6_hfi_get_device(u32 device_id,
rc = q6_hfi_init_resources(device, res);
if (rc) {
- dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc);
+ if (rc != -EPROBE_DEFER)
+ dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc);
goto err_fail_init_res;
}
return device;
err_fail_init_res:
q6_hfi_delete_device(device);
- return NULL;
+ return ERR_PTR(rc);
}
void q6_hfi_delete_device(void *device)
@@ -411,15 +416,15 @@ static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv)
int rc = 0;
if (!data || !device) {
- dprintk(VIDC_ERR, "%s - Invalid arguments", __func__);
+ dprintk(VIDC_ERR, "%s - Invalid arguments\n", __func__);
return -EINVAL;
}
- dprintk(VIDC_DBG, "%s opcode = %u payload size = %u", __func__,
+ dprintk(VIDC_DBG, "%s opcode = %u payload size = %u\n", __func__,
data->opcode, data->payload_size);
if (data->opcode == RESET_EVENTS) {
- dprintk(VIDC_ERR, "%s Received subsystem reset event: %d",
+ dprintk(VIDC_ERR, "%s Received subsystem reset event: %d\n",
__func__, data->reset_event);
pkt.packet_type = HFI_MSG_EVENT_NOTIFY;
pkt.size = sizeof(pkt);
@@ -430,13 +435,13 @@ static int q6_hfi_apr_callback(struct apr_client_data *data, void *priv)
} else if (data->payload_size > 0) {
payload = data->payload;
} else {
- dprintk(VIDC_ERR, "%s - Invalid payload size", __func__);
+ dprintk(VIDC_ERR, "%s - Invalid payload size\n", __func__);
return -EINVAL;
}
rc = q6_hfi_iface_eventq_write(device, payload);
if (rc) {
- dprintk(VIDC_ERR, "%s failed to write to event queue",
+ dprintk(VIDC_ERR, "%s failed to write to event queue\n",
__func__);
return rc;
}
@@ -458,14 +463,14 @@ static int q6_init_event_queue(struct q6_hfi_device *dev)
struct q6_iface_q_info *iface_q;
if (!dev) {
- dprintk(VIDC_ERR, "Invalid device");
+ dprintk(VIDC_ERR, "Invalid device\n");
return -EINVAL;
}
iface_q = &dev->event_queue;
iface_q->buffer = kzalloc(Q6_IFACEQ_QUEUE_SIZE, GFP_KERNEL);
if (!iface_q->buffer) {
- dprintk(VIDC_ERR, "iface_q alloc failed");
+ dprintk(VIDC_ERR, "iface_q alloc failed\n");
q6_release_event_queue(dev);
return -ENOMEM;
} else {
@@ -494,11 +499,11 @@ static int q6_hfi_core_init(void *device)
if (!dev->event_queue.buffer) {
rc = q6_init_event_queue(dev);
if (rc) {
- dprintk(VIDC_ERR, "q6_init_event_queue failed");
+ dprintk(VIDC_ERR, "q6_init_event_queue failed\n");
goto err_core_init;
}
} else {
- dprintk(VIDC_ERR, "queue buffer exists");
+ dprintk(VIDC_ERR, "queue buffer exists\n");
rc = -EEXIST;
goto err_core_init;
}
@@ -507,13 +512,13 @@ static int q6_hfi_core_init(void *device)
rc = create_pkt_cmd_sys_init(&apr.pkt, HFI_VIDEO_ARCH_OX);
if (rc) {
- dprintk(VIDC_ERR, "Failed to create sys init pkt");
+ dprintk(VIDC_ERR, "Failed to create sys init pkt\n");
goto err_core_init;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -562,7 +567,7 @@ static void *q6_hfi_session_init(void *device, u32 session_id,
if (create_pkt_cmd_sys_session_init(&apr.pkt, (u32)new_session,
session_type, codec_type)) {
- dprintk(VIDC_ERR, "session_init: failed to create packet");
+ dprintk(VIDC_ERR, "session_init: failed to create packet\n");
goto err_session_init;
}
/*
@@ -577,7 +582,7 @@ static void *q6_hfi_session_init(void *device, u32 session_id,
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
/* Delete the session id as the send pkt is not successful */
mutex_lock(&dev->session_lock);
@@ -612,13 +617,13 @@ static int q6_hal_send_session_cmd(void *sess,
rc = create_pkt_cmd_session_cmd(&apr.pkt, pkt_type, (u32)session);
if (rc) {
- dprintk(VIDC_ERR, "send session cmd: create pkt failed");
+ dprintk(VIDC_ERR, "send session cmd: create pkt failed\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -643,11 +648,11 @@ static int q6_hfi_session_clean(void *session)
{
struct hal_session *sess_close;
if (!session) {
- dprintk(VIDC_ERR, "Invalid Params %s", __func__);
+ dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
return -EINVAL;
}
sess_close = session;
- dprintk(VIDC_DBG, "deleted the session: 0x%x",
+ dprintk(VIDC_DBG, "deleted the session: 0x%x\n",
sess_close->session_id);
mutex_lock(&((struct q6_hfi_device *)
sess_close->device)->session_lock);
@@ -682,14 +687,14 @@ static int q6_hfi_session_set_buffers(void *sess,
rc = create_pkt_cmd_session_set_buffers(&apr->pkt,
(u32)session, buffer_info);
if (rc) {
- dprintk(VIDC_ERR, "set buffers: failed to create packet");
+ dprintk(VIDC_ERR, "set buffers: failed to create packet\n");
goto err_create_pkt;
}
- dprintk(VIDC_INFO, "set buffers: 0x%x", buffer_info->buffer_type);
+ dprintk(VIDC_INFO, "set buffers: 0x%x\n", buffer_info->buffer_type);
rc = apr_send_pkt(dev->apr, (uint32_t *)apr);
if (rc != apr->hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -724,15 +729,15 @@ static int q6_hfi_session_release_buffers(void *sess,
rc = create_pkt_cmd_session_release_buffers(&apr->pkt,
(u32)session, buffer_info);
if (rc) {
- dprintk(VIDC_ERR, "release buffers: failed to create packet");
+ dprintk(VIDC_ERR, "release buffers: failed to create packet\n");
goto err_create_pkt;
}
- dprintk(VIDC_INFO, "Release buffers: 0x%x", buffer_info->buffer_type);
+ dprintk(VIDC_INFO, "Release buffers: 0x%x\n", buffer_info->buffer_type);
rc = apr_send_pkt(dev->apr, (uint32_t *)apr);
if (rc != apr->hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -785,7 +790,7 @@ static int q6_hfi_session_etb(void *sess,
struct q6_hfi_device *dev;
if (!session || !input_frame || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
@@ -799,15 +804,15 @@ static int q6_hfi_session_etb(void *sess,
(u32)session, input_frame);
if (rc) {
dprintk(VIDC_ERR,
- "Session etb decoder: failed to create pkt");
+ "Session etb decoder: failed to create pkt\n");
goto err_create_pkt;
}
- dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER");
- dprintk(VIDC_DBG, "addr = 0x%x ts = %lld",
+ dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n");
+ dprintk(VIDC_DBG, "addr = 0x%x ts = %lld\n",
input_frame->device_addr, input_frame->timestamp);
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -821,13 +826,13 @@ static int q6_hfi_session_etb(void *sess,
(u32)session, input_frame);
if (rc) {
dprintk(VIDC_ERR,
- "Session etb encoder: failed to create pkt");
+ "Session etb encoder: failed to create pkt\n");
goto err_create_pkt;
}
- dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER");
+ dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n");
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -846,7 +851,7 @@ static int q6_hfi_session_ftb(void *sess,
struct q6_hfi_device *dev;
if (!session || !output_frame || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
@@ -855,14 +860,14 @@ static int q6_hfi_session_ftb(void *sess,
rc = create_pkt_cmd_session_ftb(&apr.pkt, (u32)session, output_frame);
if (rc) {
- dprintk(VIDC_ERR, "Session ftb: failed to create pkt");
+ dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n");
goto err_create_pkt;
}
- dprintk(VIDC_INFO, "Q OUTPUT BUFFER");
+ dprintk(VIDC_INFO, "Q OUTPUT BUFFER\n");
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -881,7 +886,7 @@ static int q6_hfi_session_parse_seq_hdr(void *sess,
struct q6_hfi_device *dev;
if (!session || !seq_hdr || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
@@ -894,13 +899,13 @@ static int q6_hfi_session_parse_seq_hdr(void *sess,
(u32)session, seq_hdr);
if (rc) {
dprintk(VIDC_ERR,
- "Session parse seq hdr: failed to create pkt");
+ "Session parse seq hdr: failed to create pkt\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)apr);
if (rc != apr->hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -919,7 +924,7 @@ static int q6_hfi_session_get_seq_hdr(void *sess,
struct q6_hfi_device *dev;
if (!session || !seq_hdr || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
@@ -931,13 +936,13 @@ static int q6_hfi_session_get_seq_hdr(void *sess,
rc = create_pkt_cmd_session_get_seq_hdr(&apr->pkt, (u32)session,
seq_hdr);
if (rc) {
- dprintk(VIDC_ERR, "Session get seq hdr: failed to create pkt");
+ dprintk(VIDC_ERR, "Session get seqhdr: failed to create pkt\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)apr);
if (rc != apr->hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -955,7 +960,7 @@ static int q6_hfi_session_get_buf_req(void *sess)
struct q6_hfi_device *dev;
if (!session || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
@@ -964,13 +969,13 @@ static int q6_hfi_session_get_buf_req(void *sess)
rc = create_pkt_cmd_session_get_buf_req(&apr.pkt, (u32)session);
if (rc) {
- dprintk(VIDC_ERR, "Session get buf req: failed to create pkt");
+ dprintk(VIDC_ERR, "Session get bufreq: failed to create pkt\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -987,7 +992,7 @@ static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode)
struct q6_hfi_device *dev;
if (!session || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
@@ -996,13 +1001,13 @@ static int q6_hfi_session_flush(void *sess, enum hal_flush flush_mode)
rc = create_pkt_cmd_session_flush(&apr.pkt, (u32)session, flush_mode);
if (rc) {
- dprintk(VIDC_ERR, "Session flush: failed to create pkt");
+ dprintk(VIDC_ERR, "Session flush: failed to create pkt\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)&apr);
if (rc != apr.hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -1023,24 +1028,24 @@ static int q6_hfi_session_set_property(void *sess,
struct q6_hfi_device *dev;
if (!session || !pdata || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
- dprintk(VIDC_DBG, "in set_prop,with prop id: 0x%x", ptype);
+ dprintk(VIDC_DBG, "in set_prop,with prop id: 0x%x\n", ptype);
q6_hfi_add_apr_hdr(dev, &apr->hdr, VIDC_IFACEQ_VAR_LARGE_PKT_SIZE);
rc = create_pkt_cmd_session_set_property(&apr->pkt,
(u32)session, ptype, pdata);
if (rc) {
- dprintk(VIDC_ERR, "set property: failed to create packet");
+ dprintk(VIDC_ERR, "set property: failed to create packet\n");
goto err_create_pkt;
}
rc = apr_send_pkt(dev->apr, (uint32_t *)apr);
if (rc != apr->hdr.pkt_size) {
- dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d",
+ dprintk(VIDC_ERR, "%s: apr_send_pkt failed rc: %d\n",
__func__, rc);
rc = -EBADE;
} else
@@ -1057,12 +1062,12 @@ static int q6_hfi_session_get_property(void *sess,
struct q6_hfi_device *dev;
if (!session || !session->device) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
dev = session->device;
- dprintk(VIDC_DBG, "IN func: , with property id: %d", ptype);
+ dprintk(VIDC_DBG, "IN func: , with property id: %d\n", ptype);
switch (ptype) {
case HAL_CONFIG_FRAME_RATE:
@@ -1168,7 +1173,7 @@ static int q6_hfi_session_get_property(void *sess,
case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
case HAL_PARAM_VENC_LOW_LATENCY:
default:
- dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x", ptype);
+ dprintk(VIDC_INFO, "DEFAULT: Calling 0x%x\n", ptype);
break;
}
return 0;
@@ -1187,7 +1192,7 @@ static int q6_hfi_iommu_get_domain_partition(void *dev, u32 flags,
{
(void)dev;
- dprintk(VIDC_ERR, "Not implemented: %s", __func__);
+ dprintk(VIDC_ERR, "Not implemented: %s\n", __func__);
return -ENOTSUPP;
}
@@ -1202,7 +1207,7 @@ static int q6_hfi_iommu_attach(struct q6_hfi_device *device)
struct iommu_info *iommu_map;
if (!device || !device->res) {
- dprintk(VIDC_ERR, "Invalid parameter: %p", device);
+ dprintk(VIDC_ERR, "Invalid parameter: %p\n", device);
return -EINVAL;
}
@@ -1212,16 +1217,16 @@ static int q6_hfi_iommu_attach(struct q6_hfi_device *device)
group = iommu_map->group;
domain = msm_get_iommu_domain(iommu_map->domain);
if (IS_ERR_OR_NULL(domain)) {
- dprintk(VIDC_ERR, "Failed to get domain: %s",
+ dprintk(VIDC_ERR, "Failed to get domain: %s\n",
iommu_map->name);
rc = IS_ERR(domain) ? PTR_ERR(domain) : -EINVAL;
break;
}
- dprintk(VIDC_DBG, "Attaching domain(id:%d) %p to group %p",
+ dprintk(VIDC_DBG, "Attaching domain(id:%d) %p to group %p\n",
iommu_map->domain, domain, group);
rc = iommu_attach_group(domain, group);
if (rc) {
- dprintk(VIDC_ERR, "IOMMU attach failed: %s",
+ dprintk(VIDC_ERR, "IOMMU attach failed: %s\n",
iommu_map->name);
break;
}
@@ -1248,7 +1253,7 @@ static void q6_hfi_iommu_detach(struct q6_hfi_device *device)
int i;
if (!device || !device->res) {
- dprintk(VIDC_ERR, "Invalid parameter: %p", device);
+ dprintk(VIDC_ERR, "Invalid parameter: %p\n", device);
return;
}
@@ -1288,14 +1293,14 @@ static int q6_hfi_load_fw(void *dev)
device);
if (device->apr == NULL) {
- dprintk(VIDC_ERR, "Failed to register with QDSP6");
+ dprintk(VIDC_ERR, "Failed to register with QDSP6\n");
rc = -EINVAL;
goto fail_apr_register;
}
rc = q6_hfi_iommu_attach(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to attach iommu");
+ dprintk(VIDC_ERR, "Failed to attach iommu\n");
goto fail_iommu_attach;
}
@@ -1326,7 +1331,7 @@ static void q6_hfi_unload_fw(void *hfi_device_data)
if (device->apr) {
if (apr_deregister(device->apr))
- dprintk(VIDC_ERR, "Failed to deregister APR");
+ dprintk(VIDC_ERR, "Failed to deregister APR\n");
device->apr = NULL;
}
}
@@ -1377,13 +1382,19 @@ int q6_hfi_initialize(struct hfi_device *hdev, u32 device_id,
int rc = 0;
if (!hdev || !res || !callback) {
- dprintk(VIDC_ERR, "Invalid params: %p %p %p",
+ dprintk(VIDC_ERR, "Invalid params: %p %p %p\n",
hdev, res, callback);
rc = -EINVAL;
goto err_hfi_init;
}
hdev->hfi_device_data = q6_hfi_get_device(device_id, res, callback);
+ if (IS_ERR_OR_NULL(hdev->hfi_device_data)) {
+ rc = PTR_ERR(hdev->hfi_device_data);
+ rc = !rc ? -EINVAL : rc;
+ goto err_hfi_init;
+ }
+
q6_init_hfi_callbacks(hdev);
err_hfi_init:
diff --git a/drivers/media/platform/msm/vidc/q6_hfi.h b/drivers/media/platform/msm/vidc/q6_hfi.h
index 5f48a5126b97..e63d9ef30e1b 100644
--- a/drivers/media/platform/msm/vidc/q6_hfi.h
+++ b/drivers/media/platform/msm/vidc/q6_hfi.h
@@ -13,7 +13,7 @@
#ifndef __Q6_HFI_H__
#define __Q6_HFI_H__
-#include <mach/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr.h>
#include "vidc_hfi.h"
#include "vidc_hfi_helper.h"
#include "msm_vidc_resources.h"
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
index 4345eb833348..e251ad0ab4d1 100644
--- a/drivers/media/platform/msm/vidc/venus_hfi.c
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -23,7 +23,7 @@
#include <mach/ocmem.h>
#include <mach/scm.h>
#include <mach/subsystem_restart.h>
-#include <mach/msm_smem.h>
+#include <soc/qcom/smem.h>
#include <asm/memory.h>
#include <linux/iopoll.h>
#include "hfi_packetization.h"
@@ -107,7 +107,7 @@ static void venus_hfi_sim_modify_cmd_packet(u8 *packet)
u8 i;
if (!packet) {
- dprintk(VIDC_ERR, "Invalid Param");
+ dprintk(VIDC_ERR, "Invalid Param\n");
return;
}
@@ -232,7 +232,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
u32 *write_ptr;
if (!info || !packet || !rx_req_is_set) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
@@ -245,7 +245,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
queue = (struct hfi_queue_header *) qinfo->q_hdr;
if (!queue) {
- dprintk(VIDC_ERR, "queue not present");
+ dprintk(VIDC_ERR, "queue not present\n");
return -ENOENT;
}
@@ -257,10 +257,10 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
}
packet_size_in_words = (*(u32 *)packet) >> 2;
- dprintk(VIDC_DBG, "Packet_size in words: %d", packet_size_in_words);
+ dprintk(VIDC_DBG, "Packet_size in words: %d\n", packet_size_in_words);
if (packet_size_in_words == 0) {
- dprintk(VIDC_ERR, "Zero packet size");
+ dprintk(VIDC_ERR, "Zero packet size\n");
return -ENODATA;
}
@@ -269,10 +269,10 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
empty_space = (queue->qhdr_write_idx >= read_idx) ?
(queue->qhdr_q_size - (queue->qhdr_write_idx - read_idx)) :
(read_idx - queue->qhdr_write_idx);
- dprintk(VIDC_DBG, "Empty_space: %d", empty_space);
+ dprintk(VIDC_DBG, "Empty_space: %d\n", empty_space);
if (empty_space <= packet_size_in_words) {
queue->qhdr_tx_req = 1;
- dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)",
+ dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n",
empty_space, packet_size_in_words);
return -ENOTEMPTY;
}
@@ -282,7 +282,7 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
new_write_idx = (queue->qhdr_write_idx + packet_size_in_words);
write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
(queue->qhdr_write_idx << 2));
- dprintk(VIDC_DBG, "Write Ptr: %d", (u32) write_ptr);
+ dprintk(VIDC_DBG, "Write Ptr: %d\n", (u32) write_ptr);
if (new_write_idx < queue->qhdr_q_size) {
memcpy(write_ptr, packet, packet_size_in_words << 2);
} else {
@@ -301,7 +301,6 @@ static int venus_hfi_write_queue(void *info, u8 *packet, u32 *rx_req_is_set)
/*Memory barrier to make sure write index is updated before an
* interupt is raised on venus.*/
mb();
- dprintk(VIDC_DBG, "Out : ");
return 0;
}
@@ -311,7 +310,7 @@ static void venus_hfi_hal_sim_modify_msg_packet(u8 *packet)
struct hal_session *sess;
if (!packet) {
- dprintk(VIDC_ERR, "Invalid Param: ");
+ dprintk(VIDC_ERR, "Invalid Param\n");
return;
}
@@ -367,7 +366,7 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set)
int rc = 0;
if (!info || !packet || !pb_tx_req_is_set) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
@@ -395,14 +394,14 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set)
read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
(queue->qhdr_read_idx << 2));
packet_size_in_words = (*read_ptr) >> 2;
- dprintk(VIDC_DBG, "packet_size_in_words: %d", packet_size_in_words);
+ dprintk(VIDC_DBG, "packet_size_in_words: %d\n", packet_size_in_words);
if (packet_size_in_words == 0) {
- dprintk(VIDC_ERR, "Zero packet size");
+ dprintk(VIDC_ERR, "Zero packet size\n");
return -ENODATA;
}
new_read_idx = queue->qhdr_read_idx + packet_size_in_words;
- dprintk(VIDC_DBG, "Read Ptr: %d", (u32) new_read_idx);
+ dprintk(VIDC_DBG, "Read Ptr: %d\n", (u32) new_read_idx);
if (((packet_size_in_words << 2) <= VIDC_IFACEQ_MED_PKT_SIZE)
&& queue->qhdr_read_idx <= queue->qhdr_q_size) {
if (new_read_idx < queue->qhdr_q_size) {
@@ -439,7 +438,6 @@ static int venus_hfi_read_queue(void *info, u8 *packet, u32 *pb_tx_req_is_set)
dprintk(VIDC_PKT, "%s: %p\n", __func__, qinfo);
venus_hfi_dump_packet(packet);
}
- dprintk(VIDC_DBG, "Out : ");
return rc;
}
@@ -451,23 +449,23 @@ static int venus_hfi_alloc(struct venus_hfi_device *dev, void *mem,
int rc = 0;
if (!dev || !dev->hal_client || !mem || !size) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
vmem = (struct vidc_mem_addr *)mem;
- dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d", size, flags);
+ dprintk(VIDC_INFO, "start to alloc: size:%d, Flags: %d\n", size, flags);
venus_hfi_power_enable(dev);
alloc = msm_smem_alloc(dev->hal_client, size, align, flags, usage, 1);
- dprintk(VIDC_DBG, "Alloc done");
+ dprintk(VIDC_DBG, "Alloc done\n");
if (!alloc) {
dprintk(VIDC_ERR, "Alloc failed\n");
rc = -ENOMEM;
goto fail_smem_alloc;
}
- dprintk(VIDC_DBG, "venus_hfi_alloc:ptr=%p,size=%d",
+ dprintk(VIDC_DBG, "venus_hfi_alloc: ptr = %p, size = %d\n",
alloc->kvaddr, size);
rc = msm_smem_cache_operations(dev->hal_client, alloc,
SMEM_CACHE_CLEAN);
@@ -529,7 +527,7 @@ static void venus_hfi_write_register(struct venus_hfi_device *device, u32 reg,
}
hwiosymaddr = ((u32)base_addr + (hwiosymaddr));
- dprintk(VIDC_DBG, "Base addr: 0x%x, written to: 0x%x, Value: 0x%x...",
+ dprintk(VIDC_DBG, "Base addr: 0x%x, written to: 0x%x, Value: 0x%x...\n",
(u32)base_addr, hwiosymaddr, value);
writel_relaxed(value, hwiosymaddr);
wmb();
@@ -555,6 +553,25 @@ static int venus_hfi_read_register(struct venus_hfi_device *device, u32 reg)
return rc;
}
+static void venus_hfi_set_registers(struct venus_hfi_device *device)
+{
+ struct reg_set *reg_set;
+ int i;
+
+ if (!device->res) {
+ dprintk(VIDC_ERR,
+ "device resources null, cannot set registers\n");
+ return;
+ }
+
+ reg_set = &device->res->reg_set;
+ for (i = 0; i < reg_set->count; i++) {
+ venus_hfi_write_register(device,
+ reg_set->reg_tbl[i].reg,
+ reg_set->reg_tbl[i].value, 0);
+ }
+}
+
static int venus_hfi_core_start_cpu(struct venus_hfi_device *device)
{
u32 ctrl_status = 0, count = 0, rc = 0;
@@ -655,7 +672,7 @@ static int venus_hfi_unvote_bus(void *dev,
struct venus_hfi_device *device = dev;
if (!device) {
- dprintk(VIDC_ERR, "%s invalid device handle %p",
+ dprintk(VIDC_ERR, "%s invalid device handle %p\n",
__func__, device);
return -EINVAL;
}
@@ -684,7 +701,7 @@ static void venus_hfi_unvote_buses(void *dev, enum mem_type mtype)
struct venus_hfi_device *device = dev;
if (!device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return;
}
@@ -719,7 +736,7 @@ static int venus_hfi_get_bus_vector(struct venus_hfi_device *device, int load,
if (!device || (mtype != DDR_MEM && mtype != OCMEM_MEM) ||
(type != MSM_VIDC_ENCODER && type != MSM_VIDC_DECODER)) {
- dprintk(VIDC_ERR, "%s invalid params", __func__);
+ dprintk(VIDC_ERR, "%s invalid params\n", __func__);
return -EINVAL;
}
@@ -752,7 +769,7 @@ static int venus_hfi_scale_bus(void *dev, int load,
int bus_vector = 0;
if (!device) {
- dprintk(VIDC_ERR, "%s invalid device handle %p",
+ dprintk(VIDC_ERR, "%s invalid device handle %p\n",
__func__, device);
return -EINVAL;
}
@@ -788,7 +805,7 @@ static int venus_hfi_scale_buses(void *dev, enum mem_type mtype)
struct venus_hfi_device *device = dev;
if (!device) {
- dprintk(VIDC_ERR, "%s invalid parameters", __func__);
+ dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
return -EINVAL;
}
for (i = 0; i < MSM_VIDC_MAX_DEVICES; i++) {
@@ -848,7 +865,7 @@ static inline int venus_hfi_reset_core(struct venus_hfi_device *device)
VIDC_CTRL_INIT, 0x1, 0);
rc = venus_hfi_core_start_cpu(device);
if (rc)
- dprintk(VIDC_ERR, "Failed to start core");
+ dprintk(VIDC_ERR, "Failed to start core\n");
return rc;
}
@@ -864,7 +881,7 @@ static inline int venus_hfi_clk_enable(struct venus_hfi_device *device)
return -EINVAL;
}
if (device->clocks_enabled) {
- dprintk(VIDC_DBG, "Clocks already enabled");
+ dprintk(VIDC_DBG, "Clocks already enabled\n");
return 0;
}
@@ -908,7 +925,7 @@ static inline void venus_hfi_clk_disable(struct venus_hfi_device *device)
return;
}
if (!device->clocks_enabled) {
- dprintk(VIDC_DBG, "Clocks already disabled");
+ dprintk(VIDC_DBG, "Clocks already disabled\n");
return;
}
@@ -960,14 +977,14 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device)
return -EINVAL;
}
if (!device->power_enabled) {
- dprintk(VIDC_DBG, "Power already disabled");
+ dprintk(VIDC_DBG, "Power already disabled\n");
goto already_disabled;
}
/*Temporarily enable clocks to make TZ call.*/
rc = venus_hfi_clk_enable(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to enable clocks before TZ call");
+ dprintk(VIDC_ERR, "Failed to enable clocks before TZ call\n");
return rc;
}
rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
@@ -980,7 +997,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device)
venus_hfi_iommu_detach(device);
rc = regulator_disable(venus_hfi_get_regulator(device, "venus"));
if (rc) {
- dprintk(VIDC_ERR, "Failed to disable GDSC, %d", rc);
+ dprintk(VIDC_ERR, "Failed to disable GDSC, %d\n", rc);
return rc;
}
if (device->res->has_ocmem)
@@ -989,6 +1006,7 @@ static inline int venus_hfi_power_off(struct venus_hfi_device *device)
venus_hfi_unvote_buses(device, DDR_MEM);
device->power_enabled = 0;
+ dprintk(VIDC_INFO, "entering power collapse\n");
already_disabled:
return rc;
}
@@ -1006,13 +1024,13 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device)
else
rc = venus_hfi_scale_buses(device, DDR_MEM);
if (rc) {
- dprintk(VIDC_ERR, "Failed to scale buses");
+ dprintk(VIDC_ERR, "Failed to scale buses\n");
goto err_scale_buses;
}
rc = regulator_enable(venus_hfi_get_regulator(device, "venus"));
if (rc) {
- dprintk(VIDC_ERR, "Failed to enable GDSC %d", rc);
+ dprintk(VIDC_ERR, "Failed to enable GDSC %d\n", rc);
goto err_enable_gdsc;
}
@@ -1024,21 +1042,48 @@ static inline int venus_hfi_power_on(struct venus_hfi_device *device)
rc = venus_hfi_clk_enable(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to enable clocks");
+ dprintk(VIDC_ERR, "Failed to enable clocks\n");
goto err_enable_clk;
}
+
+ /*
+ * Re-program all of the registers that get reset as a result of
+ * regulator_disable() and _enable()
+ */
+ venus_hfi_set_registers(device);
+
+ venus_hfi_write_register(device, VIDC_UC_REGION_ADDR,
+ (u32)device->iface_q_table.align_device_addr, 0);
+ venus_hfi_write_register(device,
+ VIDC_UC_REGION_SIZE, SHARED_QSIZE, 0);
+ venus_hfi_write_register(device, VIDC_CPU_CS_SCIACMDARG2,
+ (u32)device->iface_q_table.align_device_addr,
+ device->iface_q_table.align_virtual_addr);
+
+ if (!IS_ERR_OR_NULL(device->sfr.align_device_addr))
+ venus_hfi_write_register(device, VIDC_SFR_ADDR,
+ (u32)device->sfr.align_device_addr, 0);
+ if (!IS_ERR_OR_NULL(device->qdss.align_device_addr))
+ venus_hfi_write_register(device, VIDC_MMAP_ADDR,
+ (u32)device->qdss.align_device_addr, 0);
+
+ /* Reboot the firmware */
rc = venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME);
if (rc) {
dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc);
goto err_set_video_state;
}
+
+ /* Wait for boot completion */
rc = venus_hfi_reset_core(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to reset venus core");
+ dprintk(VIDC_ERR, "Failed to reset venus core\n");
goto err_reset_core;
}
+
device->power_enabled = 1;
+ dprintk(VIDC_INFO, "resuming from power collapse\n");
return rc;
err_reset_core:
venus_hfi_tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
@@ -1084,7 +1129,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device)
return -EINVAL;
}
if (device->clocks_enabled) {
- dprintk(VIDC_DBG, "Clocks are already enabled");
+ dprintk(VIDC_DBG, "Clocks are already enabled\n");
goto already_enabled;
}
cancel_delayed_work(&venus_hfi_pm_work);
@@ -1098,7 +1143,7 @@ static inline int venus_hfi_clk_gating_off(struct venus_hfi_device *device)
} else {
rc = venus_hfi_clk_enable(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed venus clock enable");
+ dprintk(VIDC_ERR, "Failed venus clock enable\n");
goto fail_clk_power_on;
}
venus_hfi_write_register(device,
@@ -1163,14 +1208,14 @@ static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device,
struct vidc_iface_q_info *q_info;
int result = -EPERM;
if (!device || !pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
mutex_lock(&device->write_lock);
q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
if (!q_info) {
- dprintk(VIDC_ERR, "cannot write to shared Q's");
+ dprintk(VIDC_ERR, "cannot write to shared Q's\n");
goto err_q_null;
}
if (!venus_hfi_write_queue(q_info, (u8 *)pkt, &rx_req_is_set)) {
@@ -1196,7 +1241,7 @@ static int venus_hfi_iface_cmdq_write(struct venus_hfi_device *device,
result = 0;
mutex_unlock(&device->clk_pwr_lock);
} else {
- dprintk(VIDC_ERR, "venus_hfi_iface_cmdq_write:queue_full");
+ dprintk(VIDC_ERR, "venus_hfi_iface_cmdq_write:queue_full\n");
}
err_q_write:
err_q_null:
@@ -1211,13 +1256,13 @@ static int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt)
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
mutex_lock(&device->read_lock);
if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX].
q_array.align_virtual_addr == 0) {
- dprintk(VIDC_ERR, "cannot read from shared MSG Q's");
+ dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n");
rc = -ENODATA;
goto read_error_null;
}
@@ -1239,7 +1284,7 @@ static int venus_hfi_iface_msgq_read(struct venus_hfi_device *device, void *pkt)
rc = 0;
mutex_unlock(&device->clk_pwr_lock);
} else {
- dprintk(VIDC_INFO, "venus_hfi_iface_msgq_read:queue_empty");
+ dprintk(VIDC_INFO, "venus_hfi_iface_msgq_read:queue_empty\n");
rc = -ENODATA;
}
read_error:
@@ -1255,13 +1300,13 @@ static int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
struct vidc_iface_q_info *q_info;
if (!pkt) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
}
mutex_lock(&device->read_lock);
if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX].
q_array.align_virtual_addr == 0) {
- dprintk(VIDC_ERR, "cannot read from shared DBG Q's");
+ dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n");
rc = -ENODATA;
goto dbg_error_null;
}
@@ -1283,7 +1328,7 @@ static int venus_hfi_iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
rc = 0;
mutex_unlock(&device->clk_pwr_lock);
} else {
- dprintk(VIDC_INFO, "venus_hfi_iface_dbgq_read:queue_empty");
+ dprintk(VIDC_INFO, "venus_hfi_iface_dbgq_read:queue_empty\n");
rc = -ENODATA;
}
dbg_error:
@@ -1375,7 +1420,7 @@ static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map,
SZ_4K, 0, &iova);
if (rc) {
dprintk(VIDC_ERR,
- "IOMMU QDSS mapping failed for addr 0x%x",
+ "IOMMU QDSS mapping failed for addr 0x%x\n",
venus_qdss_entries[i][0]);
rc = -ENOMEM;
break;
@@ -1387,7 +1432,7 @@ static int venus_hfi_get_qdss_iommu_virtual_addr(struct hfi_mem_map *mem_map,
}
if (i < num_entries) {
dprintk(VIDC_ERR,
- "IOMMU QDSS mapping failed, Freeing entries %d", i);
+ "IOMMU QDSS mapping failed, Freeing entries %d\n", i);
for (--i; i >= 0; i--) {
msm_iommu_unmap_contig_buffer(
(unsigned long)(mem_map[i].virtual_addr),
@@ -1416,7 +1461,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev)
QUEUE_SIZE, 1, 0,
HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
- dprintk(VIDC_ERR, "iface_q_table_alloc_fail");
+ dprintk(VIDC_ERR, "iface_q_table_alloc_fail\n");
goto fail_alloc_queue;
}
dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr;
@@ -1444,7 +1489,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev)
HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
dprintk(VIDC_WARN,
- "qdss_alloc_fail: QDSS messages logging will not work");
+ "qdss_alloc_fail: QDSS messages logging will not work\n");
dev->qdss.align_device_addr = NULL;
} else {
dev->qdss.align_device_addr = mem_addr->align_device_addr;
@@ -1456,7 +1501,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev)
SFR_SIZE, 1, 0,
HAL_BUFFER_INTERNAL_CMD_QUEUE);
if (rc) {
- dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work");
+ dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work\n");
dev->sfr.align_device_addr = NULL;
} else {
dev->sfr.align_device_addr = mem_addr->align_device_addr;
@@ -1517,7 +1562,7 @@ static int venus_hfi_interface_queues_init(struct venus_hfi_device *dev)
rc = venus_hfi_get_qdss_iommu_virtual_addr(mem_map, domain, partition);
if (rc) {
dprintk(VIDC_ERR,
- "IOMMU mapping failed, Freeing qdss memdata");
+ "IOMMU mapping failed, Freeing qdss memdata\n");
venus_hfi_free(dev, dev->qdss.mem_data);
dev->qdss.mem_data = NULL;
}
@@ -1536,25 +1581,6 @@ fail_alloc_queue:
return -ENOMEM;
}
-static void venus_hfi_set_registers(struct venus_hfi_device *device)
-{
- struct reg_set *reg_set;
- int i;
-
- if (!device->res) {
- dprintk(VIDC_ERR,
- "device resources null, cannot set registers\n");
- return;
- }
-
- reg_set = &device->res->reg_set;
- for (i = 0; i < reg_set->count; i++) {
- venus_hfi_write_register(device,
- reg_set->reg_tbl[i].reg,
- reg_set->reg_tbl[i].value, 0);
- }
-}
-
static int venus_hfi_sys_set_debug(struct venus_hfi_device *device, u32 debug)
{
u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
@@ -1620,7 +1646,7 @@ static int venus_hfi_core_init(void *device)
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "Invalid device");
+ dprintk(VIDC_ERR, "Invalid device\n");
return -ENODEV;
}
@@ -1634,23 +1660,23 @@ static int venus_hfi_core_init(void *device)
if (!dev->hal_client) {
dev->hal_client = msm_smem_new_client(SMEM_ION, dev->res);
if (dev->hal_client == NULL) {
- dprintk(VIDC_ERR, "Failed to alloc ION_Client");
+ dprintk(VIDC_ERR, "Failed to alloc ION_Client\n");
rc = -ENODEV;
goto err_core_init;
}
- dprintk(VIDC_DBG, "Dev_Virt: 0x%x, Reg_Virt: 0x%x",
+ dprintk(VIDC_DBG, "Dev_Virt: 0x%x, Reg_Virt: 0x%x\n",
dev->hal_data->device_base_addr,
(u32) dev->hal_data->register_base_addr);
rc = venus_hfi_interface_queues_init(dev);
if (rc) {
- dprintk(VIDC_ERR, "failed to init queues");
+ dprintk(VIDC_ERR, "failed to init queues\n");
rc = -ENOMEM;
goto err_core_init;
}
} else {
- dprintk(VIDC_ERR, "hal_client exists");
+ dprintk(VIDC_ERR, "hal_client exists\n");
rc = -EEXIST;
goto err_core_init;
}
@@ -1659,14 +1685,14 @@ static int venus_hfi_core_init(void *device)
VIDC_CTRL_INIT, 0x1, 0);
rc = venus_hfi_core_start_cpu(dev);
if (rc) {
- dprintk(VIDC_ERR, "Failed to start core");
+ dprintk(VIDC_ERR, "Failed to start core\n");
rc = -ENODEV;
goto err_core_init;
}
rc = create_pkt_cmd_sys_init(&pkt, HFI_VIDEO_ARCH_OX);
if (rc) {
- dprintk(VIDC_ERR, "Failed to create sys init pkt");
+ dprintk(VIDC_ERR, "Failed to create sys init pkt\n");
goto err_core_init;
}
if (venus_hfi_iface_cmdq_write(dev, &pkt)) {
@@ -1675,7 +1701,7 @@ static int venus_hfi_core_init(void *device)
}
rc = create_pkt_cmd_sys_image_version(&version_pkt);
if (rc || venus_hfi_iface_cmdq_write(dev, &version_pkt))
- dprintk(VIDC_WARN, "Failed to send image version pkt to f/w");
+ dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n");
return rc;
err_core_init:
@@ -1690,7 +1716,7 @@ static int venus_hfi_core_release(void *device)
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return -ENODEV;
}
if (dev->hal_client) {
@@ -1719,14 +1745,17 @@ static int venus_hfi_is_cmd_pending(struct venus_hfi_device *dev)
struct vidc_iface_q_info *q_info;
u32 write_ptr, read_ptr;
u32 rc = 0;
+
q_info = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
if (!q_info)
- dprintk(VIDC_ERR, "cannot read shared Q's");
- queue = (struct hfi_queue_header *) q_info->q_hdr;
+ dprintk(VIDC_ERR, "cannot read shared Q's\n");
+
+ queue = (struct hfi_queue_header *)q_info->q_hdr;
if (!queue) {
- dprintk(VIDC_ERR, "queue not present");
+ dprintk(VIDC_ERR, "queue not present\n");
return -ENOENT;
}
+
write_ptr = (u32)queue->qhdr_write_idx;
read_ptr = (u32)queue->qhdr_read_idx;
rc = read_ptr - write_ptr;
@@ -1740,7 +1769,7 @@ static inline void venus_hfi_clk_gating_on(struct venus_hfi_device *device)
return;
}
if (!device->clocks_enabled) {
- dprintk(VIDC_DBG, "Clocks are already disabled");
+ dprintk(VIDC_DBG, "Clocks are already disabled\n");
goto already_disabled;
}
/*SYS Idle should be last message so mask any further interrupts
@@ -1773,6 +1802,7 @@ static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device)
"%s : Clock enable failed\n", __func__);
goto err_clk_gating_off;
}
+
intr_status = venus_hfi_read_register(
device,
VIDC_WRAPPER_INTR_STATUS);
@@ -1782,19 +1812,20 @@ static void venus_hfi_core_clear_interrupt(struct venus_hfi_device *device)
(intr_status &
VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK)) {
device->intr_status |= intr_status;
- dprintk(VIDC_DBG, "INTERRUPT for device: 0x%x: "
- "times: %d interrupt_status: %d",
- (u32) device, ++device->reg_count, intr_status);
+ dprintk(VIDC_DBG,
+ "INTERRUPT for device: 0x%x: times: %d interrupt_status: %d\n",
+ (u32)device, ++device->reg_count, intr_status);
} else {
- dprintk(VIDC_INFO, "SPURIOUS_INTR for device: 0x%x: "
- "times: %d interrupt_status: %d",
- (u32) device, ++device->spur_count, intr_status);
+ dprintk(VIDC_INFO,
+ "SPURIOUS_INTR for device: 0x%x: times: %d interrupt_status: %d\n",
+ (u32)device, ++device->spur_count, intr_status);
}
+
venus_hfi_write_register(device,
VIDC_CPU_CS_A2HSOFTINTCLR, 1, 0);
venus_hfi_write_register(device,
VIDC_WRAPPER_INTR_CLEAR, intr_status, 0);
- dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt");
+ dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt\n");
err_clk_gating_off:
mutex_unlock(&device->clk_pwr_lock);
mutex_unlock(&device->write_lock);
@@ -1809,7 +1840,7 @@ static int venus_hfi_core_set_resource(void *device,
struct venus_hfi_device *dev;
if (!device || !resource_hdr || !resource_value) {
- dprintk(VIDC_ERR, "set_res: Invalid Params");
+ dprintk(VIDC_ERR, "set_res: Invalid Params\n");
return -EINVAL;
} else {
dev = device;
@@ -1820,7 +1851,7 @@ static int venus_hfi_core_set_resource(void *device,
rc = create_pkt_set_cmd_sys_resource(pkt, resource_hdr,
resource_value);
if (rc) {
- dprintk(VIDC_ERR, "set_res: failed to create packet");
+ dprintk(VIDC_ERR, "set_res: failed to create packet\n");
goto err_create_pkt;
}
if (venus_hfi_iface_cmdq_write(dev, pkt))
@@ -1838,7 +1869,7 @@ static int venus_hfi_core_release_resource(void *device,
struct venus_hfi_device *dev;
if (!device || !resource_hdr) {
- dprintk(VIDC_ERR, "Inv-Params in rel_res");
+ dprintk(VIDC_ERR, "Inv-Params in rel_res\n");
return -EINVAL;
} else {
dev = device;
@@ -1846,7 +1877,7 @@ static int venus_hfi_core_release_resource(void *device,
rc = create_pkt_cmd_sys_release_resource(&pkt, resource_hdr);
if (rc) {
- dprintk(VIDC_ERR, "release_res: failed to create packet");
+ dprintk(VIDC_ERR, "release_res: failed to create packet\n");
goto err_create_pkt;
}
@@ -1866,13 +1897,13 @@ static int venus_hfi_core_ping(void *device)
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return -ENODEV;
}
rc = create_pkt_cmd_sys_ping(&pkt);
if (rc) {
- dprintk(VIDC_ERR, "core_ping: failed to create packet");
+ dprintk(VIDC_ERR, "core_ping: failed to create packet\n");
goto err_create_pkt;
}
@@ -1893,13 +1924,13 @@ static int venus_hfi_core_trigger_ssr(void *device,
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return -ENODEV;
}
rc = create_pkt_ssr_cmd(type, &pkt);
if (rc) {
- dprintk(VIDC_ERR, "core_ping: failed to create packet");
+ dprintk(VIDC_ERR, "core_ping: failed to create packet\n");
goto err_create_pkt;
}
@@ -1920,17 +1951,17 @@ static int venus_hfi_session_set_property(void *sess,
int rc = 0;
if (!sess || !pdata) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
}
- dprintk(VIDC_INFO, "in set_prop,with prop id: 0x%x", ptype);
+ dprintk(VIDC_INFO, "in set_prop,with prop id: 0x%x\n", ptype);
if (create_pkt_cmd_session_set_property(pkt, (u32)session, ptype,
pdata)) {
- dprintk(VIDC_ERR, "set property: failed to create packet");
+ dprintk(VIDC_ERR, "set property: failed to create packet\n");
return -EINVAL;
}
@@ -1947,17 +1978,17 @@ static int venus_hfi_session_get_property(void *sess,
struct hal_session *session;
int rc = 0;
if (!sess) {
- dprintk(VIDC_ERR, "Invalid Params in ");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
}
- dprintk(VIDC_INFO, "%s: property id: %d", __func__, ptype);
+ dprintk(VIDC_INFO, "%s: property id: %d\n", __func__, ptype);
rc = create_pkt_cmd_session_get_property(
&pkt, (u32)session, ptype);
if (rc) {
- dprintk(VIDC_ERR, "get property profile: pkt failed");
+ dprintk(VIDC_ERR, "get property profile: pkt failed\n");
goto err_create_pkt;
}
if (venus_hfi_iface_cmdq_write(session->device, &pkt)) {
@@ -1989,7 +2020,7 @@ static void *venus_hfi_session_init(void *device, u32 session_id,
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return NULL;
}
@@ -2010,7 +2041,7 @@ static void *venus_hfi_session_init(void *device, u32 session_id,
if (create_pkt_cmd_sys_session_init(&pkt, (u32)new_session,
session_type, codec_type)) {
- dprintk(VIDC_ERR, "session_init: failed to create packet");
+ dprintk(VIDC_ERR, "session_init: failed to create packet\n");
goto err_session_init_fail;
}
@@ -2039,7 +2070,7 @@ static int venus_hfi_send_session_cmd(void *session_id,
rc = create_pkt_cmd_session_cmd(&pkt, pkt_type, (u32)session);
if (rc) {
- dprintk(VIDC_ERR, "send session cmd: create pkt failed");
+ dprintk(VIDC_ERR, "send session cmd: create pkt failed\n");
goto err_create_pkt;
}
@@ -2066,11 +2097,11 @@ static int venus_hfi_session_clean(void *session)
{
struct hal_session *sess_close;
if (!session) {
- dprintk(VIDC_ERR, "Invalid Params %s", __func__);
+ dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
return -EINVAL;
}
sess_close = session;
- dprintk(VIDC_DBG, "deleted the session: 0x%p",
+ dprintk(VIDC_DBG, "deleted the session: 0x%p\n",
sess_close);
mutex_lock(&((struct venus_hfi_device *)
sess_close->device)->session_lock);
@@ -2090,7 +2121,7 @@ static int venus_hfi_session_set_buffers(void *sess,
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2104,11 +2135,11 @@ static int venus_hfi_session_set_buffers(void *sess,
rc = create_pkt_cmd_session_set_buffers(pkt,
(u32)session, buffer_info);
if (rc) {
- dprintk(VIDC_ERR, "set buffers: failed to create packet");
+ dprintk(VIDC_ERR, "set buffers: failed to create packet\n");
goto err_create_pkt;
}
- dprintk(VIDC_INFO, "set buffers: 0x%x", buffer_info->buffer_type);
+ dprintk(VIDC_INFO, "set buffers: 0x%x\n", buffer_info->buffer_type);
if (venus_hfi_iface_cmdq_write(session->device, pkt))
rc = -ENOTEMPTY;
err_create_pkt:
@@ -2124,7 +2155,7 @@ static int venus_hfi_session_release_buffers(void *sess,
struct hal_session *session;
if (!sess || !buffer_info) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2138,11 +2169,11 @@ static int venus_hfi_session_release_buffers(void *sess,
rc = create_pkt_cmd_session_release_buffers(pkt,
(u32)session, buffer_info);
if (rc) {
- dprintk(VIDC_ERR, "release buffers: failed to create packet");
+ dprintk(VIDC_ERR, "release buffers: failed to create packet\n");
goto err_create_pkt;
}
- dprintk(VIDC_INFO, "Release buffers: 0x%x", buffer_info->buffer_type);
+ dprintk(VIDC_INFO, "Release buffers: 0x%x\n", buffer_info->buffer_type);
if (venus_hfi_iface_cmdq_write(session->device, pkt))
rc = -ENOTEMPTY;
err_create_pkt:
@@ -2192,7 +2223,7 @@ static int venus_hfi_session_etb(void *sess,
struct hal_session *session;
if (!sess || !input_frame) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2205,10 +2236,10 @@ static int venus_hfi_session_etb(void *sess,
(u32)session, input_frame);
if (rc) {
dprintk(VIDC_ERR,
- "Session etb decoder: failed to create pkt");
+ "Session etb decoder: failed to create pkt\n");
goto err_create_pkt;
}
- dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER");
+ dprintk(VIDC_DBG, "Q DECODER INPUT BUFFER\n");
if (venus_hfi_iface_cmdq_write(session->device, &pkt))
rc = -ENOTEMPTY;
} else {
@@ -2219,10 +2250,10 @@ static int venus_hfi_session_etb(void *sess,
(u32)session, input_frame);
if (rc) {
dprintk(VIDC_ERR,
- "Session etb encoder: failed to create pkt");
+ "Session etb encoder: failed to create pkt\n");
goto err_create_pkt;
}
- dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER");
+ dprintk(VIDC_DBG, "Q ENCODER INPUT BUFFER\n");
if (venus_hfi_iface_cmdq_write(session->device, &pkt))
rc = -ENOTEMPTY;
}
@@ -2238,7 +2269,7 @@ static int venus_hfi_session_ftb(void *sess,
struct hal_session *session;
if (!sess || !output_frame) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2246,7 +2277,7 @@ static int venus_hfi_session_ftb(void *sess,
rc = create_pkt_cmd_session_ftb(&pkt, (u32)session, output_frame);
if (rc) {
- dprintk(VIDC_ERR, "Session ftb: failed to create pkt");
+ dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n");
goto err_create_pkt;
}
@@ -2265,7 +2296,7 @@ static int venus_hfi_session_parse_seq_hdr(void *sess,
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2277,7 +2308,7 @@ static int venus_hfi_session_parse_seq_hdr(void *sess,
seq_hdr);
if (rc) {
dprintk(VIDC_ERR,
- "Session parse seq hdr: failed to create pkt");
+ "Session parse seq hdr: failed to create pkt\n");
goto err_create_pkt;
}
@@ -2296,7 +2327,7 @@ static int venus_hfi_session_get_seq_hdr(void *sess,
struct hal_session *session;
if (!sess || !seq_hdr) {
- dprintk(VIDC_ERR, "Invalid Params");
+ dprintk(VIDC_ERR, "Invalid Params\n");
return -EINVAL;
} else {
session = sess;
@@ -2305,7 +2336,8 @@ static int venus_hfi_session_get_seq_hdr(void *sess,
pkt = (struct hfi_cmd_session_get_sequence_header_packet *) packet;
rc = create_pkt_cmd_session_get_seq_hdr(pkt, (u32)session, seq_hdr);
if (rc) {
- dprintk(VIDC_ERR, "Session get seq hdr: failed to create pkt");
+ dprintk(VIDC_ERR,
+ "Session get seq hdr: failed to create pkt\n");
goto err_create_pkt;
}
@@ -2330,7 +2362,8 @@ static int venus_hfi_session_get_buf_req(void *sess)
rc = create_pkt_cmd_session_get_buf_req(&pkt, (u32)session);
if (rc) {
- dprintk(VIDC_ERR, "Session get buf req: failed to create pkt");
+ dprintk(VIDC_ERR,
+ "Session get buf req: failed to create pkt\n");
goto err_create_pkt;
}
@@ -2355,7 +2388,7 @@ static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode)
rc = create_pkt_cmd_session_flush(&pkt, (u32)session, flush_mode);
if (rc) {
- dprintk(VIDC_ERR, "Session flush: failed to create pkt");
+ dprintk(VIDC_ERR, "Session flush: failed to create pkt\n");
goto err_create_pkt;
}
@@ -2405,12 +2438,12 @@ static int venus_hfi_check_core_registered(
FIRMWARE_SIZE))) {
return 0;
} else {
- dprintk(VIDC_INFO, "Device not registered");
+ dprintk(VIDC_INFO, "Device not registered\n");
return -EINVAL;
}
}
} else {
- dprintk(VIDC_INFO, "no device Registered");
+ dprintk(VIDC_INFO, "no device Registered\n");
}
return -EINVAL;
}
@@ -2433,13 +2466,13 @@ static int venus_hfi_core_pc_prep(void *device)
if (device) {
dev = device;
} else {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return -ENODEV;
}
rc = create_pkt_cmd_sys_pc_prep(&pkt);
if (rc) {
- dprintk(VIDC_ERR, "Failed to create sys pc prep pkt");
+ dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n");
goto err_create_pkt;
}
@@ -2467,26 +2500,26 @@ static void venus_hfi_pm_hndlr(struct work_struct *work)
init_completion(&pc_prep_done);
rc = venus_hfi_core_pc_prep(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to prepare venus for power off");
+ dprintk(VIDC_ERR, "Failed to prepare venus for power off\n");
return;
}
rc = wait_for_completion_timeout(&pc_prep_done,
msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
if (!rc) {
- dprintk(VIDC_ERR, "Wait interrupted or timeout: %d", rc);
+ dprintk(VIDC_ERR, "Wait interrupted or timeout: %d\n", rc);
return;
}
mutex_lock(&device->clk_pwr_lock);
if (device->clocks_enabled) {
dprintk(VIDC_ERR,
- "Clocks are still enabled after PC_PREP_DONE, ignore power off");
+ "Clocks are still enabled after PC_PREP_DONE, ignore power off\n");
goto clks_enabled;
}
rc = venus_hfi_power_off(device);
if (rc)
- dprintk(VIDC_ERR, "Failed venus power off");
+ dprintk(VIDC_ERR, "Failed venus power off\n");
clks_enabled:
mutex_unlock(&device->clk_pwr_lock);
}
@@ -2496,7 +2529,7 @@ static int venus_hfi_try_clk_gating(struct venus_hfi_device *device)
int rc = 0;
u32 ctrl_status = 0;
if (!device) {
- dprintk(VIDC_ERR, "invalid device");
+ dprintk(VIDC_ERR, "invalid device\n");
return -ENODEV;
}
mutex_lock(&device->write_lock);
@@ -2506,14 +2539,14 @@ static int venus_hfi_try_clk_gating(struct venus_hfi_device *device)
device,
VIDC_CPU_CS_SCIACMDARG0);
dprintk(VIDC_DBG,
- "venus_hfi_try_clk_gating - rc %d, ctrl_status 0x%x",
+ "venus_hfi_try_clk_gating - rc %d, ctrl_status 0x%x\n",
rc, ctrl_status);
if (((ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK)
|| (ctrl_status & VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY))
&& !rc)
venus_hfi_clk_gating_on(device);
else
- dprintk(VIDC_DBG, "Ignore clock gating");
+ dprintk(VIDC_DBG, "Ignore clock gating\n");
mutex_unlock(&device->clk_pwr_lock);
mutex_unlock(&device->write_lock);
return rc;
@@ -2533,7 +2566,7 @@ static void venus_hfi_process_msg_event_notify(
vsfr = (struct hfi_sfr_struct *)
device->sfr.align_virtual_addr;
if (vsfr)
- dprintk(VIDC_ERR, "SFR Message from FW : %s",
+ dprintk(VIDC_ERR, "SFR Message from FW : %s\n",
vsfr->rg_data);
}
}
@@ -2546,13 +2579,13 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device)
if (device) {
if ((device->intr_status &
VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK)) {
- dprintk(VIDC_ERR, "Received: Watchdog timeout %s",
+ dprintk(VIDC_ERR, "Received: Watchdog timeout %s\n",
__func__);
vsfr = (struct hfi_sfr_struct *)
device->sfr.align_virtual_addr;
if (vsfr)
dprintk(VIDC_ERR,
- "SFR Message from FW : %s",
+ "SFR Message from FW : %s\n",
vsfr->rg_data);
venus_hfi_process_sys_watchdog_timeout(device);
}
@@ -2569,7 +2602,7 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device)
while (!venus_hfi_iface_dbgq_read(device, packet)) {
struct hfi_msg_sys_debug_packet *pkt =
(struct hfi_msg_sys_debug_packet *) packet;
- dprintk(VIDC_FW, "FW-SAYS: %s", pkt->rg_msg_data);
+ dprintk(VIDC_FW, "%s", pkt->rg_msg_data);
}
if (rc == HFI_MSG_SYS_IDLE) {
dprintk(VIDC_DBG, "Received HFI_MSG_SYS_IDLE\n");
@@ -2580,11 +2613,11 @@ static void venus_hfi_response_handler(struct venus_hfi_device *device)
rc = venus_hfi_try_clk_gating(device);
if (rc)
dprintk(VIDC_ERR,
- "Failed clk gating after PC_PREP_DONE");
+ "Failed clk gating after PC_PREP_DONE\n");
complete(&pc_prep_done);
}
} else {
- dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT");
+ dprintk(VIDC_ERR, "SPURIOUS_INTERRUPT\n");
}
}
@@ -2593,7 +2626,7 @@ static void venus_hfi_core_work_handler(struct work_struct *work)
struct venus_hfi_device *device = list_first_entry(
&hal_ctxt.dev_head, struct venus_hfi_device, list);
- dprintk(VIDC_INFO, " GOT INTERRUPT () ");
+ dprintk(VIDC_INFO, "GOT INTERRUPT\n");
if (!device->callback) {
dprintk(VIDC_ERR, "No interrupt callback function: %p\n",
device);
@@ -2609,10 +2642,9 @@ static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler);
static irqreturn_t venus_hfi_isr(int irq, void *dev)
{
struct venus_hfi_device *device = dev;
- dprintk(VIDC_INFO, "vidc_hal_isr() %d ", irq);
+ dprintk(VIDC_INFO, "vidc_hal_isr %d\n", irq);
disable_irq_nosync(irq);
queue_work(device->vidc_workq, &venus_hfi_work);
- dprintk(VIDC_INFO, "vidc_hal_isr() %d ", irq);
return IRQ_HANDLED;
}
@@ -2632,16 +2664,16 @@ static int venus_hfi_init_regs_and_interrupts(
device->register_base, device->register_size,
device->irq);
if (!rc) {
- dprintk(VIDC_ERR, "Core present/Already added");
+ dprintk(VIDC_ERR, "Core present/Already added\n");
rc = -EEXIST;
goto err_core_init;
}
- dprintk(VIDC_DBG, "HAL_DATA will be assigned now");
+ dprintk(VIDC_DBG, "HAL_DATA will be assigned now\n");
hal = (struct hal_data *)
kzalloc(sizeof(struct hal_data), GFP_KERNEL);
if (!hal) {
- dprintk(VIDC_ERR, "Failed to alloc");
+ dprintk(VIDC_ERR, "Failed to alloc\n");
rc = -ENOMEM;
goto err_core_init;
}
@@ -2651,7 +2683,7 @@ static int venus_hfi_init_regs_and_interrupts(
ioremap_nocache(device->register_base, device->register_size);
if (!hal->register_base_addr) {
dprintk(VIDC_ERR,
- "could not map reg addr %d of size %d",
+ "could not map reg addr %d of size %d\n",
device->register_base, device->register_size);
goto error_irq_fail;
}
@@ -2712,7 +2744,7 @@ static inline int venus_hfi_init_clocks(struct msm_vidc_platform_resources *res,
venus_hfi_for_each_clock(device, cl, {
if (!strcmp(cl->name, "mem_clk") && !res->has_ocmem) {
dprintk(VIDC_ERR,
- "Found %s on a target that doesn't support ocmem",
+ "Found %s on a target that doesn't support ocmem\n",
cl->name);
rc = -ENOENT;
goto err_found_bad_ocmem;
@@ -2767,7 +2799,7 @@ static inline void venus_hfi_disable_clks(struct venus_hfi_device *device)
venus_hfi_for_each_clock(device, cl, {
if (!device->clocks_enabled && cl->has_sw_power_collapse) {
dprintk(VIDC_DBG,
- "Omitting clk_disable of %s in %s as it's already disabled",
+ "Omitting clk_disable of %s in %s as it's already disabled\n",
cl->name, __func__);
clk_unprepare(cl->clk);
} else {
@@ -2841,7 +2873,7 @@ static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device,
domain = iommu_group_get_iommudata(iommu_map->group);
if (!domain) {
dprintk(VIDC_ERR,
- "Failed to get domain data for group %p",
+ "Failed to get domain data for group %p\n",
iommu_map->group);
rc = -EINVAL;
goto fail_group;
@@ -2849,7 +2881,7 @@ static int venus_hfi_register_iommu_domains(struct venus_hfi_device *device,
iommu_map->domain = msm_find_domain_no(domain);
if (iommu_map->domain < 0) {
dprintk(VIDC_ERR,
- "Failed to get domain index for domain %p",
+ "Failed to get domain index for domain %p\n",
domain);
rc = -EINVAL;
goto fail_group;
@@ -2999,7 +3031,7 @@ static int venus_hfi_unset_ocmem(void *dev)
goto ocmem_unset_failed;
}
if (!device->resources.ocmem.buf) {
- dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set",
+ dprintk(VIDC_INFO, "%s Trying to free OCMEM which is not set\n",
__func__);
rc = -EINVAL;
goto ocmem_unset_failed;
@@ -3100,7 +3132,7 @@ static int venus_hfi_free_ocmem(void *dev)
int rc = 0;
if (!device) {
- dprintk(VIDC_ERR, "%s invalid device handle %p",
+ dprintk(VIDC_ERR, "%s invalid device handle %p\n",
__func__, device);
return -EINVAL;
}
@@ -3409,14 +3441,14 @@ static int venus_hfi_load_fw(void *dev)
mutex_init(&device->clk_pwr_lock);
rc = venus_hfi_iommu_attach(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to attach iommu");
+ dprintk(VIDC_ERR, "Failed to attach iommu\n");
goto fail_iommu_attach;
}
mutex_lock(&device->clk_pwr_lock);
rc = venus_hfi_enable_regulators(device);
if (rc) {
- dprintk(VIDC_ERR, "Failed to enable GDSC %d", rc);
+ dprintk(VIDC_ERR, "Failed to enable GDSC %d\n", rc);
mutex_unlock(&device->clk_pwr_lock);
goto fail_enable_gdsc;
}
@@ -3527,7 +3559,7 @@ static int venus_hfi_get_fw_info(void *dev, enum fw_info info)
break;
default:
- dprintk(VIDC_ERR, "Invalid fw info requested");
+ dprintk(VIDC_ERR, "Invalid fw info requested\n");
}
return rc;
}
@@ -3582,13 +3614,9 @@ int venus_hfi_capability_check(u32 fourcc, u32 width,
return -EINVAL;
}
- if (msm_vp8_low_tier && fourcc == V4L2_PIX_FMT_VP8) {
- *max_width = DEFAULT_WIDTH;
- *max_height = DEFAULT_HEIGHT;
- }
if (width > *max_width) {
dprintk(VIDC_ERR,
- "Unsupported width = %u supported max width = %u",
+ "Unsupported width = %u supported max width = %u\n",
width, *max_width);
rc = -ENOTSUPP;
}
@@ -3602,17 +3630,17 @@ static void *venus_hfi_add_device(u32 device_id,
struct venus_hfi_device *hdevice = NULL;
int rc = 0;
- if (device_id || !res || !callback) {
- dprintk(VIDC_ERR, "Invalid Paramters");
+ if (!res || !callback) {
+ dprintk(VIDC_ERR, "Invalid Parameters\n");
return NULL;
}
- dprintk(VIDC_INFO, "entered , device_id: %d", device_id);
+ dprintk(VIDC_INFO, "entered , device_id: %d\n", device_id);
hdevice = (struct venus_hfi_device *)
kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL);
if (!hdevice) {
- dprintk(VIDC_ERR, "failed to allocate new device");
+ dprintk(VIDC_ERR, "failed to allocate new device\n");
goto err_alloc;
}
@@ -3766,6 +3794,7 @@ int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
if (IS_ERR_OR_NULL(hdev->hfi_device_data)) {
rc = PTR_ERR(hdev->hfi_device_data);
+ rc = !rc ? -EINVAL : rc;
goto err_venus_hfi_init;
}
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
index 9a2dc4802bff..f79d43c20304 100644
--- a/drivers/media/platform/msm/vidc/vidc_hfi_api.h
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -43,6 +43,7 @@
#define HAL_BUFFERFLAG_READONLY 0x00000200
#define HAL_BUFFERFLAG_ENDOFSUBFRAME 0x00000400
#define HAL_BUFFERFLAG_EOSEQ 0x00200000
+#define HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP 0x10000000
#define HAL_BUFFERFLAG_DROP_FRAME 0x20000000
#define HAL_BUFFERFLAG_TS_DISCONTINUITY 0x40000000
#define HAL_BUFFERFLAG_TS_ERROR 0x80000000
diff --git a/drivers/media/platform/msm/vpu/vpu_hfi.c b/drivers/media/platform/msm/vpu/vpu_hfi.c
index 7485df490edf..7b9b49c7a868 100644
--- a/drivers/media/platform/msm/vpu/vpu_hfi.c
+++ b/drivers/media/platform/msm/vpu/vpu_hfi.c
@@ -128,9 +128,23 @@ struct vpu_hfi_device {
struct vpu_platform_resources *platform_resouce;
};
+struct addr_range {
+ u32 start;
+ u32 end;
+};
+
/* global */
static struct vpu_hfi_device g_hfi_device;
+static struct addr_range csr_skip_addrs[] = {
+ /* start and end offsets of inaccessible address ranges */
+ { 0x0000, 0x000F },
+ { 0x0018, 0x001B },
+ { 0x0020, 0x0037 },
+ { 0x00C0, 0x00DF },
+ { 0x01A0, 0x01AF },
+};
+
/*
* write a packet into the IPC memory
* caller needs to lock the queue
@@ -422,9 +436,9 @@ static int vpu_hfi_queues_init(struct vpu_hfi_device *dev, void *start_addr,
(u32) dev->mem_base;
dev->rxqs[i].q_data_size = vpu_hfi_q_size(RX_Q_IDX_TO_Q_ID(i));
dev->rxqs[i].state = HFI_QUEUE_STATE_DISABLED;
- vpu_hfi_init_qhdr(dev->txqs[i].q_hdr, false,
- dev->txqs[i].q_data_offset,
- dev->txqs[i].q_data_size);
+ vpu_hfi_init_qhdr(dev->rxqs[i].q_hdr, false,
+ dev->rxqs[i].q_data_offset,
+ dev->rxqs[i].q_data_size);
qmem_size_sum += vpu_hfi_q_size(RX_Q_IDX_TO_Q_ID(i));
mutex_init(&dev->rxqs[i].lock);
@@ -519,7 +533,7 @@ static int vpu_hfi_boot(struct vpu_hfi_device *hdevice)
/* wait for VPU FW up (poll status register) */
timeout = vpu_pil_timeout/20;
- while ((raw_hfi_status_read((u32)(hdevice->reg_base)) & 1) == 0) {
+ while (!raw_hfi_fw_ready((u32)hdevice->reg_base)) {
if (timeout-- <= 0) {
/* FW bootup timed out */
pr_err("VPU FW bootup timeout\n");
@@ -791,9 +805,9 @@ void vpu_hfi_stop(void)
free_irq(hdevice->irq_wd, hdevice);
if (!hdevice->watchdog_bited) {
- if (!raw_hfi_fw_halted((u32)(hdevice->reg_base))) {
+ if (!raw_hfi_fw_halted((u32)hdevice->reg_base)) {
msleep(20);
- if (!raw_hfi_fw_halted((u32)(hdevice->reg_base)))
+ if (!raw_hfi_fw_halted((u32)hdevice->reg_base))
pr_warn("firmware not halted!\n");
}
}
@@ -1144,15 +1158,48 @@ size_t vpu_hfi_print_queues(char *buf, size_t buf_size)
return strlcat(buf, "", buf_size);
}
-
int vpu_hfi_dump_csr_regs(char *buf, size_t buf_size)
{
struct vpu_hfi_device *hdevice = &g_hfi_device;
u32 v_base = (u32)(hdevice->reg_base);
u32 p_base = (u32)(hdevice->platform_resouce->register_base_phy);
+ u32 last_addr = v_base +
+ csr_skip_addrs[ARRAY_SIZE(csr_skip_addrs) - 1].end;
+ u32 addr;
+ char temp[32];
+ int i = 0, skip = 0, temp_size = 32;
strlcpy(buf, "", buf_size);
- raw_hfi_dump_csr_regs(v_base, p_base, buf, buf_size);
+
+ /* read one at a time. Print 4 registers per line */
+ for (addr = v_base; addr <= last_addr; addr += sizeof(u32)) {
+ if ((addr % 0x10) == 0) {
+ snprintf(temp, temp_size, "@0x%08x -",
+ (addr - v_base + p_base));
+ strlcat(buf, temp, buf_size);
+ }
+
+ if ((addr - v_base) >= csr_skip_addrs[i].start &&
+ (addr - v_base) <= csr_skip_addrs[i].end) {
+ skip = 1;
+ snprintf(temp, temp_size, " xxxxxxxxxx");
+ strlcat(buf, temp, buf_size);
+ } else {
+ if (skip) {
+ i++;
+ skip = 0;
+ }
+
+ snprintf(temp, temp_size, " 0x%08x",
+ readl_relaxed(addr + 0 * sizeof(u32)));
+ strlcat(buf, temp, buf_size);
+ }
+
+ if ((addr % 0x10) == 0xc) {
+ snprintf(temp, temp_size, "\n");
+ strlcat(buf, temp, buf_size);
+ }
+ }
return strlcat(buf, "\n", buf_size);
}
diff --git a/drivers/media/platform/msm/vpu/vpu_hfi_intf.h b/drivers/media/platform/msm/vpu/vpu_hfi_intf.h
index ab0621eadcc2..046c54b21233 100644
--- a/drivers/media/platform/msm/vpu/vpu_hfi_intf.h
+++ b/drivers/media/platform/msm/vpu/vpu_hfi_intf.h
@@ -44,10 +44,9 @@ struct hfi_queue_table_header {
#define VPU_MAX_QUEUE_NUM (VPU_MAX_TXQ_NUM + VPU_MAX_RXQ_NUM)
/* qhdr_q_size in bytes */
-#define VPU_SYS_QUEUE_SIZE 1024
-#define VPU_SESSION_QUEUE_SIZE 2048
-#define VPU_LOGGING_QUEUE_SIZE 2048 /* assume this size */
-#define VPU_TUNE_QUEUE_SIZE 20480
+#define VPU_SYS_QUEUE_SIZE SZ_1K
+#define VPU_SESSION_QUEUE_SIZE SZ_2K
+#define VPU_LOGGING_QUEUE_SIZE SZ_64K
#define TX_Q_IDX_TO_Q_ID(idx) (VPU_SYSTEM_CMD_QUEUE_ID + (idx * 2))
#define RX_Q_IDX_TO_Q_ID(idx) (VPU_SYSTEM_MSG_QUEUE_ID + (idx * 2))
@@ -146,8 +145,6 @@ static inline u32 vpu_hfi_q_size(int q_id)
* VPU CSR register offsets
*/
-/* A5 control */
-#define VPU_CSR_A5_CONTROL 0x018
/* fw -> apps sgi interrupt */
#define VPU_CSR_APPS_SGI_STS 0x050
#define VPU_CSR_APPS_SGI_CLR 0x054
@@ -226,43 +223,18 @@ static inline u32 raw_hfi_status_read(u32 regbase)
return val;
}
-static inline bool raw_hfi_fw_halted(u32 regbase)
+static inline bool raw_hfi_fw_ready(u32 regbase)
{
- u32 val;
+ u32 val = raw_hfi_status_read(regbase);
- val = readl_relaxed(regbase + VPU_CSR_A5_CONTROL);
-
- /* bit 7 is Firmware WFI status */
- if (val & (1 << 7))
- return true;
- else
- return false;
+ return (val & 0x1) ? true : false;
}
-#define add2buf(dest, dest_size, temp, temp_size, __fmt, arg...) \
- do { \
- snprintf(temp, temp_size, __fmt, ## arg); \
- strlcat(dest, temp, dest_size); \
- } while (0)
-
-static inline void raw_hfi_dump_csr_regs(u32 v_base, u32 p_base,
- char *buf, size_t buf_size)
+static inline bool raw_hfi_fw_halted(u32 regbase)
{
- /* temporary buffer */
- size_t t_s = SZ_128;
- char t[t_s];
- u32 addr;
- u32 last_addr = v_base + VPU_HW_VERSION;
-
- /* read each register (4 per line) */
- for (addr = v_base; addr <= last_addr; addr += 4 * sizeof(u32))
- add2buf(buf, buf_size, t, t_s,
- "@0x%08x - 0x%08x 0x%08x 0x%08x 0x%08x\n",
- (addr - v_base + p_base),
- readl_relaxed(addr + 0 * sizeof(u32)),
- readl_relaxed(addr + 1 * sizeof(u32)),
- readl_relaxed(addr + 2 * sizeof(u32)),
- readl_relaxed(addr + 3 * sizeof(u32)));
+ u32 val = raw_hfi_status_read(regbase);
+
+ return (val & 0x2) ? true : false;
}
#endif /* __H_VPU_HFI_INTF_H__ */
diff --git a/drivers/media/platform/msm/vpu/vpu_resources.c b/drivers/media/platform/msm/vpu/vpu_resources.c
index d770e72604f6..520d75d08f54 100644
--- a/drivers/media/platform/msm/vpu/vpu_resources.c
+++ b/drivers/media/platform/msm/vpu/vpu_resources.c
@@ -79,9 +79,9 @@ static struct vpu_iommu_map iommus_array[VPU_MAX_IOMMU_DOMAIN] = {
};
static const char * const clk_table_dt_entries[] = {
- [VPU_BUS_CLK] = "qti,bus-clk-load-freq-tbl",
- [VPU_MAPLE_CLK] = "qti,maple-clk-load-freq-tbl",
- [VPU_VDP_CLK] = "qti,vdp-clk-load-freq-tbl",
+ [VPU_BUS_CLK] = "qcom,bus-clk-load-freq-tbl",
+ [VPU_MAPLE_CLK] = "qcom,maple-clk-load-freq-tbl",
+ [VPU_VDP_CLK] = "qcom,vdp-clk-load-freq-tbl",
};
struct bus_pdata_config {
@@ -93,7 +93,7 @@ struct bus_pdata_config {
static struct bus_pdata_config bus_pdata_config = {
.masters = {MSM_BUS_MASTER_VPU, MSM_BUS_MASTER_VPU},
.slaves = {MSM_BUS_SLAVE_EBI_CH0, MSM_BUS_SLAVE_EBI_CH0},
- .name = "qti,bus-load-vector-tbl",
+ .name = "qcom,bus-load-vector-tbl",
};
static int __get_u32_array_num_elements(struct platform_device *pdev,
@@ -267,7 +267,7 @@ static int __vpu_load_iommu_maps(struct vpu_platform_resources *res)
u32 count = 0;
num_elements = of_property_count_strings(pdev->dev.of_node,
- "qti,enabled-iommu-maps");
+ "qcom,enabled-iommu-maps");
if (num_elements <= 0) {
pr_warn("No list of IOMMUs to be enabled\n");
return ret;
@@ -278,7 +278,7 @@ static int __vpu_load_iommu_maps(struct vpu_platform_resources *res)
for (i = 0; i < num_elements; i++) {
ret = of_property_read_string_index(pdev->dev.of_node,
- "qti,enabled-iommu-maps", i, &name);
+ "qcom,enabled-iommu-maps", i, &name);
if (ret)
return ret;
diff --git a/drivers/media/platform/msm/vpu/vpu_v4l2.c b/drivers/media/platform/msm/vpu/vpu_v4l2.c
index 3f2aba7b8b48..7ce992614fd2 100644
--- a/drivers/media/platform/msm/vpu/vpu_v4l2.c
+++ b/drivers/media/platform/msm/vpu/vpu_v4l2.c
@@ -618,7 +618,7 @@ static int vpu_remove(struct platform_device *pdev)
static const struct of_device_id vpu_dt_match[] = {
- {.compatible = "qti,vpu"},
+ {.compatible = "qcom,vpu"},
{}
};
diff --git a/drivers/media/platform/msm/wfd/wfd-ioctl.c b/drivers/media/platform/msm/wfd/wfd-ioctl.c
index 26b526e05e92..40e12937a09c 100644
--- a/drivers/media/platform/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/platform/msm/wfd/wfd-ioctl.c
@@ -1425,6 +1425,7 @@ static struct vb2_mem_ops wfd_vb2_mem_ops = {
int wfd_initialize_vb2_queue(struct vb2_queue *q, void *priv)
{
+ q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
q->io_modes = VB2_USERPTR;
q->ops = &wfd_vidbuf_ops;
diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c
index 66f80973596b..ed79d7b78e7d 100644
--- a/drivers/mfd/db8500-prcmu.c
+++ b/drivers/mfd/db8500-prcmu.c
@@ -1724,9 +1724,9 @@ static long round_clock_rate(u8 clock, unsigned long rate)
/* CPU FREQ table, may be changed due to if MAX_OPP is supported. */
static struct cpufreq_frequency_table db8500_cpufreq_table[] = {
- { .frequency = 200000, .index = ARM_EXTCLK,},
- { .frequency = 400000, .index = ARM_50_OPP,},
- { .frequency = 800000, .index = ARM_100_OPP,},
+ { .frequency = 200000, .driver_data = ARM_EXTCLK,},
+ { .frequency = 400000, .driver_data = ARM_50_OPP,},
+ { .frequency = 800000, .driver_data = ARM_100_OPP,},
{ .frequency = CPUFREQ_TABLE_END,}, /* To be used for MAX_OPP. */
{ .frequency = CPUFREQ_TABLE_END,},
};
@@ -1901,7 +1901,7 @@ static int set_armss_rate(unsigned long rate)
return -EINVAL;
/* Set the new arm opp. */
- return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].index);
+ return db8500_prcmu_set_arm_opp(db8500_cpufreq_table[i].driver_data);
}
static int set_plldsi_rate(unsigned long rate)
@@ -3105,7 +3105,7 @@ static void db8500_prcmu_update_cpufreq(void)
{
if (prcmu_has_arm_maxopp()) {
db8500_cpufreq_table[3].frequency = 1000000;
- db8500_cpufreq_table[3].index = ARM_MAX_OPP;
+ db8500_cpufreq_table[3].driver_data = ARM_MAX_OPP;
}
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 872e9bed53f1..ae0174d09790 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -618,4 +618,5 @@ source "drivers/misc/carma/Kconfig"
source "drivers/misc/altera-stapl/Kconfig"
source "drivers/misc/mei/Kconfig"
source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/qcom/Kconfig"
endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7c1e40658af6..a9a93e5e24cf 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -62,3 +62,4 @@ obj-$(CONFIG_QFP_FUSE) += qfp_fuse.o
obj-$(CONFIG_TI_DRV2667) += ti_drv2667.o
obj-$(CONFIG_QPNP_MISC) += qpnp-misc.o
obj-$(CONFIG_APQ8084_DOCKING_STATION) += apq8084_dock.o
+obj-y += qcom/
diff --git a/drivers/misc/apq8084_dock.c b/drivers/misc/apq8084_dock.c
index bfd19baa84e3..fdf18602a77a 100644
--- a/drivers/misc/apq8084_dock.c
+++ b/drivers/misc/apq8084_dock.c
@@ -80,7 +80,7 @@ static int apq8084_dock_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, dock);
INIT_WORK(&dock->dock_work, dock_detected_work);
- dock->dock_detect = of_get_named_gpio(node, "qti,dock-detect-gpio", 0);
+ dock->dock_detect = of_get_named_gpio(node, "qcom,dock-detect-gpio", 0);
if (dock->dock_detect < 0) {
dev_err(dock->dev, "unable to get dock-detect-gpio\n");
return dock->dock_detect;
@@ -94,7 +94,7 @@ static int apq8084_dock_probe(struct platform_device *pdev)
return ret;
dock->dock_hub_reset = of_get_named_gpio(node,
- "qti,dock-hub-reset-gpio", 0);
+ "qcom,dock-hub-reset-gpio", 0);
if (dock->dock_hub_reset < 0) {
dev_err(dock->dev, "unable to get dock-hub-reset-gpio\n");
return dock->dock_hub_reset;
@@ -106,7 +106,7 @@ static int apq8084_dock_probe(struct platform_device *pdev)
return ret;
dock->dock_eth_reset = of_get_named_gpio(node,
- "qti,dock-eth-reset-gpio", 0);
+ "qcom,dock-eth-reset-gpio", 0);
if (dock->dock_eth_reset < 0) {
dev_err(dock->dev, "unable to get dock-eth-reset-gpio\n");
return dock->dock_eth_reset;
@@ -117,7 +117,7 @@ static int apq8084_dock_probe(struct platform_device *pdev)
if (ret)
return ret;
- dock->dock_enable = of_get_named_gpio(node, "qti,dock-enable-gpio", 0);
+ dock->dock_enable = of_get_named_gpio(node, "qcom,dock-enable-gpio", 0);
if (dock->dock_enable < 0) {
dev_err(dock->dev, "unable to get dock-enable-gpio\n");
return dock->dock_enable;
@@ -145,7 +145,7 @@ static int apq8084_dock_remove(struct platform_device *pdev)
}
static struct of_device_id of_match_table[] = {
- { .compatible = "qti,apq8084-dock",
+ { .compatible = "qcom,apq8084-dock",
}
};
diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig
new file mode 100644
index 000000000000..237854828a2d
--- /dev/null
+++ b/drivers/misc/qcom/Kconfig
@@ -0,0 +1,8 @@
+config MSM_QDSP6V2_CODECS
+ bool "Audio QDSP6V2 APR support"
+ depends on MSM_SMD
+ help
+ Enable Audio codecs with APR IPC protocol support between
+ application processor and QDSP6 for B-family. APR is
+ used by audio driver to configure QDSP6's
+ ASM, ADM and AFE.
diff --git a/drivers/misc/qcom/Makefile b/drivers/misc/qcom/Makefile
new file mode 100644
index 000000000000..120bdddcbc84
--- /dev/null
+++ b/drivers/misc/qcom/Makefile
@@ -0,0 +1 @@
+obj-y += qdsp6v2/
diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/drivers/misc/qcom/qdsp6v2/Makefile
new file mode 100644
index 000000000000..26f3c404e705
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o audio_utils.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_utils_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o
diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c
new file mode 100644
index 000000000000..14dc7348e0b3
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/aac_in.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_aac.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 5 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((1536+sizeof(struct meta_out_dsp)) * 5))
+
+#define AAC_FORMAT_ADTS 65535
+
+/* ------------------- device --------------------- */
+static long aac_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_aac_enc_config *enc_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t aac_mode = AAC_ENC_MODE_AAC_LC;
+
+ enc_cfg = audio->enc_cfg;
+ aac_config = audio->codec_cfg;
+ /* ENCODE CFG (after new set of API's are published )bharath*/
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+
+ if (audio->opened) {
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ } else {
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ pr_debug("%s: starting in non_tunnel mode",
+ __func__);
+ rc = q6asm_open_read_write(audio->ac,
+ FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:open read write failed\n",
+ __func__);
+ break;
+ }
+ }
+ if (audio->feedback == TUNNEL_MODE) {
+ pr_debug("%s: starting in tunnel mode",
+ __func__);
+ rc = q6asm_open_read(audio->ac,
+ FORMAT_MPEG4_AAC);
+
+ if (rc < 0) {
+ pr_err("%s:open read failed\n",
+ __func__);
+ break;
+ }
+ }
+ audio->stopped = 0;
+ }
+
+ pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__,
+ aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag);
+ if (aac_config->sbr_ps_on_flag)
+ aac_mode = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_mode = AAC_ENC_MODE_AAC_P;
+ else
+ aac_mode = AAC_ENC_MODE_AAC_LC;
+
+ rc = q6asm_enc_cfg_blk_aac(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->sample_rate,
+ enc_cfg->channels,
+ enc_cfg->bit_rate,
+ aac_mode,
+ enc_cfg->stream_format);
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure"
+ "failed rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac);
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ enc_cfg = audio->enc_cfg;
+ if (enc_cfg->channels == CH_MODE_MONO)
+ cfg.channels = 1;
+ else
+ cfg.channels = 2;
+ cfg.sample_rate = enc_cfg->sample_rate;
+ cfg.bit_rate = enc_cfg->bit_rate;
+ /* ADTS(-1) to ADTS(0x00), RAW(0x00) to RAW(0x03) */
+ cfg.stream_format = ((enc_cfg->stream_format == \
+ 0x00) ? AUDIO_AAC_FORMAT_ADTS : AUDIO_AAC_FORMAT_RAW);
+ pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d"
+ "bitrate=%d\n", __func__, audio->ac->session,
+ cfg.stream_format, cfg.sample_rate, cfg.bit_rate);
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AAC_ENC_CONFIG: {
+ struct msm_audio_aac_enc_config cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__,
+ audio->ac->session, cfg.stream_format);
+
+ if ((cfg.stream_format != AUDIO_AAC_FORMAT_RAW) &&
+ (cfg.stream_format != AAC_FORMAT_ADTS)) {
+ pr_err("%s:session id %d: unsupported AAC format\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+
+ if (cfg.channels == 1) {
+ cfg.channels = CH_MODE_MONO;
+ } else if (cfg.channels == 2) {
+ cfg.channels = CH_MODE_STEREO;
+ } else {
+ rc = -EINVAL;
+ break;
+ }
+ if ((cfg.sample_rate < 8000) && (cfg.sample_rate > 48000)) {
+ pr_err("%s: ERROR in setting samplerate = %d\n",
+ __func__, cfg.sample_rate);
+ rc = -EINVAL;
+ break;
+ }
+ /* For aac-lc, min_bit_rate = min(24Kbps, 0.5*SR*num_chan);
+ max_bi_rate = min(192Kbps, 6*SR*num_chan);
+ min_sample_rate = 8000Hz, max_rate=48000 */
+ if ((cfg.bit_rate < 4000) || (cfg.bit_rate > 192000)) {
+ pr_err("%s: ERROR in setting bitrate = %d\n",
+ __func__, cfg.bit_rate);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->sample_rate = cfg.sample_rate;
+ enc_cfg->channels = cfg.channels;
+ enc_cfg->bit_rate = cfg.bit_rate;
+ enc_cfg->stream_format =
+ ((cfg.stream_format == AUDIO_AAC_FORMAT_RAW) ? \
+ 0x03 : 0x00);
+ pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x"
+ "bitrate=0x%x, format(adts/raw) = %d\n",
+ __func__, audio->ac->session, enc_cfg->sample_rate,
+ enc_cfg->channels, enc_cfg->bit_rate,
+ enc_cfg->stream_format);
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, &audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config aac_cfg;
+ struct msm_audio_aac_config *audio_aac_cfg;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ audio_aac_cfg = audio->codec_cfg;
+
+ if (copy_from_user(&aac_cfg, (void *)arg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d"
+ " sbr_ps_flag = %d\n", __func__,
+ audio->ac->session, aac_cfg.sbr_on_flag,
+ aac_cfg.sbr_ps_on_flag);
+ audio_aac_cfg->sbr_on_flag = aac_cfg.sbr_on_flag;
+ audio_aac_cfg->sbr_ps_on_flag = aac_cfg.sbr_ps_on_flag;
+ if ((audio_aac_cfg->sbr_on_flag == 1) ||
+ (audio_aac_cfg->sbr_ps_on_flag == 1)) {
+ if (enc_cfg->sample_rate < 24000) {
+ pr_err("%s: ERROR in setting samplerate = %d"
+ "\n", __func__, enc_cfg->sample_rate);
+ rc = -EINVAL;
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int aac_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_aac_enc_config *enc_cfg;
+ struct msm_audio_aac_config *aac_config;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for aac"
+ "driver\n", __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config\n", __func__, audio->ac->session);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ aac_config = audio->codec_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 1536;
+ audio->max_frames_per_buf = 5;
+ enc_cfg->sample_rate = 8000;
+ enc_cfg->channels = 1;
+ enc_cfg->bit_rate = 16000;
+ enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ aac_config->format = AUDIO_AAC_FORMAT_ADTS;
+ aac_config->audio_object = AUDIO_AAC_OBJECT_LC;
+ aac_config->sbr_on_flag = 0;
+ aac_config->sbr_ps_on_flag = 0;
+ aac_config->channel_configuration = 1;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for"
+ "audio client\n", __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ /* open aac encoder in tunnel mode */
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC,
+ FORMAT_LINEAR_PCM);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->buf_cfg.meta_info_enable = 0x01;
+ pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+ audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: Tunnel Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__,
+ audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->buf_cfg.meta_info_enable = 0x00;
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_ioctl = aac_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = aac_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+struct miscdevice audio_aac_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init aac_in_init(void)
+{
+ return misc_register(&audio_aac_in_misc);
+}
+device_initcall(aac_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
new file mode 100644
index 000000000000..91c588c064d5
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2010-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_amrnb.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((32+sizeof(struct meta_out_dsp)) * 10))
+
+/* ------------------- device --------------------- */
+static long amrnb_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ rc = q6asm_enc_cfg_blk_amrnb(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->band_mode,
+ enc_cfg->dtx_enable);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd amrnb media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session,
+ audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__,
+ audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:AUDIO_STOP\n", __func__);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__,
+ audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+ struct msm_audio_amrnb_enc_config_v2 cfg;
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(struct msm_audio_amrnb_enc_config_v2))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.band_mode > 8 ||
+ cfg.band_mode < 1) {
+ pr_err("%s:session id %d: invalid band mode\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ /* AMR NB encoder accepts values between 0-7
+ while openmax provides value between 1-8
+ as per spec */
+ enc_cfg->band_mode = (cfg.band_mode - 1);
+ enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0);
+ enc_cfg->frame_format = 0;
+ pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+ __func__, audio->ac->session,
+ enc_cfg->band_mode, enc_cfg->dtx_enable);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int amrnb_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s Could not allocate memory for amrnb"
+ "driver\n", __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 32;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->band_mode = 7;
+ enc_cfg->dtx_enable = 0;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio"
+ "client\n", __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open amrnb encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session,
+ rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n",
+ __func__, audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_ioctl = amrnb_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = amrnb_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init amrnb_in_init(void)
+{
+ return misc_register(&audio_amrnb_in_misc);
+}
+
+device_initcall(amrnb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
new file mode 100644
index 000000000000..ea3fe5b84068
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
@@ -0,0 +1,282 @@
+/* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/msm_audio_amrwb.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <asm/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((61+sizeof(struct meta_out_dsp)) * 10))
+
+/* ------------------- device --------------------- */
+static long amrwb_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ rc = q6asm_enc_cfg_blk_amrwb(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->band_mode,
+ enc_cfg->dtx_enable);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd amrwb media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block"
+ "failed\n", __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session,
+ audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:AUDIO_STOP\n", __func__);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed"
+ "rc=%d\n", __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_AMRWB_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_amrwb_enc_config)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_AMRWB_ENC_CONFIG: {
+ struct msm_audio_amrwb_enc_config cfg;
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(struct msm_audio_amrwb_enc_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (cfg.band_mode > 8) {
+ pr_err("%s:session id %d: invalid band mode\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ /* ToDo: AMR WB encoder accepts values between 0-8
+ while openmax provides value between 9-17
+ as per spec */
+ enc_cfg->band_mode = cfg.band_mode;
+ enc_cfg->dtx_enable = (cfg.dtx_enable ? 1 : 0);
+ /* Currently DSP does not support different frameformat */
+ enc_cfg->frame_format = 0;
+ pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+ __func__, audio->ac->session,
+ enc_cfg->band_mode, enc_cfg->dtx_enable);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int amrwb_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_amrwb_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for amrwb driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for amrwb"
+ "config param\n", __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 32;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->band_mode = 8;
+ enc_cfg->dtx_enable = 0;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 16000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s:audio[%p]: Could not allocate memory for audio"
+ "client\n", __func__, audio);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open amrwb encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration"
+ "failed rc=%d\n", __func__, audio->ac->session,
+ rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n",
+ __func__, audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_ioctl = amrwb_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = amrwb_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+struct miscdevice audio_amrwb_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwb_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init amrwb_in_init(void)
+{
+ return misc_register(&audio_amrwb_in_misc);
+}
+
+device_initcall(amrwb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c
new file mode 100644
index 000000000000..caeb79d22880
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c
@@ -0,0 +1,309 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/msm_audio_aac.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+#define PCM_BUFSZ_MIN_AAC ((8*1024) + sizeof(struct dec_meta_out))
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_aac_cfg aac_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t sbr_ps = 0x00;
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ /* turn on both sbr and ps */
+ rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+ if (rc < 0)
+ pr_err("sbr-ps enable failed\n");
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ if (aac_config->sbr_ps_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+ else
+ aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+ switch (aac_config->format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ aac_cfg.format = 0x00;
+ break;
+ case AUDIO_AAC_FORMAT_LOAS:
+ aac_cfg.format = 0x01;
+ break;
+ case AUDIO_AAC_FORMAT_ADIF:
+ aac_cfg.format = 0x02;
+ break;
+ default:
+ case AUDIO_AAC_FORMAT_RAW:
+ aac_cfg.format = 0x03;
+ }
+ aac_cfg.ep_config = aac_config->ep_config;
+ aac_cfg.section_data_resilience =
+ aac_config->aac_section_data_resilience_flag;
+ aac_cfg.scalefactor_data_resilience =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_cfg.spectral_data_resilience =
+ aac_config->aac_spectral_data_resilience_flag;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ if (audio->feedback == TUNNEL_MODE) {
+ aac_cfg.sample_rate = aac_config->sample_rate;
+ aac_cfg.ch_cfg = aac_config->channel_configuration;
+ } else {
+ aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ }
+
+ pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
+ __func__, aac_cfg.format,
+ aac_cfg.aot, aac_cfg.ch_cfg,
+ aac_cfg.sample_rate);
+
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ rc = enable_volume_ramp(audio);
+ if (rc < 0) {
+ pr_err("%s: Failed to enable volume ramp\n",
+ __func__);
+ }
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config *aac_config;
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ break;
+ } else {
+ uint16_t sce_left = 1, sce_right = 2;
+ aac_config = audio->codec_cfg;
+ /* PL_PR is 0 only need to check PL_SR */
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
+ pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid"
+ "dual_mono mode =%d\n", __func__,
+ aac_config->dual_mono_mode);
+ } else {
+ /* convert the data from user into sce_left
+ * and sce_right based on the definitions
+ */
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify"
+ "dual_mono mode =%d\n", __func__,
+ aac_config->dual_mono_mode);
+ switch (aac_config->dual_mono_mode) {
+ case AUDIO_AAC_DUAL_MONO_PL_PR:
+ sce_left = 1;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_SR:
+ sce_left = 2;
+ sce_right = 2;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_PR:
+ sce_left = 2;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_PL_SR:
+ default:
+ sce_left = 1;
+ sce_right = 2;
+ break;
+ }
+ rc = q6asm_cfg_dual_mono_aac(audio->ac,
+ sce_left, sce_right);
+ if (rc < 0)
+ pr_err("%s: asm cmd dualmono failed"
+ " rc=%d\n", __func__, rc);
+ }
+ }
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("%s[%p]:Failed in utils_ioctl: %d\n",
+ __func__, audio, rc);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+ struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_aac_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:Could not allocate memory for aac"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ aac_config = audio->codec_cfg;
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC;
+ aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MPEG4_AAC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open AAC decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_aac_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_aac_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_aac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+ return misc_register(&audio_aac_misc);
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
new file mode 100644
index 000000000000..fc023c1b825f
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
@@ -0,0 +1,165 @@
+/* amrnb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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 "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrnb_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrnb_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMRNB);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrnb_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrnb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrnb_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_amrnb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrnb",
+ .fops = &audio_amrnb_fops,
+};
+
+static int __init audio_amrnb_init(void)
+{
+ return misc_register(&audio_amrnb_misc);
+}
+
+device_initcall(audio_amrnb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
new file mode 100644
index 000000000000..256da4d37912
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
@@ -0,0 +1,168 @@
+/* amrwb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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 "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwb_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_amrwb_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMRWB);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_amrwb_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrwb_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrwb_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_amrwb_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwb",
+ .fops = &audio_amrwb_fops,
+};
+
+static int __init audio_amrwb_init(void)
+{
+ return misc_register(&audio_amrwb_misc);
+}
+
+device_initcall(audio_amrwb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
new file mode 100644
index 000000000000..544bf9ca8a25
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
@@ -0,0 +1,238 @@
+/* amr-wbplus audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/msm_audio_amrwbplus.h>
+#include "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwbplus_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+ if (audio != NULL) {
+ char name[sizeof("msm_amrwbplus_") + 5];
+ snprintf(name, sizeof(name), "msm_amrwbplus_%04x",
+ audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_amrwbplus_debug_fops);
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+ }
+}
+#else
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+}
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct asm_amrwbplus_cfg q6_amrwbplus_cfg;
+ struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config;
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_err("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ amrwbplus_drv_config =
+ (struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg;
+
+ q6_amrwbplus_cfg.size_bytes =
+ amrwbplus_drv_config->size_bytes;
+ q6_amrwbplus_cfg.version =
+ amrwbplus_drv_config->version;
+ q6_amrwbplus_cfg.num_channels =
+ amrwbplus_drv_config->num_channels;
+ q6_amrwbplus_cfg.amr_band_mode =
+ amrwbplus_drv_config->amr_band_mode;
+ q6_amrwbplus_cfg.amr_dtx_mode =
+ amrwbplus_drv_config->amr_dtx_mode;
+ q6_amrwbplus_cfg.amr_frame_fmt =
+ amrwbplus_drv_config->amr_frame_fmt;
+ q6_amrwbplus_cfg.amr_lsf_idx =
+ amrwbplus_drv_config->amr_lsf_idx;
+
+ rc = q6asm_media_format_block_amrwbplus(audio->ac,
+ &q6_amrwbplus_cfg);
+ if (rc < 0) {
+ pr_err("q6asm_media_format_block_amrwb+ failed...\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_GET_AMRWBPLUS_CONFIG_V2: {
+ if ((audio) && (arg) && (audio->codec_cfg)) {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_amrwbplus_config_v2))) {
+ rc = -EFAULT;
+ pr_err("wb+ config get copy_to_user failed");
+ break;
+ }
+ } else {
+ pr_err("wb+ config v2 invalid parameters..");
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AMRWBPLUS_CONFIG_V2: {
+ if ((audio) && (arg) && (audio->codec_cfg)) {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_amrwbplus_config_v2))) {
+ rc = -EFAULT;
+ pr_err("wb+ config set copy_to_user_failed");
+ break;
+ }
+ } else {
+ pr_err("wb+ config invalid parameters..");
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("kzalloc failed for amrwb+ decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg =
+ kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:failed kzalloc for amrwb+ config structure",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac =
+ q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_AMR_WB_PLUS);
+ if (rc < 0) {
+ pr_err("amrwbplus NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS);
+ if (rc < 0) {
+ pr_err("wb+ T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("audio_amrwbplus Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+ config_debug_fs(audio);
+ pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_amrwbplus_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_amrwbplus_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_amrwbplus",
+ .fops = &audio_amrwbplus_fops,
+};
+
+static int __init audio_amrwbplus_init(void)
+{
+ return misc_register(&audio_amrwbplus_misc);
+}
+
+device_initcall(audio_amrwbplus_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
new file mode 100644
index 000000000000..3498e6927a29
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
@@ -0,0 +1,174 @@
+/* evrc audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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 "audio_utils_aio.h"
+
+
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_evrc_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_evrc_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_evrc_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_evrc_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_evrc_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_evrc_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc",
+ .fops = &audio_evrc_fops,
+};
+
+static int __init audio_evrc_init(void)
+{
+ return misc_register(&audio_evrc_misc);
+}
+
+device_initcall(audio_evrc_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
new file mode 100644
index 000000000000..d2f02702bb3e
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
@@ -0,0 +1,174 @@
+/* mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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 "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_mp3_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ rc = enable_volume_ramp(audio);
+ if (rc < 0) {
+ pr_err("%s: Failed to enable volume ramp\n",
+ __func__);
+ }
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_mp3_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for mp3 decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MP3);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open MP3 decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MP3);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_mp3_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_mp3_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_mp3_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_mp3_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_mp3",
+ .fops = &audio_mp3_fops,
+};
+
+static int __init audio_mp3_init(void)
+{
+ return misc_register(&audio_mp3_misc);
+}
+
+device_initcall(audio_mp3_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
new file mode 100644
index 000000000000..0a8ce8e4adc6
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
@@ -0,0 +1,328 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/msm_audio_aac.h>
+#include <mach/socinfo.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+
+
+/* Default number of pre-allocated event packets */
+#define PCM_BUFSZ_MIN_AACM ((8*1024) + sizeof(struct dec_meta_out))
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_aac_cfg aac_cfg;
+ struct msm_audio_aac_config *aac_config;
+ uint32_t sbr_ps = 0x00;
+ aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+ if (audio->feedback == TUNNEL_MODE) {
+ aac_cfg.sample_rate = aac_config->sample_rate;
+ aac_cfg.ch_cfg = aac_config->channel_configuration;
+ } else {
+ aac_cfg.sample_rate = audio->pcm_cfg.sample_rate;
+ aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+ }
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm_native(audio->ac,
+ aac_cfg.sample_rate,
+ aac_cfg.ch_cfg);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ /* turn on both sbr and ps */
+ rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+ if (rc < 0)
+ pr_err("sbr-ps enable failed\n");
+ if (aac_config->sbr_ps_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+ else if (aac_config->sbr_on_flag)
+ aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+ else
+ aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+ switch (aac_config->format) {
+ case AUDIO_AAC_FORMAT_ADTS:
+ aac_cfg.format = 0x00;
+ break;
+ case AUDIO_AAC_FORMAT_LOAS:
+ aac_cfg.format = 0x01;
+ break;
+ case AUDIO_AAC_FORMAT_ADIF:
+ aac_cfg.format = 0x02;
+ break;
+ default:
+ case AUDIO_AAC_FORMAT_RAW:
+ aac_cfg.format = 0x03;
+ }
+ aac_cfg.ep_config = aac_config->ep_config;
+ aac_cfg.section_data_resilience =
+ aac_config->aac_section_data_resilience_flag;
+ aac_cfg.scalefactor_data_resilience =
+ aac_config->aac_scalefactor_data_resilience_flag;
+ aac_cfg.spectral_data_resilience =
+ aac_config->aac_spectral_data_resilience_flag;
+
+ pr_debug("%s:format=%x aot=%d ch=%d sr=%d\n",
+ __func__, aac_cfg.format,
+ aac_cfg.aot, aac_cfg.ch_cfg,
+ aac_cfg.sample_rate);
+
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ if (!cpu_is_msm8x60()) {
+ rc = q6asm_set_encdec_chan_map(audio->ac, 2);
+ if (rc < 0) {
+ pr_err("%s: cmd set encdec_chan_map failed\n",
+ __func__);
+ break;
+ }
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_GET_AAC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_CONFIG: {
+ struct msm_audio_aac_config *aac_config;
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_aac_config))) {
+ rc = -EFAULT;
+ } else {
+ uint16_t sce_left = 1, sce_right = 2;
+ aac_config = audio->codec_cfg;
+ if (aac_config->dual_mono_mode >
+ AUDIO_AAC_DUAL_MONO_PL_SR) {
+ pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n",
+ __func__, aac_config->dual_mono_mode);
+ } else {
+ /* convert the data from user into sce_left
+ * and sce_right based on the definitions
+ */
+ pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n",
+ __func__, aac_config->dual_mono_mode);
+ switch (aac_config->dual_mono_mode) {
+ case AUDIO_AAC_DUAL_MONO_PL_PR:
+ sce_left = 1;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_SR:
+ sce_left = 2;
+ sce_right = 2;
+ break;
+ case AUDIO_AAC_DUAL_MONO_SL_PR:
+ sce_left = 2;
+ sce_right = 1;
+ break;
+ case AUDIO_AAC_DUAL_MONO_PL_SR:
+ default:
+ sce_left = 1;
+ sce_right = 2;
+ break;
+ }
+ rc = q6asm_cfg_dual_mono_aac(audio->ac,
+ sce_left, sce_right);
+ if (rc < 0)
+ pr_err("%s: asm cmd dualmono failed rc=%d\n",
+ __func__, rc);
+ } break;
+ }
+ break;
+ }
+ case AUDIO_SET_AAC_MIX_CONFIG: {
+ pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(unsigned long))) {
+ rc = -EFAULT;
+ break;
+ } else {
+ unsigned long *mix_coeff =
+ (unsigned long *)audio->codec_cfg;
+ pr_debug("%s, value of coeff = %lu",
+ __func__, *mix_coeff);
+ q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff);
+ if (rc < 0)
+ pr_err("%s asm aac_sel_mix_coef failed rc=%d\n",
+ __func__, rc);
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("Calling utils ioctl\n");
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+ struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_multi_aac_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s: Could not allocate memory for aac config\n",
+ __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ aac_config = audio->codec_cfg;
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM;
+ aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_MPEG4_MULTI_AAC);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open AAC decoder, expected frames is always 1
+ audio->buf_cfg.frames_per_buf = 0x01;*/
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_multi_aac_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_aac_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n",
+ __func__, audio->feedback, audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_multiaac_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_multi_aac",
+ .fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+ return misc_register(&audio_multiaac_misc);
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
new file mode 100644
index 000000000000..4993226d61b3
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
@@ -0,0 +1,179 @@
+/* qcelp(v13k) audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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 "audio_utils_aio.h"
+
+#define FRAME_SIZE_DEC_QCELP ((32) + sizeof(struct dec_meta_in))
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_qcelp_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+ audio->ac->session,
+ audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_qcelp_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for aac decode driver\n");
+ return -ENOMEM;
+ }
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->pcm_cfg.channel_count = 1;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_qcelp_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_qcelp_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_qcelp_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_qcelp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp",
+ .fops = &audio_qcelp_fops,
+};
+
+static int __init audio_qcelp_init(void)
+{
+ return misc_register(&audio_qcelp_misc);
+}
+
+device_initcall(audio_qcelp_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
new file mode 100644
index 000000000000..042b77b9e722
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -0,0 +1,656 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+static int audio_in_pause(struct q6audio_in *audio)
+{
+ int rc;
+
+ rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+ if (rc < 0)
+ pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
+ audio->ac->session, rc);
+
+ return rc;
+}
+
+static int audio_in_flush(struct q6audio_in *audio)
+{
+ int rc;
+
+ pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
+ /* Flush if session running */
+ if (audio->enabled) {
+ /* Implicitly issue a pause to the encoder before flushing */
+ rc = audio_in_pause(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: pause cmd failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ return rc;
+ }
+
+ rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+ if (rc < 0) {
+ pr_err("%s:session id %d: flush cmd failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ return rc;
+ }
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+ pr_debug("Rerun the session\n");
+ }
+ audio->rflush = 1;
+ audio->wflush = 1;
+ memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
+ wake_up(&audio->read_wait);
+ /* get read_lock to ensure no more waiting read thread */
+ mutex_lock(&audio->read_lock);
+ audio->rflush = 0;
+ mutex_unlock(&audio->read_lock);
+ wake_up(&audio->write_wait);
+ /* get write_lock to ensure no more waiting write thread */
+ mutex_lock(&audio->write_lock);
+ audio->wflush = 0;
+ mutex_unlock(&audio->write_lock);
+ pr_debug("%s:session id %d: in_bytes %d\n", __func__,
+ audio->ac->session, atomic_read(&audio->in_bytes));
+ pr_debug("%s:session id %d: in_samples %d\n", __func__,
+ audio->ac->session, atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+ atomic_set(&audio->out_count, 0);
+ return 0;
+}
+
+/* must be called with audio->lock held */
+int audio_in_enable(struct q6audio_in *audio)
+{
+ if (audio->enabled)
+ return 0;
+
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+/* must be called with audio->lock held */
+int audio_in_disable(struct q6audio_in *audio)
+{
+ int rc = 0;
+ if (!audio->stopped) {
+ audio->enabled = 0;
+ audio->opened = 0;
+ pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
+ __func__, audio->ac->session,
+ atomic_read(&audio->in_bytes),
+ atomic_read(&audio->in_samples));
+
+ rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+ if (rc < 0)
+ pr_err("%s:session id %d: Failed to close the session rc=%d\n",
+ __func__, audio->ac->session,
+ rc);
+ audio->stopped = 1;
+ memset(audio->out_frame_info, 0,
+ sizeof(audio->out_frame_info));
+ wake_up(&audio->read_wait);
+ wake_up(&audio->write_wait);
+ }
+ pr_debug("%s:session id %d: enabled[%d]\n", __func__,
+ audio->ac->session, audio->enabled);
+ return rc;
+}
+
+int audio_in_buf_alloc(struct q6audio_in *audio)
+{
+ int rc = 0;
+
+ switch (audio->buf_alloc) {
+ case NO_BUF_ALLOC:
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN,
+ audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
+ audio->ac->session);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ }
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ break;
+ case BUF_ALLOC_IN:
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ break;
+ case BUF_ALLOC_OUT:
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__,
+ audio->ac->session);
+ rc = -ENOMEM;
+ break;
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ }
+ break;
+ default:
+ pr_debug("%s:session id %d: buf[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ }
+
+ return rc;
+}
+/* ------------------- device --------------------- */
+long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+
+ if (cmd == AUDIO_GET_STATS) {
+ struct msm_audio_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+ return -EFAULT;
+ return rc;
+ }
+
+ mutex_lock(&audio->lock);
+ switch (cmd) {
+ case AUDIO_FLUSH: {
+ /* Make sure we're stopped and we wake any threads
+ * that might be blocked holding the read_lock.
+ * While audio->stopped read threads will always
+ * exit immediately.
+ */
+ rc = audio_in_flush(audio);
+ if (rc < 0)
+ pr_err("%s:session id %d: Flush Fail rc=%d\n",
+ __func__, audio->ac->session, rc);
+ else { /* Register back the flushed read buffer with DSP */
+ int cnt = 0;
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ pr_debug("register the read buffer\n");
+ }
+ break;
+ }
+ case AUDIO_PAUSE: {
+ pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
+ audio->ac->session);
+ if (audio->enabled)
+ audio_in_pause(audio);
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->str_cfg.buffer_size;
+ cfg.buffer_count = audio->str_cfg.buffer_count;
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session, cfg.buffer_size,
+ cfg.buffer_count);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Minimum single frame size,
+ but with in maximum frames number */
+ if ((cfg.buffer_size < (audio->min_frame_size+ \
+ sizeof(struct meta_out_dsp))) ||
+ (cfg.buffer_count < FRAME_NUM)) {
+ rc = -EINVAL;
+ break;
+ }
+ audio->str_cfg.buffer_size = cfg.buffer_size;
+ audio->str_cfg.buffer_count = cfg.buffer_count;
+ if (audio->opened) {
+ rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+ ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+ audio->str_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ audio->buf_alloc |= BUF_ALLOC_OUT;
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
+ __func__, audio->ac->session,
+ audio->str_cfg.buffer_size,
+ audio->str_cfg.buffer_count);
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ if (copy_to_user((void *) arg, &audio->ac->session,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_BUF_CFG: {
+ struct msm_audio_buf_cfg cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ break;
+ }
+
+ /* Restrict the num of frames per buf to coincide with
+ * default buf size */
+ if (cfg.frames_per_buf > audio->max_frames_per_buf) {
+ rc = -EFAULT;
+ break;
+ }
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
+ pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
+ audio->ac->session, cfg.meta_info_enable,
+ cfg.frames_per_buf);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG: {
+ pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+
+ if (copy_to_user((void *)arg, &audio->buf_cfg,
+ sizeof(struct msm_audio_buf_cfg)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ if (copy_to_user((void *)arg, &audio->pcm_cfg,
+ sizeof(struct msm_audio_config)))
+ rc = -EFAULT;
+ break;
+
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config cfg;
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ break;
+ }
+ if (audio->feedback != NON_TUNNEL_MODE) {
+ pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
+ __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ break;
+ }
+ if ((cfg.buffer_count > PCM_BUF_COUNT) ||
+ (cfg.buffer_count == 1))
+ cfg.buffer_count = PCM_BUF_COUNT;
+
+ audio->pcm_cfg.buffer_count = cfg.buffer_count;
+ audio->pcm_cfg.buffer_size = cfg.buffer_size;
+ audio->pcm_cfg.channel_count = cfg.channel_count;
+ audio->pcm_cfg.sample_rate = cfg.sample_rate;
+ if (audio->opened && audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+ ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+ audio->pcm_cfg.buffer_count);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Buffer Alloc failed\n",
+ __func__, audio->ac->session);
+ rc = -ENOMEM;
+ break;
+ }
+ }
+ audio->buf_alloc |= BUF_ALLOC_IN;
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
+ audio->ac->session, audio->pcm_cfg.buffer_count,
+ audio->pcm_cfg.buffer_size);
+ break;
+ }
+ default:
+ /* call codec specific ioctl */
+ rc = audio->enc_ioctl(file, cmd, arg);
+ }
+ mutex_unlock(&audio->lock);
+ return rc;
+}
+
+ssize_t audio_in_read(struct file *file,
+ char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct q6audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ unsigned char *data;
+ uint32_t offset = 0;
+ uint32_t size = 0;
+ int rc = 0;
+ uint32_t idx;
+ struct meta_out_dsp meta;
+ uint32_t bytes_to_copy = 0;
+ uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+ (sizeof(unsigned char) +
+ (sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
+
+ memset(&meta, 0, sizeof(meta));
+ pr_debug("%s:session id %d: read - %d\n", __func__, audio->ac->session,
+ count);
+ if (!audio->enabled)
+ return -EFAULT;
+ mutex_lock(&audio->read_lock);
+ while (count > 0) {
+ rc = wait_event_interruptible(
+ audio->read_wait,
+ ((atomic_read(&audio->out_count) > 0) ||
+ (audio->stopped) ||
+ audio->rflush || audio->eos_rsp ||
+ audio->event_abort));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
+
+ if (rc < 0)
+ break;
+
+ if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
+ audio->rflush) {
+ pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
+ __func__,
+ audio->ac->session);
+ rc = 0;/* End of File */
+ break;
+ }
+ if (!(atomic_read(&audio->out_count)) &&
+ (audio->eos_rsp == 1) &&
+ (count >= (sizeof(unsigned char) +
+ sizeof(struct meta_out_dsp)))) {
+ unsigned char num_of_frames;
+ pr_info("%s:session id %d: eos %d at output\n",
+ __func__, audio->ac->session, audio->eos_rsp);
+ if (buf != start)
+ break;
+ num_of_frames = 0xFF;
+ if (copy_to_user(buf, &num_of_frames,
+ sizeof(unsigned char))) {
+ rc = -EFAULT;
+ break;
+ }
+ buf += sizeof(unsigned char);
+ meta.frame_size = 0xFFFF;
+ meta.encoded_pcm_samples = 0xFFFF;
+ meta.msw_ts = 0x00;
+ meta.lsw_ts = 0x00;
+ meta.nflags = AUD_EOS_SET;
+ audio->eos_rsp = 0;
+ if (copy_to_user(buf, &meta, sizeof(meta))) {
+ rc = -EFAULT;
+ break;
+ }
+ buf += sizeof(meta);
+ break;
+ }
+ data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
+ &size, &idx);
+ if ((count >= (size + mfield_size)) && data) {
+ if (audio->buf_cfg.meta_info_enable) {
+ if (copy_to_user(buf,
+ &audio->out_frame_info[idx][0],
+ sizeof(unsigned char))) {
+ rc = -EFAULT;
+ break;
+ }
+ bytes_to_copy =
+ (size + audio->out_frame_info[idx][1]);
+ /* Number of frames information copied */
+ buf += sizeof(unsigned char);
+ count -= sizeof(unsigned char);
+ } else {
+ offset = audio->out_frame_info[idx][1];
+ bytes_to_copy = size;
+ }
+
+ pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
+ __func__, audio->ac->session,
+ audio->out_frame_info[idx][1],
+ audio->out_frame_info[idx][0]);
+
+ if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
+ rc = -EFAULT;
+ break;
+ }
+ count -= bytes_to_copy;
+ buf += bytes_to_copy;
+ } else {
+ pr_err("%s:session id %d: short read data[%p] bytesavail[%d]bytesrequest[%d]\n",
+ __func__,
+ audio->ac->session,
+ data, size, count);
+ }
+ atomic_dec(&audio->out_count);
+ q6asm_read(audio->ac);
+ break;
+ }
+ mutex_unlock(&audio->read_lock);
+
+ pr_debug("%s:session id %d: read: %d bytes\n", __func__,
+ audio->ac->session, (buf-start));
+ if (buf > start)
+ return buf - start;
+ return rc;
+}
+
+static int extract_meta_info(char *buf, unsigned long *msw_ts,
+ unsigned long *lsw_ts, unsigned int *flags)
+{
+ struct meta_in *meta = (struct meta_in *)buf;
+ *msw_ts = meta->ntimestamp.highpart;
+ *lsw_ts = meta->ntimestamp.lowpart;
+ *flags = meta->nflags;
+ return 0;
+}
+
+ssize_t audio_in_write(struct file *file,
+ const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct q6audio_in *audio = file->private_data;
+ const char __user *start = buf;
+ size_t xfer = 0;
+ char *cpy_ptr;
+ int rc = 0;
+ unsigned char *data;
+ uint32_t size = 0;
+ uint32_t idx = 0;
+ uint32_t nflags = 0;
+ unsigned long msw_ts = 0;
+ unsigned long lsw_ts = 0;
+ uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+ sizeof(struct meta_in);
+
+ pr_debug("%s:session id %d: to write[%d]\n", __func__,
+ audio->ac->session, count);
+ if (!audio->enabled)
+ return -EFAULT;
+ mutex_lock(&audio->write_lock);
+
+ while (count > 0) {
+ rc = wait_event_interruptible(audio->write_wait,
+ ((atomic_read(&audio->in_count) > 0) ||
+ (audio->stopped) ||
+ (audio->wflush) || (audio->event_abort)));
+
+ if (audio->event_abort) {
+ rc = -EIO;
+ break;
+ }
+
+ if (rc < 0)
+ break;
+ if (audio->stopped || audio->wflush) {
+ pr_debug("%s: session id %d: stop or flush\n", __func__,
+ audio->ac->session);
+ rc = -EBUSY;
+ break;
+ }
+ /* if no PCM data, might have only eos buffer
+ such case do not hold cpu buffer */
+ if ((buf == start) && (count == mfield_size)) {
+ char eos_buf[sizeof(struct meta_in)];
+ /* Processing begining of user buffer */
+ if (copy_from_user(eos_buf, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ extract_meta_info(eos_buf, &msw_ts, &lsw_ts,
+ &nflags);
+ buf += mfield_size;
+ /* send the EOS and return */
+ pr_debug("%s:session id %d: send EOS 0x%8x\n",
+ __func__,
+ audio->ac->session, nflags);
+ break;
+ }
+ data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
+ &size, &idx);
+ if (!data) {
+ pr_debug("%s:session id %d: No buf available\n",
+ __func__, audio->ac->session);
+ continue;
+ }
+ cpy_ptr = data;
+ if (audio->buf_cfg.meta_info_enable) {
+ if (buf == start) {
+ /* Processing beginning of user buffer */
+ if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+ rc = -EFAULT;
+ break;
+ }
+ /* Check if EOS flag is set and buffer has
+ * contains just meta field
+ */
+ extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
+ &nflags);
+ buf += mfield_size;
+ count -= mfield_size;
+ } else {
+ pr_debug("%s:session id %d: continuous buffer\n",
+ __func__, audio->ac->session);
+ }
+ }
+ xfer = (count > (audio->pcm_cfg.buffer_size)) ?
+ (audio->pcm_cfg.buffer_size) : count;
+
+ if (copy_from_user(cpy_ptr, buf, xfer)) {
+ rc = -EFAULT;
+ break;
+ }
+ rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
+ if (rc < 0) {
+ rc = -EFAULT;
+ break;
+ }
+ atomic_dec(&audio->in_count);
+ count -= xfer;
+ buf += xfer;
+ }
+ mutex_unlock(&audio->write_lock);
+ pr_debug("%s:session id %d: eos_condition 0x%8x buf[0x%x] start[0x%x]\n",
+ __func__, audio->ac->session,
+ nflags, (int) buf, (int) start);
+ if (nflags & AUD_EOS_SET) {
+ rc = q6asm_cmd(audio->ac, CMD_EOS);
+ pr_info("%s:session id %d: eos %d at input\n", __func__,
+ audio->ac->session, audio->eos_rsp);
+ }
+ pr_debug("%s:session id %d: Written %d Avail Buf[%d]", __func__,
+ audio->ac->session, (buf - start - mfield_size),
+ atomic_read(&audio->in_count));
+ if (!rc) {
+ if (buf > start)
+ return buf - start;
+ }
+ return rc;
+}
+
+int audio_in_release(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = file->private_data;
+ pr_info("%s: session id %d\n", __func__, audio->ac->session);
+ mutex_lock(&audio->lock);
+ audio_in_disable(audio);
+ q6asm_audio_client_free(audio->ac);
+ mutex_unlock(&audio->lock);
+ kfree(audio->enc_cfg);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return 0;
+}
+
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/drivers/misc/qcom/qdsp6v2/audio_utils.h
new file mode 100644
index 000000000000..7209724f484d
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/msm_audio.h>
+#include "q6audio_common.h"
+
+#define FRAME_NUM (8)
+
+#define PCM_BUF_COUNT (2)
+
+#define AUD_EOS_SET 0x01
+#define TUNNEL_MODE 0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define NO_BUF_ALLOC 0x00
+#define BUF_ALLOC_IN 0x01
+#define BUF_ALLOC_OUT 0x02
+#define BUF_ALLOC_INOUT 0x03
+#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095))
+
+struct timestamp {
+ unsigned long lowpart;
+ unsigned long highpart;
+} __packed;
+
+struct meta_in {
+ unsigned short offset;
+ struct timestamp ntimestamp;
+ unsigned int nflags;
+} __packed;
+
+struct meta_out_dsp {
+ u32 offset_to_frame;
+ u32 frame_size;
+ u32 encoded_pcm_samples;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 nflags;
+} __packed;
+
+struct meta_out {
+ unsigned char num_of_frames;
+ struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+struct q6audio_in {
+ spinlock_t dsp_lock;
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ struct mutex write_lock;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ struct audio_client *ac;
+ struct msm_audio_stream_config str_cfg;
+ void *enc_cfg;
+ struct msm_audio_buf_cfg buf_cfg;
+ struct msm_audio_config pcm_cfg;
+ void *codec_cfg;
+
+ /* number of buffers available to read/write */
+ atomic_t in_count;
+ atomic_t out_count;
+
+ /* first idx: num of frames per buf, second idx: offset to frame */
+ uint32_t out_frame_info[FRAME_NUM][2];
+ int eos_rsp;
+ int opened;
+ int enabled;
+ int stopped;
+ int event_abort;
+ int feedback; /* Flag indicates whether used
+ in Non Tunnel mode */
+ int rflush;
+ int wflush;
+ int buf_alloc;
+ uint16_t min_frame_size;
+ uint16_t max_frames_per_buf;
+ long (*enc_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+int audio_in_enable(struct q6audio_in *audio);
+int audio_in_disable(struct q6audio_in *audio);
+int audio_in_buf_alloc(struct q6audio_in *audio);
+long audio_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+ssize_t audio_in_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos);
+ssize_t audio_in_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos);
+int audio_in_release(struct inode *inode, struct file *file);
+
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
new file mode 100644
index 000000000000..b1e5a30bc374
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -0,0 +1,1483 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include <linux/debugfs.h>
+#include <linux/msm_audio_ion.h>
+#include "audio_utils_aio.h"
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+#include <linux/qdsp6v2/audio_dev_ctl.h>
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t audio_aio_debug_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ const int debug_bufmax = 4096;
+ static char buffer[4096];
+ int n = 0;
+ struct q6audio_aio *audio = file->private_data;
+
+ mutex_lock(&audio->lock);
+ n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "enabled %d\n", audio->enabled);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "stopped %d\n", audio->stopped);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "feedback %d\n", audio->feedback);
+ mutex_unlock(&audio->lock);
+ /* Following variables are only useful for debugging when
+ * when playback halts unexpectedly. Thus, no mutual exclusion
+ * enforced
+ */
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "wflush %d\n", audio->wflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "rflush %d\n", audio->rflush);
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "inqueue empty %d\n", list_empty(&audio->in_queue));
+ n += scnprintf(buffer + n, debug_bufmax - n,
+ "outqueue empty %d\n", list_empty(&audio->out_queue));
+ buffer[n] = 0;
+ return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+#endif
+
+int insert_eos_buf(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct dec_meta_out *eos_buf = buf_node->kvaddr;
+ pr_debug("%s[%p]:insert_eos_buf\n", __func__, audio);
+ eos_buf->num_of_frames = 0xFFFFFFFF;
+ eos_buf->meta_out_dsp[0].offset_to_frame = 0x0;
+ eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET;
+ return sizeof(struct dec_meta_out) +
+ sizeof(eos_buf->meta_out_dsp[0]);
+}
+
+/* Routine which updates read buffers of driver/dsp,
+ for flush operation as DSP output might not have proper
+ value set */
+static int insert_meta_data_flush(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct dec_meta_out *meta_data = buf_node->kvaddr;
+ meta_data->num_of_frames = 0x0;
+ meta_data->meta_out_dsp[0].offset_to_frame = 0x0;
+ meta_data->meta_out_dsp[0].nflags = 0x0;
+ return sizeof(struct dec_meta_out) +
+ sizeof(meta_data->meta_out_dsp[0]);
+}
+
+static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr,
+ unsigned long len,
+ struct audio_aio_ion_region **region)
+{
+ struct audio_aio_ion_region *region_elt;
+
+ int match_count = 0;
+
+ *region = NULL;
+
+ /* returns physical address or zero */
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len) {
+ /* offset since we could pass vaddr inside a registerd
+ * ion buffer
+ */
+
+ match_count++;
+ if (!*region)
+ *region = region_elt;
+ }
+ }
+
+ if (match_count > 1) {
+ pr_err("%s[%p]:multiple hits for vaddr %p, len %ld\n",
+ __func__, audio, addr, len);
+ list_for_each_entry(region_elt, &audio->ion_region_queue,
+ list) {
+ if (addr >= region_elt->vaddr &&
+ addr < region_elt->vaddr + region_elt->len &&
+ addr + len <= region_elt->vaddr + region_elt->len)
+ pr_err("\t%s[%p]:%p, %ld --> %p\n",
+ __func__, audio,
+ region_elt->vaddr,
+ region_elt->len,
+ (void *)region_elt->paddr);
+ }
+ }
+
+ return *region ? 0 : -1;
+}
+
+static unsigned long audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
+ unsigned long len, int ref_up, void **kvaddr)
+{
+ struct audio_aio_ion_region *region;
+ unsigned long paddr;
+ int ret;
+
+ ret = audio_aio_ion_lookup_vaddr(audio, addr, len, &region);
+ if (ret) {
+ pr_err("%s[%p]:lookup (%p, %ld) failed\n",
+ __func__, audio, addr, len);
+ return 0;
+ }
+ if (ref_up)
+ region->ref_cnt++;
+ else
+ region->ref_cnt--;
+ pr_debug("%s[%p]:found region %p ref_cnt %d\n",
+ __func__, audio, region, region->ref_cnt);
+ paddr = region->paddr + (addr - region->vaddr);
+ /* provide kernel virtual address for accessing meta information */
+ if (kvaddr)
+ *kvaddr = (void *) (region->kvaddr + (addr - region->vaddr));
+ return paddr;
+}
+
+static int audio_aio_pause(struct q6audio_aio *audio)
+{
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p], enabled = %d\n", __func__, audio,
+ audio->enabled);
+ if (audio->enabled) {
+ rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+ if (rc < 0)
+ pr_err("%s[%p]: pause cmd failed rc=%d\n",
+ __func__, audio, rc);
+
+ if (rc == 0) {
+ /* Send suspend only if pause was successful */
+ rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
+ if (rc < 0)
+ pr_err("%s[%p]: suspend cmd failed rc=%d\n",
+ __func__, audio, rc);
+ } else
+ pr_err("%s[%p]: not sending suspend since pause failed\n",
+ __func__, audio);
+
+ } else
+ pr_err("%s[%p]: Driver not enabled\n", __func__, audio);
+ return rc;
+}
+
+static int audio_aio_flush(struct q6audio_aio *audio)
+{
+ int rc;
+
+ if (audio->enabled) {
+ /* Implicitly issue a pause to the decoder before flushing if
+ it is not in pause state */
+ if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
+ rc = audio_aio_pause(audio);
+ if (rc < 0)
+ pr_err("%s[%p}: pause cmd failed rc=%d\n",
+ __func__, audio,
+ rc);
+ else
+ audio->drv_status |= ADRV_STATUS_PAUSE;
+ }
+ rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+ if (rc < 0)
+ pr_err("%s[%p]: flush cmd failed rc=%d\n",
+ __func__, audio, rc);
+ /* Not in stop state, reenable the stream */
+ if (audio->stopped == 0) {
+ rc = audio_aio_enable(audio);
+ if (rc)
+ pr_err("%s[%p]:audio re-enable failed\n",
+ __func__, audio);
+ else {
+ audio->enabled = 1;
+ if (audio->drv_status & ADRV_STATUS_PAUSE)
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ }
+ }
+ }
+ pr_debug("%s[%p]:in_bytes %d\n",
+ __func__, audio, atomic_read(&audio->in_bytes));
+ pr_debug("%s[%p]:in_samples %d\n",
+ __func__, audio, atomic_read(&audio->in_samples));
+ atomic_set(&audio->in_bytes, 0);
+ atomic_set(&audio->in_samples, 0);
+ return 0;
+}
+
+static int audio_aio_outport_flush(struct q6audio_aio *audio)
+{
+ int rc;
+
+ rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
+ if (rc < 0)
+ pr_err("%s[%p}: output port flush cmd failed rc=%d\n",
+ __func__, audio, rc);
+ return rc;
+}
+
+/* Write buffer to DSP / Handle Ack from DSP */
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload event_payload;
+ struct audio_aio_buffer_node *used_buf;
+
+ /* No active flush in progress */
+ if (audio->wflush)
+ return;
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (list_empty(&audio->out_queue)) {
+ pr_warning("%s: ingore unexpected event from dsp\n", __func__);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ return;
+ }
+ used_buf = list_first_entry(&audio->out_queue,
+ struct audio_aio_buffer_node, list);
+ if (token == used_buf->token) {
+ list_del(&used_buf->list);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ pr_debug("%s[%p]:consumed buffer\n", __func__, audio);
+ event_payload.aio_buf = used_buf->buf;
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ event_payload);
+ kfree(used_buf);
+ if (list_empty(&audio->out_queue) &&
+ (audio->drv_status & ADRV_STATUS_FSYNC)) {
+ pr_debug("%s[%p]: list is empty, reached EOS in Tunnel\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
+ } else {
+ pr_err("%s[%p]:expected=%lx ret=%x\n",
+ __func__, audio, used_buf->token, token);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+}
+
+/* ------------------- device --------------------- */
+void audio_aio_async_out_flush(struct q6audio_aio *audio)
+{
+ struct audio_aio_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+ unsigned long flags;
+
+ pr_debug("%s[%p}\n", __func__, audio);
+ /* EOS followed by flush, EOS response not guranteed, free EOS i/p
+ buffer */
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+
+ if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) {
+ pr_debug("%s[%p]: EOS followed by flush received,acknowledge"\
+ " eos i/p buffer immediately\n", __func__, audio);
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+ audio->eos_write_payload);
+ memset(&audio->eos_write_payload , 0,
+ sizeof(union msm_audio_event_payload));
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ list_for_each_safe(ptr, next, &audio->out_queue) {
+ buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+ list_del(&buf_node->list);
+ payload.aio_buf = buf_node->buf;
+ audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload);
+ kfree(buf_node);
+ pr_debug("%s[%p]: Propagate WRITE_DONE during flush\n",
+ __func__, audio);
+ }
+}
+
+void audio_aio_async_in_flush(struct q6audio_aio *audio)
+{
+ struct audio_aio_buffer_node *buf_node;
+ struct list_head *ptr, *next;
+ union msm_audio_event_payload payload;
+
+ pr_debug("%s[%p]\n", __func__, audio);
+ list_for_each_safe(ptr, next, &audio->in_queue) {
+ buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+ list_del(&buf_node->list);
+ /* Forcefull send o/p eos buffer after flush, if no eos response
+ * received by dsp even after sending eos command */
+ if ((audio->eos_rsp != 1) && audio->eos_flag) {
+ pr_debug("%s[%p]: send eos on o/p buffer during flush\n",
+ __func__, audio);
+ payload.aio_buf = buf_node->buf;
+ payload.aio_buf.data_len =
+ insert_eos_buf(audio, buf_node);
+ audio->eos_flag = 0;
+ } else {
+ payload.aio_buf = buf_node->buf;
+ payload.aio_buf.data_len =
+ insert_meta_data_flush(audio, buf_node);
+ }
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload);
+ kfree(buf_node);
+ pr_debug("%s[%p]: Propagate READ_DONE during flush\n",
+ __func__, audio);
+ }
+}
+
+int audio_aio_enable(struct q6audio_aio *audio)
+{
+ /* 2nd arg: 0 -> run immediately
+ 3rd arg: 0 -> msw_ts, 4th arg: 0 ->lsw_ts */
+ return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+int audio_aio_disable(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ if (audio->opened) {
+ audio->enabled = 0;
+ audio->opened = 0;
+ pr_debug("%s[%p]: inbytes[%d] insamples[%d]\n", __func__,
+ audio, atomic_read(&audio->in_bytes),
+ atomic_read(&audio->in_samples));
+ /* Close the session */
+ rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+ if (rc < 0)
+ pr_err("%s[%p]:Failed to close the session rc=%d\n",
+ __func__, audio, rc);
+ audio->stopped = 1;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->cmd_wait);
+ }
+ pr_debug("%s[%p]:enabled[%d]\n", __func__, audio, audio->enabled);
+ return rc;
+}
+
+void audio_aio_reset_ion_region(struct q6audio_aio *audio)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+ list_del(&region->list);
+ msm_audio_ion_free_legacy(audio->client, region->handle);
+ kfree(region);
+ }
+
+ return;
+}
+
+void audio_aio_reset_event_queue(struct q6audio_aio *audio)
+{
+ unsigned long flags;
+ struct audio_aio_event *drv_evt;
+ struct list_head *ptr, *next;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ list_for_each_safe(ptr, next, &audio->event_queue) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ drv_evt = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ kfree(drv_evt);
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ return;
+}
+
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+ pr_debug("%s[%p]: phy_address = 0x%lx\n",
+ __func__, audio, region->paddr);
+ if (region != NULL) {
+ rc = q6asm_memory_unmap(audio->ac,
+ (uint32_t)region->paddr, IN);
+ if (rc < 0)
+ pr_err("%s[%p]: memory unmap failed\n",
+ __func__, audio);
+ }
+ }
+}
+
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+
+static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+ void *private_data)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *) private_data;
+ int rc = 0;
+
+ switch (evt_id) {
+ case AUDDEV_EVT_STREAM_VOL_CHG:
+ audio->volume = evt_payload->session_vol;
+ pr_debug("%s[%p]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
+ __func__, audio, audio->volume, audio->enabled);
+ if (audio->enabled == 1) {
+ if (audio->ac) {
+ rc = q6asm_set_volume(audio->ac, audio->volume);
+ if (rc < 0) {
+ pr_err("%s[%p]: Send Volume command failed rc=%d\n",
+ __func__, audio, rc);
+ }
+ }
+ }
+ break;
+ default:
+ pr_err("%s[%p]:ERROR:wrong event\n", __func__, audio);
+ break;
+ }
+}
+
+int register_volume_listener(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+
+ rc = auddev_register_evt_listner(audio->device_events,
+ AUDDEV_CLNT_DEC,
+ audio->ac->session,
+ audio_aio_listner,
+ (void *)audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: Event listener failed\n", __func__, audio);
+ rc = -EACCES;
+ }
+ return rc;
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+ auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
+}
+
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+ int rc = 0;
+ struct asm_softpause_params softpause;
+ struct asm_softvolume_params softvol;
+
+ if (audio->ac == NULL)
+ return -EINVAL;
+ pr_debug("%s[%p]\n", __func__, audio);
+ softpause.enable = SOFT_PAUSE_ENABLE;
+ softpause.period = SOFT_PAUSE_PERIOD;
+ softpause.step = SOFT_PAUSE_STEP;
+ softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR;
+
+ softvol.period = SOFT_VOLUME_PERIOD;
+ softvol.step = SOFT_VOLUME_STEP;
+ softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR;
+
+ if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR)
+ softpause.step = SOFT_PAUSE_STEP_LINEAR;
+ if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR)
+ softvol.step = SOFT_VOLUME_STEP_LINEAR;
+ rc = q6asm_set_volume(audio->ac, audio->volume);
+ if (rc < 0) {
+ pr_err("%s: Send Volume command failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ rc = q6asm_set_softpause(audio->ac, &softpause);
+ if (rc < 0) {
+ pr_err("%s: Send SoftPause Param failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ rc = q6asm_set_softvolume(audio->ac, &softvol);
+ if (rc < 0) {
+ pr_err("%s: Send SoftVolume Param failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* disable mute by default */
+ rc = q6asm_set_mute(audio->ac, 0);
+ if (rc < 0) {
+ pr_err("%s: Send mute command failed rc=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ return rc;
+}
+
+#else /*CONFIG_USE_DEV_CTRL_VOLUME*/
+int register_volume_listener(struct q6audio_aio *audio)
+{
+ return 0;/* do nothing */
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+ return;/* do nothing */
+}
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+ return 0; /* do nothing */
+}
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+
+int audio_aio_release(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = file->private_data;
+ pr_debug("%s[%p]\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ audio->wflush = 1;
+ if (audio->enabled)
+ audio_aio_flush(audio);
+ audio->wflush = 0;
+ audio->drv_ops.out_flush(audio);
+ audio->drv_ops.in_flush(audio);
+ audio_aio_disable(audio);
+ audio_aio_unmap_ion_region(audio);
+ audio_aio_reset_ion_region(audio);
+ msm_audio_ion_client_destroy(audio->client);
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ audio_aio_reset_event_queue(audio);
+ q6asm_audio_client_free(audio->ac);
+ mutex_unlock(&audio->lock);
+ mutex_destroy(&audio->lock);
+ mutex_destroy(&audio->read_lock);
+ mutex_destroy(&audio->write_lock);
+ mutex_destroy(&audio->get_event_lock);
+ unregister_volume_listener(audio);
+
+#ifdef CONFIG_DEBUG_FS
+ if (audio->dentry)
+ debugfs_remove(audio->dentry);
+#endif
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return 0;
+}
+
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+ int rc = 0;
+ struct q6audio_aio *audio = file->private_data;
+
+ if (!audio->enabled || audio->feedback)
+ return -EINVAL;
+
+ /* Blocking client sends more data */
+ mutex_lock(&audio->lock);
+ audio->drv_status |= ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+
+ audio->eos_rsp = 0;
+
+ pr_debug("%s[%p]Wait for write done from DSP\n", __func__, audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ (list_empty(&audio->out_queue)) ||
+ audio->wflush || audio->stopped);
+
+ if (audio->stopped || audio->wflush) {
+ pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ , __func__, audio);
+ audio->wflush = 0;
+ rc = -EBUSY;
+ }
+
+ if (rc < 0) {
+ pr_err("%s[%p]: wait event for list_empty failed, rc = %d\n",
+ __func__, audio, rc);
+ goto done;
+ }
+
+ rc = q6asm_cmd(audio->ac, CMD_EOS);
+ pr_debug("%s[%p]: EOS cmd sent to DSP\n", __func__, audio);
+
+ if (rc < 0)
+ pr_err("%s[%p]: q6asm_cmd failed, rc = %d",
+ __func__, audio, rc);
+
+ pr_debug("%s[%p]: wait for RENDERED_EOS from DSP\n"
+ , __func__, audio);
+ rc = wait_event_interruptible(audio->write_wait,
+ (audio->eos_rsp || audio->wflush ||
+ audio->stopped));
+
+ if (rc < 0) {
+ pr_err("%s[%p]: wait event for eos_rsp failed, rc = %d\n",
+ __func__, audio, rc);
+ goto done;
+ }
+
+ if (audio->stopped || audio->wflush) {
+ audio->wflush = 0;
+ pr_debug("%s[%p]: Audio Flushed or Stopped,this is not EOS\n"
+ , __func__, audio);
+ rc = -EBUSY;
+ }
+
+ if (audio->eos_rsp == 1)
+ pr_debug("%s[%p]: EOS\n", __func__, audio);
+
+
+done:
+ mutex_lock(&audio->lock);
+ audio->drv_status &= ~ADRV_STATUS_FSYNC;
+ mutex_unlock(&audio->lock);
+
+ return rc;
+}
+
+static int audio_aio_events_pending(struct q6audio_aio *audio)
+{
+ unsigned long flags;
+ int empty;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ empty = !list_empty(&audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return empty || audio->event_abort;
+}
+
+static long audio_aio_process_event_req(struct q6audio_aio *audio,
+ void __user *arg)
+{
+ long rc;
+ struct msm_audio_event usr_evt;
+ struct audio_aio_event *drv_evt = NULL;
+ int timeout;
+ unsigned long flags;
+
+ if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event)))
+ return -EFAULT;
+
+ timeout = (int)usr_evt.timeout_ms;
+
+ if (timeout > 0) {
+ rc = wait_event_interruptible_timeout(audio->event_wait,
+ audio_aio_events_pending
+ (audio),
+ msecs_to_jiffies
+ (timeout));
+ if (rc == 0)
+ return -ETIMEDOUT;
+ } else {
+ rc = wait_event_interruptible(audio->event_wait,
+ audio_aio_events_pending(audio));
+ }
+ if (rc < 0)
+ return rc;
+
+ if (audio->event_abort) {
+ audio->event_abort = 0;
+ return -ENODEV;
+ }
+
+ rc = 0;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+ if (!list_empty(&audio->event_queue)) {
+ drv_evt = list_first_entry(&audio->event_queue,
+ struct audio_aio_event, list);
+ list_del(&drv_evt->list);
+ }
+ if (drv_evt) {
+ usr_evt.event_type = drv_evt->event_type;
+ usr_evt.event_payload = drv_evt->payload;
+ list_add_tail(&drv_evt->list, &audio->free_event_queue);
+ } else {
+ pr_err("%s[%p]:Unexpected path\n", __func__, audio);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return -EPERM;
+ }
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+ if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
+ pr_debug("%s[%p]:posted AUDIO_EVENT_WRITE_DONE to user\n",
+ __func__, audio);
+ mutex_lock(&audio->write_lock);
+ audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0, 0);
+ mutex_unlock(&audio->write_lock);
+ } else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+ pr_debug("%s[%p]:posted AUDIO_EVENT_READ_DONE to user\n",
+ __func__, audio);
+ mutex_lock(&audio->read_lock);
+ audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+ drv_evt->payload.aio_buf.buf_len, 0, 0);
+ mutex_unlock(&audio->read_lock);
+ }
+
+ /* Some read buffer might be held up in DSP,release all
+ * Once EOS indicated
+ */
+ if (audio->eos_rsp && !list_empty(&audio->in_queue)) {
+ pr_debug("%s[%p]:Send flush command to release read buffers"\
+ " held up in DSP\n", __func__, audio);
+ audio_aio_flush(audio);
+ }
+
+ if (copy_to_user(arg, &usr_evt, sizeof(usr_evt)))
+ rc = -EFAULT;
+
+ return rc;
+}
+
+static int audio_aio_ion_check(struct q6audio_aio *audio,
+ void *vaddr, unsigned long len)
+{
+ struct audio_aio_ion_region *region_elt;
+ struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len };
+
+ list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+ if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+ OVERLAPS(region_elt, &t)) {
+ pr_err("%s[%p]:region (vaddr %p len %ld) clashes with registered region (vaddr %p paddr %p len %ld)\n",
+ __func__, audio, vaddr, len,
+ region_elt->vaddr,
+ (void *)region_elt->paddr, region_elt->len);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int audio_aio_ion_add(struct q6audio_aio *audio,
+ struct msm_audio_ion_info *info)
+{
+ ion_phys_addr_t paddr = 0;
+ size_t len = 0;
+ struct audio_aio_ion_region *region;
+ int rc = -EINVAL;
+ struct ion_handle *handle = NULL;
+ unsigned long ionflag;
+ void *kvaddr = NULL;
+
+ pr_debug("%s[%p]:\n", __func__, audio);
+ region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+ if (!region) {
+ rc = -ENOMEM;
+ goto end;
+ }
+
+ rc = msm_audio_ion_import_legacy("Audio_Dec_Client", audio->client,
+ &handle, info->fd, &ionflag,
+ 0, &paddr, &len, &kvaddr);
+ if (rc) {
+ pr_err("%s: msm audio ion alloc failed\n", __func__);
+ goto import_error;
+ }
+
+ rc = audio_aio_ion_check(audio, info->vaddr, len);
+ if (rc < 0) {
+ pr_err("%s: audio_aio_ion_check failed\n", __func__);
+ goto ion_error;
+ }
+
+ region->handle = handle;
+ region->vaddr = info->vaddr;
+ region->fd = info->fd;
+ region->paddr = paddr;
+ region->kvaddr = (unsigned long)kvaddr;
+ region->len = len;
+ region->ref_cnt = 0;
+ pr_debug("%s[%p]:add region paddr %lx vaddr %p, len %lu kvaddr %lx\n",
+ __func__, audio,
+ region->paddr, region->vaddr, region->len, region->kvaddr);
+ list_add_tail(&region->list, &audio->ion_region_queue);
+ rc = q6asm_memory_map(audio->ac, (uint32_t) paddr, IN, (uint32_t) len,
+ 1);
+ if (rc < 0) {
+ pr_err("%s[%p]: memory map failed\n", __func__, audio);
+ goto mmap_error;
+ } else {
+ goto end;
+ }
+mmap_error:
+ list_del(&region->list);
+ion_error:
+ msm_audio_ion_free_legacy(audio->client, handle);
+import_error:
+ kfree(region);
+end:
+ return rc;
+}
+
+static int audio_aio_ion_remove(struct q6audio_aio *audio,
+ struct msm_audio_ion_info *info)
+{
+ struct audio_aio_ion_region *region;
+ struct list_head *ptr, *next;
+ int rc = -EINVAL;
+
+ pr_debug("%s[%p]:info fd %d vaddr %p\n",
+ __func__, audio, info->fd, info->vaddr);
+
+ list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+ region = list_entry(ptr, struct audio_aio_ion_region, list);
+
+ if ((region->fd == info->fd) &&
+ (region->vaddr == info->vaddr)) {
+ if (region->ref_cnt) {
+ pr_debug("%s[%p]:region %p in use ref_cnt %d\n",
+ __func__, audio, region,
+ region->ref_cnt);
+ break;
+ }
+ pr_debug("%s[%p]:remove region fd %d vaddr %p\n",
+ __func__, audio, info->fd, info->vaddr);
+ rc = q6asm_memory_unmap(audio->ac,
+ (uint32_t) region->paddr, IN);
+ if (rc < 0)
+ pr_err("%s[%p]: memory unmap failed\n",
+ __func__, audio);
+
+ list_del(&region->list);
+ msm_audio_ion_free_legacy(audio->client,
+ region->handle);
+ kfree(region);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static void audio_aio_async_write(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ int rc;
+ struct audio_client *ac;
+ struct audio_aio_write_param param;
+
+ if (!audio || !buf_node) {
+ pr_err("%s NULL pointer audio=[0x%p], buf_node=[0x%p]\n",
+ __func__, audio, buf_node);
+ return;
+ }
+ pr_debug("%s[%p]: Send write buff %p phy %lx len %d meta_enable = %d\n",
+ __func__, audio, buf_node, buf_node->paddr,
+ buf_node->buf.data_len,
+ audio->buf_cfg.meta_info_enable);
+ pr_debug("%s[%p]: flags = 0x%x\n", __func__, audio,
+ buf_node->meta_info.meta_in.nflags);
+
+ ac = audio->ac;
+ /* Offset with appropriate meta */
+ if (audio->feedback) {
+ /* Non Tunnel mode */
+ param.paddr = buf_node->paddr + sizeof(struct dec_meta_in);
+ param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in);
+ } else {
+ /* Tunnel mode */
+ param.paddr = buf_node->paddr;
+ param.len = buf_node->buf.data_len;
+ }
+ param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart;
+ param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart;
+ param.flags = buf_node->meta_info.meta_in.nflags;
+ /* If no meta_info enaled, indicate no time stamp valid */
+ if (!audio->buf_cfg.meta_info_enable)
+ param.flags = 0xFF00;
+
+ if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET)
+ param.flags |= AUDIO_DEC_EOF_SET;
+
+ param.uid = param.paddr;
+ /* Read command will populate paddr as token */
+ buf_node->token = param.paddr;
+ rc = q6asm_async_write(ac, &param);
+ if (rc < 0)
+ pr_err("%s[%p]:failed\n", __func__, audio);
+}
+
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+ union msm_audio_event_payload payload)
+{
+ struct audio_aio_event *e_node = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+ if (!list_empty(&audio->free_event_queue)) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&e_node->list);
+ } else {
+ e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC);
+ if (!e_node) {
+ pr_err("%s[%p]:No mem to post event %d\n",
+ __func__, audio, type);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ return;
+ }
+ }
+
+ e_node->event_type = type;
+ e_node->payload = payload;
+
+ list_add_tail(&e_node->list, &audio->event_queue);
+ spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+ wake_up(&audio->event_wait);
+}
+
+static void audio_aio_async_read(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node)
+{
+ struct audio_client *ac;
+ struct audio_aio_read_param param;
+ int rc;
+
+ pr_debug("%s[%p]: Send read buff %p phy %lx len %d\n",
+ __func__, audio, buf_node,
+ buf_node->paddr, buf_node->buf.buf_len);
+ ac = audio->ac;
+ /* Provide address so driver can append nr frames information */
+ param.paddr = buf_node->paddr +
+ sizeof(struct dec_meta_out);
+ param.len = buf_node->buf.buf_len -
+ sizeof(struct dec_meta_out);
+ param.uid = param.paddr;
+ /* Write command will populate paddr as token */
+ buf_node->token = param.paddr;
+ rc = q6asm_async_read(ac, &param);
+ if (rc < 0)
+ pr_err("%s[%p]:failed\n", __func__, audio);
+}
+
+static int audio_aio_buf_add(struct q6audio_aio *audio, unsigned dir,
+ void __user *arg)
+{
+ unsigned long flags;
+ struct audio_aio_buffer_node *buf_node;
+
+
+ buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);
+
+ if (!buf_node)
+ return -ENOMEM;
+
+ if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+ kfree(buf_node);
+ return -EFAULT;
+ }
+
+ pr_debug("%s[%p]:node %p dir %x buf_addr %p buf_len %d data_len %d\n",
+ __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, buf_node->buf.data_len);
+ buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
+ buf_node->buf.buf_len, 1,
+ &buf_node->kvaddr);
+ if (dir) {
+ /* write */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (!audio->feedback && !buf_node->buf.data_len)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ extract_meta_out_info(audio, buf_node, 1);
+ /* Not a EOS buffer */
+ if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) {
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio_aio_async_write(audio, buf_node);
+ /* EOS buffer handled in driver */
+ list_add_tail(&buf_node->list, &audio->out_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ } else if (buf_node->meta_info.meta_in.nflags
+ & AUDIO_DEC_EOS_SET) {
+ if (!audio->wflush) {
+ pr_debug("%s[%p]:Send EOS cmd at i/p\n",
+ __func__, audio);
+ /* Driver will forcefully post writedone event
+ * once eos ack recived from DSP
+ */
+ audio->eos_write_payload.aio_buf =\
+ buf_node->buf;
+ audio->eos_flag = 1;
+ audio->eos_rsp = 0;
+ q6asm_cmd(audio->ac, CMD_EOS);
+ kfree(buf_node);
+ } else { /* Flush in progress, send back i/p
+ * EOS buffer as is
+ */
+ union msm_audio_event_payload event_payload;
+ event_payload.aio_buf = buf_node->buf;
+ audio_aio_post_event(audio,
+ AUDIO_EVENT_WRITE_DONE,
+ event_payload);
+ kfree(buf_node);
+ }
+ }
+ } else {
+ /* read */
+ if (!buf_node->paddr ||
+ (buf_node->paddr & 0x1) ||
+ (buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
+ kfree(buf_node);
+ return -EINVAL;
+ }
+ /* No EOS reached */
+ if (!audio->eos_rsp) {
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ audio_aio_async_read(audio, buf_node);
+ /* EOS buffer handled in driver */
+ list_add_tail(&buf_node->list, &audio->in_queue);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+ /* EOS reached at input side fake all upcoming read buffer to
+ * indicate the same
+ */
+ else {
+ union msm_audio_event_payload event_payload;
+ event_payload.aio_buf = buf_node->buf;
+ event_payload.aio_buf.data_len =
+ insert_eos_buf(audio, buf_node);
+ pr_debug("%s[%p]: propagate READ_DONE as EOS done\n",\
+ __func__, audio);
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+ event_payload);
+ kfree(buf_node);
+ }
+ }
+ return 0;
+}
+
+void audio_aio_ioport_reset(struct q6audio_aio *audio)
+{
+ if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+ /* If fsync is in progress, make sure
+ * return value of fsync indicates
+ * abort due to flush
+ */
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p]:fsync in progress\n", __func__, audio);
+ audio->drv_ops.out_flush(audio);
+ } else
+ audio->drv_ops.out_flush(audio);
+ if (audio->feedback == NON_TUNNEL_MODE)
+ audio->drv_ops.in_flush(audio);
+ }
+}
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file)
+{
+ int rc = 0;
+ int i;
+ struct audio_aio_event *e_node = NULL;
+ struct list_head *ptr, *next;
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ audio->pcm_cfg.sample_rate = 48000;
+ audio->pcm_cfg.channel_count = 2;
+
+ /* Only AIO interface */
+ if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s[%p]:set to aio interface\n", __func__, audio);
+ audio->drv_status |= ADRV_STATUS_AIO_INTF;
+ audio->drv_ops.out_flush = audio_aio_async_out_flush;
+ audio->drv_ops.in_flush = audio_aio_async_in_flush;
+ q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
+ } else {
+ pr_err("%s[%p]:SIO interface not supported\n",
+ __func__, audio);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ /* Initialize all locks of audio instance */
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ mutex_init(&audio->get_event_lock);
+ spin_lock_init(&audio->dsp_lock);
+ spin_lock_init(&audio->event_queue_lock);
+ init_waitqueue_head(&audio->cmd_wait);
+ init_waitqueue_head(&audio->write_wait);
+ init_waitqueue_head(&audio->event_wait);
+ INIT_LIST_HEAD(&audio->out_queue);
+ INIT_LIST_HEAD(&audio->in_queue);
+ INIT_LIST_HEAD(&audio->ion_region_queue);
+ INIT_LIST_HEAD(&audio->free_event_queue);
+ INIT_LIST_HEAD(&audio->event_queue);
+
+ audio->drv_ops.out_flush(audio);
+ audio->opened = 1;
+ file->private_data = audio;
+ audio->codec_ioctl = audio_aio_ioctl;
+
+ for (i = 0; i < AUDIO_EVENT_NUM; i++) {
+ e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL);
+ if (e_node)
+ list_add_tail(&e_node->list, &audio->free_event_queue);
+ else {
+ pr_err("%s[%p]:event pkt alloc failed\n",
+ __func__, audio);
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ }
+ audio->client = msm_audio_ion_client_create(UINT_MAX,
+ "Audio_Dec_Client");
+ if (IS_ERR_OR_NULL(audio->client)) {
+ pr_err("Unable to create ION client\n");
+ rc = -ENOMEM;
+ goto cleanup;
+ }
+ pr_debug("Ion client create in audio_aio_open %p", audio->client);
+
+ rc = register_volume_listener(audio);
+ if (rc < 0)
+ goto ion_cleanup;
+
+ return 0;
+ion_cleanup:
+ msm_audio_ion_client_destroy(audio->client);
+ audio->client = NULL;
+cleanup:
+ list_for_each_safe(ptr, next, &audio->free_event_queue) {
+ e_node = list_first_entry(&audio->free_event_queue,
+ struct audio_aio_event, list);
+ list_del(&e_node->list);
+ kfree(e_node);
+ }
+fail:
+ return rc;
+}
+
+long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_GET_STATS: {
+ struct msm_audio_stats stats;
+ uint64_t timestamp;
+ memset(&stats, 0, sizeof(struct msm_audio_stats));
+ stats.byte_count = atomic_read(&audio->in_bytes);
+ stats.sample_count = atomic_read(&audio->in_samples);
+ rc = q6asm_get_session_time(audio->ac, &timestamp);
+ if (rc >= 0)
+ memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
+ else
+ pr_debug("Error while getting timestamp\n");
+ if (copy_to_user((void *)arg, &stats, sizeof(stats)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_GET_EVENT: {
+ pr_debug("%s[%p]:AUDIO_GET_EVENT\n", __func__, audio);
+ if (mutex_trylock(&audio->get_event_lock)) {
+ rc = audio_aio_process_event_req(audio,
+ (void __user *)arg);
+ mutex_unlock(&audio->get_event_lock);
+ } else
+ rc = -EBUSY;
+ break;
+ }
+ case AUDIO_ABORT_GET_EVENT: {
+ audio->event_abort = 1;
+ wake_up(&audio->event_wait);
+ break;
+ }
+ case AUDIO_ASYNC_WRITE: {
+ mutex_lock(&audio->write_lock);
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ rc = -EBUSY;
+ else {
+ if (audio->enabled)
+ rc = audio_aio_buf_add(audio, 1,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ }
+ mutex_unlock(&audio->write_lock);
+ break;
+ }
+ case AUDIO_ASYNC_READ: {
+ mutex_lock(&audio->read_lock);
+ if ((audio->feedback) && (audio->enabled))
+ rc = audio_aio_buf_add(audio, 0,
+ (void __user *)arg);
+ else
+ rc = -EPERM;
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+ case AUDIO_OUTPORT_FLUSH: {
+ pr_debug("%s[%p]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
+ mutex_lock(&audio->read_lock);
+ rc = audio_aio_outport_flush(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: AUDIO_OUTPORT_FLUSH failed\n",
+ __func__, audio);
+ rc = -EINTR;
+ }
+ mutex_unlock(&audio->read_lock);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s[%p]: AUDIO_STOP session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ mutex_lock(&audio->lock);
+ audio->stopped = 1;
+ rc = audio_aio_flush(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]:Audio Stop procedure failed rc=%d\n",
+ __func__, audio, rc);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->enabled = 0;
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_PAUSE: {
+ pr_debug("%s[%p]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
+ mutex_lock(&audio->lock);
+ if (arg == 1) {
+ rc = audio_aio_pause(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]: pause FAILED rc=%d\n",
+ __func__, audio, rc);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->drv_status |= ADRV_STATUS_PAUSE;
+ } else if (arg == 0) {
+ if (audio->drv_status & ADRV_STATUS_PAUSE) {
+ rc = audio_aio_enable(audio);
+ if (rc)
+ pr_err("%s[%p]: audio enable failed\n",
+ __func__, audio);
+ else {
+ audio->drv_status &= ~ADRV_STATUS_PAUSE;
+ audio->enabled = 1;
+ }
+ }
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_FLUSH: {
+ pr_debug("%s[%p]: AUDIO_FLUSH sessionid[%d]\n", __func__,
+ audio, audio->ac->session);
+ mutex_lock(&audio->lock);
+ audio->rflush = 1;
+ audio->wflush = 1;
+ if (audio->drv_status & ADRV_STATUS_FSYNC) {
+ pr_debug("%s[%p] Waking up the audio_aio_fsync\n",
+ __func__, audio);
+ wake_up(&audio->write_wait);
+ }
+ /* Flush DSP */
+ rc = audio_aio_flush(audio);
+ /* Flush input / Output buffer in software*/
+ audio_aio_ioport_reset(audio);
+ if (rc < 0) {
+ pr_err("%s[%p]:AUDIO_FLUSH interrupted\n",
+ __func__, audio);
+ rc = -EINTR;
+ } else {
+ audio->rflush = 0;
+ if (audio->drv_status & ADRV_STATUS_FSYNC)
+ wake_up(&audio->write_wait);
+ else
+ audio->wflush = 0;
+
+ }
+ audio->eos_flag = 0;
+ audio->eos_rsp = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_REGISTER_ION: {
+ struct msm_audio_ion_info info;
+ pr_debug("%s[%p]:AUDIO_REGISTER_ION\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&info, (void *)arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audio_aio_ion_add(audio, &info);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_DEREGISTER_ION: {
+ struct msm_audio_ion_info info;
+ mutex_lock(&audio->lock);
+ pr_debug("%s[%p]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+ if (copy_from_user(&info, (void *)arg, sizeof(info)))
+ rc = -EFAULT;
+ else
+ rc = audio_aio_ion_remove(audio, &info);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ mutex_lock(&audio->lock);
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.buffer_size = audio->str_cfg.buffer_size;
+ cfg.buffer_count = audio->str_cfg.buffer_count;
+ pr_debug("%s[%p]:GET STREAM CFG %d %d\n",
+ __func__, audio, cfg.buffer_size, cfg.buffer_count);
+ if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_STREAM_CONFIG: {
+ struct msm_audio_stream_config cfg;
+ pr_debug("%s[%p]:SET STREAM CONFIG\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_CONFIG: {
+ struct msm_audio_config cfg;
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg)))
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_CONFIG: {
+ struct msm_audio_config config;
+ pr_err("%s[%p]:AUDIO_SET_CONFIG\n", __func__, audio);
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&config, (void *)arg, sizeof(config))) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if (audio->feedback != NON_TUNNEL_MODE) {
+ pr_err("%s[%p]:Not sufficient permission to change the playback mode\n",
+ __func__, audio);
+ rc = -EACCES;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if ((config.buffer_count > PCM_BUF_COUNT) ||
+ (config.buffer_count == 1))
+ config.buffer_count = PCM_BUF_COUNT;
+
+ if (config.buffer_size < PCM_BUFSZ_MIN)
+ config.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->pcm_cfg.buffer_count = config.buffer_count;
+ audio->pcm_cfg.buffer_size = config.buffer_size;
+ audio->pcm_cfg.channel_count = config.channel_count;
+ audio->pcm_cfg.sample_rate = config.sample_rate;
+ rc = 0;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_SET_BUF_CFG: {
+ struct msm_audio_buf_cfg cfg;
+ mutex_lock(&audio->lock);
+ if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ if ((audio->feedback == NON_TUNNEL_MODE) &&
+ !cfg.meta_info_enable) {
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+
+ audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+ pr_debug("%s[%p]:session id %d: Set-buf-cfg: meta[%d]",
+ __func__, audio,
+ audio->ac->session, cfg.meta_info_enable);
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_BUF_CFG: {
+ pr_debug("%s[%p]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+ __func__, audio,
+ audio->ac->session, audio->buf_cfg.meta_info_enable,
+ audio->buf_cfg.frames_per_buf);
+
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->buf_cfg,
+ sizeof(struct msm_audio_buf_cfg)))
+ rc = -EFAULT;
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ case AUDIO_GET_SESSION_ID: {
+ mutex_lock(&audio->lock);
+ if (copy_to_user((void *)arg, &audio->ac->session,
+ sizeof(unsigned short))) {
+ rc = -EFAULT;
+ }
+ mutex_unlock(&audio->lock);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
new file mode 100644
index 000000000000..0efcd647f9d6
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
@@ -0,0 +1,221 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/msm_audio.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/msm_ion.h>
+#include <asm/ioctls.h>
+#include <asm/atomic.h>
+#include "q6audio_common.h"
+
+#define TUNNEL_MODE 0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */
+#define ADRV_STATUS_FSYNC 0x00000008
+#define ADRV_STATUS_PAUSE 0x00000010
+#define AUDIO_DEC_EOS_SET 0x00000001
+#define AUDIO_DEC_EOF_SET 0x00000010
+#define AUDIO_EVENT_NUM 10
+
+#define __CONTAINS(r, v, l) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __v = v; \
+ typeof(v) __e = __v + l; \
+ int res = ((__v >= __r->vaddr) && \
+ (__e <= __r->vaddr + __r->len)); \
+ res; \
+})
+
+#define CONTAINS(r1, r2) ({ \
+ typeof(r2) __r2 = r2; \
+ __CONTAINS(r1, __r2->vaddr, __r2->len); \
+})
+
+#define IN_RANGE(r, v) ({ \
+ typeof(r) __r = r; \
+ typeof(v) __vv = v; \
+ int res = ((__vv >= __r->vaddr) && \
+ (__vv < (__r->vaddr + __r->len))); \
+ res; \
+})
+
+#define OVERLAPS(r1, r2) ({ \
+ typeof(r1) __r1 = r1; \
+ typeof(r2) __r2 = r2; \
+ typeof(__r2->vaddr) __v = __r2->vaddr; \
+ typeof(__v) __e = __v + __r2->len - 1; \
+ int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+ res; \
+})
+
+struct timestamp {
+ unsigned long lowpart;
+ unsigned long highpart;
+} __packed;
+
+struct meta_out_dsp {
+ u32 offset_to_frame;
+ u32 frame_size;
+ u32 encoded_pcm_samples;
+ u32 msw_ts;
+ u32 lsw_ts;
+ u32 nflags;
+} __packed;
+
+struct dec_meta_in {
+ unsigned char reserved[18];
+ unsigned short offset;
+ struct timestamp ntimestamp;
+ unsigned int nflags;
+} __packed;
+
+struct dec_meta_out {
+ unsigned int reserved[7];
+ unsigned int num_of_frames;
+ struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+/* General meta field to store meta info
+locally */
+union meta_data {
+ struct dec_meta_out meta_out;
+ struct dec_meta_in meta_in;
+} __packed;
+
+#define PCM_BUF_COUNT (2)
+/* Buffer with meta */
+#define PCM_BUFSZ_MIN ((4*1024) + sizeof(struct dec_meta_out))
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM (2)
+#define FRAME_SIZE ((4*1536) + sizeof(struct dec_meta_in))
+
+struct audio_aio_ion_region {
+ struct list_head list;
+ struct ion_handle *handle;
+ int fd;
+ void *vaddr;
+ unsigned long paddr;
+ unsigned long kvaddr;
+ unsigned long len;
+ unsigned ref_cnt;
+};
+
+struct audio_aio_event {
+ struct list_head list;
+ int event_type;
+ union msm_audio_event_payload payload;
+};
+
+struct audio_aio_buffer_node {
+ struct list_head list;
+ struct msm_audio_aio_buf buf;
+ unsigned long paddr;
+ unsigned long token;
+ void *kvaddr;
+ union meta_data meta_info;
+};
+
+struct q6audio_aio;
+struct audio_aio_drv_operations {
+ void (*out_flush) (struct q6audio_aio *);
+ void (*in_flush) (struct q6audio_aio *);
+};
+
+struct q6audio_aio {
+ atomic_t in_bytes;
+ atomic_t in_samples;
+
+ struct msm_audio_stream_config str_cfg;
+ struct msm_audio_buf_cfg buf_cfg;
+ struct msm_audio_config pcm_cfg;
+ void *codec_cfg;
+
+ struct audio_client *ac;
+
+ struct mutex lock;
+ struct mutex read_lock;
+ struct mutex write_lock;
+ struct mutex get_event_lock;
+ wait_queue_head_t cmd_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t event_wait;
+ spinlock_t dsp_lock;
+ spinlock_t event_queue_lock;
+
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dentry;
+#endif
+ struct list_head out_queue; /* queue to retain output buffers */
+ struct list_head in_queue; /* queue to retain input buffers */
+ struct list_head free_event_queue;
+ struct list_head event_queue;
+ struct list_head ion_region_queue; /* protected by lock */
+ struct ion_client *client;
+ struct audio_aio_drv_operations drv_ops;
+ union msm_audio_event_payload eos_write_payload;
+ uint32_t device_events;
+ uint16_t volume;
+ uint32_t drv_status;
+ int event_abort;
+ int eos_rsp;
+ int eos_flag;
+ int opened;
+ int enabled;
+ int stopped;
+ int feedback;
+ int rflush; /* Read flush */
+ int wflush; /* Write flush */
+ long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload);
+
+int insert_eos_buf(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node);
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node, int dir);
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file);
+int audio_aio_enable(struct q6audio_aio *audio);
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+ union msm_audio_event_payload payload);
+int audio_aio_release(struct inode *inode, struct file *file);
+long audio_aio_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+void audio_aio_async_out_flush(struct q6audio_aio *audio);
+void audio_aio_async_in_flush(struct q6audio_aio *audio);
+void audio_aio_ioport_reset(struct q6audio_aio *audio);
+int enable_volume_ramp(struct q6audio_aio *audio);
+#ifdef CONFIG_DEBUG_FS
+ssize_t audio_aio_debug_open(struct inode *inode, struct file *file);
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+#endif
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c
new file mode 100644
index 000000000000..5e3de8609803
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c
@@ -0,0 +1,214 @@
+/* wma audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/types.h>
+#include <linux/msm_audio_wma.h>
+#include "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wma_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_wma_cfg wma_cfg;
+ struct msm_audio_wma_config_v2 *wma_config;
+ pr_debug("%s[%p]: AUDIO_START session_id[%d]\n", __func__,
+ audio, audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+ wma_cfg.format_tag = wma_config->format_tag;
+ wma_cfg.ch_cfg = wma_config->numchannels;
+ wma_cfg.sample_rate = wma_config->samplingrate;
+ wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond;
+ wma_cfg.block_align = wma_config->block_align;
+ wma_cfg.valid_bits_per_sample =
+ wma_config->validbitspersample;
+ wma_cfg.ch_mask = wma_config->channelmask;
+ wma_cfg.encode_opt = wma_config->encodeopt;
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_GET_WMA_CONFIG_V2: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_wma_config_v2))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ case AUDIO_SET_WMA_CONFIG_V2: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_wma_config_v2))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wma_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s:Could not allocate memory for wma"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_WMA_V9);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open WMA decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wma_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_wma_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__,
+ audio->feedback,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wma_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_wma_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wma",
+ .fops = &audio_wma_fops,
+};
+
+static int __init audio_wma_init(void)
+{
+ return misc_register(&audio_wma_misc);
+}
+
+device_initcall(audio_wma_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
new file mode 100644
index 000000000000..ce49cac47fa5
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
@@ -0,0 +1,273 @@
+/* wmapro audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ *
+ * 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/types.h>
+#include <linux/msm_audio_wmapro.h>
+#include "audio_utils_aio.h"
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wmapro_debug_fops = {
+ .read = audio_aio_debug_read,
+ .open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_aio *audio = file->private_data;
+ int rc = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct asm_wmapro_cfg wmapro_cfg;
+ struct msm_audio_wmapro_config *wmapro_config;
+ pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+ audio->ac->session);
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ /* Configure PCM output block */
+ rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+ if (rc < 0) {
+ pr_err("pcm output block config failed\n");
+ break;
+ }
+ }
+ wmapro_config = (struct msm_audio_wmapro_config *)
+ audio->codec_cfg;
+ if ((wmapro_config->formattag == 0x162) ||
+ (wmapro_config->formattag == 0x163) ||
+ (wmapro_config->formattag == 0x166) ||
+ (wmapro_config->formattag == 0x167)) {
+ wmapro_cfg.format_tag = wmapro_config->formattag;
+ } else {
+ pr_err("%s:AUDIO_START failed: formattag = %d\n",
+ __func__, wmapro_config->formattag);
+ rc = -EINVAL;
+ break;
+ }
+ if ((wmapro_config->numchannels == 1) ||
+ (wmapro_config->numchannels == 2)) {
+ wmapro_cfg.ch_cfg = wmapro_config->numchannels;
+ } else {
+ pr_err("%s:AUDIO_START failed: channels = %d\n",
+ __func__, wmapro_config->numchannels);
+ rc = -EINVAL;
+ break;
+ }
+ if ((wmapro_config->samplingrate <= 48000) ||
+ (wmapro_config->samplingrate > 0)) {
+ wmapro_cfg.sample_rate =
+ wmapro_config->samplingrate;
+ } else {
+ pr_err("%s:AUDIO_START failed: sample_rate = %d\n",
+ __func__, wmapro_config->samplingrate);
+ rc = -EINVAL;
+ break;
+ }
+ wmapro_cfg.avg_bytes_per_sec =
+ wmapro_config->avgbytespersecond;
+ if ((wmapro_config->asfpacketlength <= 13376) ||
+ (wmapro_config->asfpacketlength > 0)) {
+ wmapro_cfg.block_align =
+ wmapro_config->asfpacketlength;
+ } else {
+ pr_err("%s:AUDIO_START failed: block_align = %d\n",
+ __func__, wmapro_config->asfpacketlength);
+ rc = -EINVAL;
+ break;
+ }
+ if ((wmapro_config->validbitspersample == 16) ||
+ (wmapro_config->validbitspersample == 24)) {
+ wmapro_cfg.valid_bits_per_sample =
+ wmapro_config->validbitspersample;
+ } else {
+ pr_err("%s:AUDIO_START failed: bitspersample = %d\n",
+ __func__,
+ wmapro_config->validbitspersample);
+ rc = -EINVAL;
+ break;
+ }
+ if ((wmapro_config->channelmask == 4) ||
+ (wmapro_config->channelmask == 3)) {
+ wmapro_cfg.ch_mask = wmapro_config->channelmask;
+ } else {
+ pr_err("%s:AUDIO_START failed: channel_mask = %d\n",
+ __func__, wmapro_config->channelmask);
+ rc = -EINVAL;
+ break;
+ }
+ wmapro_cfg.encode_opt = wmapro_config->encodeopt;
+ wmapro_cfg.adv_encode_opt =
+ wmapro_config->advancedencodeopt;
+ wmapro_cfg.adv_encode_opt2 =
+ wmapro_config->advancedencodeopt2;
+ /* Configure Media format block */
+ rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg);
+ if (rc < 0) {
+ pr_err("cmd media format block failed\n");
+ break;
+ }
+ rc = audio_aio_enable(audio);
+ audio->eos_rsp = 0;
+ audio->eos_flag = 0;
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("Audio Start procedure failed rc=%d\n", rc);
+ break;
+ }
+ pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+ if (audio->stopped == 1)
+ audio->stopped = 0;
+ break;
+ }
+ case AUDIO_GET_WMAPRO_CONFIG: {
+ if (copy_to_user((void *)arg, audio->codec_cfg,
+ sizeof(struct msm_audio_wmapro_config))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+ case AUDIO_SET_WMAPRO_CONFIG: {
+ if (copy_from_user(audio->codec_cfg, (void *)arg,
+ sizeof(struct msm_audio_wmapro_config))) {
+ rc = -EFAULT;
+ break;
+ }
+ break;
+ }
+ default:
+ pr_debug("%s[%p]: Calling utils ioctl\n", __func__, audio);
+ rc = audio->codec_ioctl(file, cmd, arg);
+ if (rc)
+ pr_err("Failed in utils_ioctl: %d\n", rc);
+ }
+ return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_aio *audio = NULL;
+ int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+ /* 4 bytes represents decoder number, 1 byte for terminate string */
+ char name[sizeof "msm_wmapro_" + 5];
+#endif
+ audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("Could not allocate memory for wma decode driver\n");
+ return -ENOMEM;
+ }
+ audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config),
+ GFP_KERNEL);
+ if (audio->codec_cfg == NULL) {
+ pr_err("%s: Could not allocate memory for wmapro"
+ "config\n", __func__);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+
+ audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("Could not allocate memory for audio client\n");
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+ FORMAT_WMA_V10PRO);
+ if (rc < 0) {
+ pr_err("NT mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = NON_TUNNEL_MODE;
+ /* open WMA decoder, expected frames is always 1*/
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ } else if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_mode & FMODE_READ)) {
+ rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO);
+ if (rc < 0) {
+ pr_err("T mode Open failed rc=%d\n", rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ audio->feedback = TUNNEL_MODE;
+ audio->buf_cfg.meta_info_enable = 0x00;
+ } else {
+ pr_err("Not supported mode\n");
+ rc = -EACCES;
+ goto fail;
+ }
+ rc = audio_aio_open(audio, file);
+ if (rc < 0) {
+ pr_err("audio_aio_open rc=%d\n", rc);
+ goto fail;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ snprintf(name, sizeof name, "msm_wmapro_%04x", audio->ac->session);
+ audio->dentry = debugfs_create_file(name, S_IFREG | S_IRUGO,
+ NULL, (void *)audio,
+ &audio_wmapro_debug_fops);
+
+ if (IS_ERR(audio->dentry))
+ pr_debug("debugfs_create_file failed\n");
+#endif
+ pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__,
+ audio->ac->session);
+ return rc;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->codec_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_wmapro_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_open,
+ .release = audio_aio_release,
+ .unlocked_ioctl = audio_ioctl,
+ .fsync = audio_aio_fsync,
+};
+
+struct miscdevice audio_wmapro_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_wmapro",
+ .fops = &audio_wmapro_fops,
+};
+
+static int __init audio_wmapro_init(void)
+{
+ return misc_register(&audio_wmapro_misc);
+}
+
+device_initcall(audio_wmapro_init);
diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c
new file mode 100644
index 000000000000..a7852662d546
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c
@@ -0,0 +1,293 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((23+sizeof(struct meta_out_dsp)) * 10))
+
+/* ------------------- device --------------------- */
+static long evrc_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ /* rate_modulation_cmd set to zero
+ currently not configurable from user space */
+ rc = q6asm_enc_cfg_blk_evrc(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate, 0);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd evrc media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_EVRC_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_evrc_enc_config)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_EVRC_ENC_CONFIG: {
+ struct msm_audio_evrc_enc_config cfg;
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(struct msm_audio_evrc_enc_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (cfg.min_bit_rate > 4 ||
+ cfg.min_bit_rate < 1 ||
+ (cfg.min_bit_rate == 2)) {
+ pr_err("%s:session id %d: invalid min bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg.max_bit_rate > 4 ||
+ cfg.max_bit_rate < 1 ||
+ (cfg.max_bit_rate == 2)) {
+ pr_err("%s:session id %d: invalid max bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->min_bit_rate = cfg.min_bit_rate;
+ enc_cfg->max_bit_rate = cfg.max_bit_rate;
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
+ audio->ac->session, enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int evrc_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_evrc_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for evrc driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 23;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->min_bit_rate = 4;
+ enc_cfg->max_bit_rate = 4;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open evrc encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n",
+ __func__, audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_EVRC);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__,
+ audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_ioctl = evrc_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = evrc_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+struct miscdevice audio_evrc_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_evrc_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init evrc_in_init(void)
+{
+ return misc_register(&audio_evrc_in_misc);
+}
+
+device_initcall(evrc_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
new file mode 100644
index 000000000000..7fcab43b8922
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+*/
+
+
+/* For Decoders */
+#ifndef __Q6_AUDIO_COMMON_H__
+#define __Q6_AUDIO_COMMON_H__
+
+#if defined(CONFIG_ARCH_MSM8974) \
+ || defined(CONFIG_ARCH_MSM8226) || defined(CONFIG_ARCH_MSM8610) \
+ || defined(CONFIG_ARCH_APQ8084) || defined(CONFIG_ARCH_MPQ8092)
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#else
+#include <sound/apr_audio.h>
+#include <sound/q6asm.h>
+#endif
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *audio);
+
+
+/* For Encoders */
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv);
+
+void audio_in_get_dsp_frames(void *audio,
+ uint32_t token, uint32_t *payload);
+
+#endif /*__Q6_AUDIO_COMMON_H__*/
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
new file mode 100644
index 000000000000..7786fc01bcfd
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
@@ -0,0 +1,98 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_in *audio = (struct q6audio_in *)priv;
+ unsigned long flags;
+
+ pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ switch (opcode) {
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ audio_in_get_dsp_frames(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ atomic_inc(&audio->in_count);
+ wake_up(&audio->write_wait);
+ break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ audio->eos_rsp = 1;
+ wake_up(&audio->read_wait);
+ break;
+ case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+ break;
+ case ASM_SESSION_EVENTX_OVERFLOW:
+ pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
+ __func__, audio->ac->session);
+ break;
+ case RESET_EVENTS:
+ pr_debug("%s:received RESET EVENTS\n", __func__);
+ audio->enabled = 0;
+ audio->stopped = 1;
+ audio->event_abort = 1;
+ wake_up(&audio->read_wait);
+ wake_up(&audio->write_wait);
+ break;
+ default:
+ pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
+ audio->ac->session, opcode);
+ break;
+ }
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+void audio_in_get_dsp_frames(void *priv,
+ uint32_t token, uint32_t *payload)
+{
+ struct q6audio_in *audio = (struct q6audio_in *)priv;
+ uint32_t index;
+
+ index = token;
+ pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
+ __func__, audio->ac->session, token, payload[9],
+ payload[5]);
+ pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
+ audio->ac->session, payload[7], payload[6]);
+ pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
+ audio->ac->session, payload[8], payload[10]);
+ pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__,
+ audio->ac->session, payload[4]);
+
+ audio->out_frame_info[index][0] = payload[9];
+ audio->out_frame_info[index][1] = payload[5];
+
+ /* statistics of read */
+ atomic_add(payload[4], &audio->in_bytes);
+ atomic_add(payload[9], &audio->in_samples);
+
+ if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
+ atomic_inc(&audio->out_count);
+ wake_up(&audio->read_wait);
+ }
+}
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
new file mode 100644
index 000000000000..21040b15e1ce
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
@@ -0,0 +1,219 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils_aio.h"
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+
+ pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+ case RESET_EVENTS:
+ audio_aio_cb(opcode, token, payload, audio);
+ break;
+ default:
+ pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
+ break;
+ }
+}
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+ uint32_t *payload, void *priv/*struct q6audio_aio *audio*/)
+{
+ struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+ union msm_audio_event_payload e_payload;
+
+ switch (opcode) {
+ case ASM_DATA_EVENT_WRITE_DONE_V2:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_write_ack(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_READ_DONE_V2:
+ pr_debug("%s[%p]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
+ __func__, audio, token);
+ audio_aio_async_read_ack(audio, token, payload);
+ break;
+ case ASM_DATA_EVENT_RENDERED_EOS:
+ /* EOS Handle */
+ pr_debug("%s[%p]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
+ if (audio->feedback) { /* Non-Tunnel mode */
+ audio->eos_rsp = 1;
+ /* propagate input EOS i/p buffer,
+ after receiving DSP acknowledgement */
+ if (audio->eos_flag &&
+ (audio->eos_write_payload.aio_buf.buf_addr)) {
+ audio_aio_post_event(audio,
+ AUDIO_EVENT_WRITE_DONE,
+ audio->eos_write_payload);
+ memset(&audio->eos_write_payload , 0,
+ sizeof(union msm_audio_event_payload));
+ audio->eos_flag = 0;
+ }
+ } else { /* Tunnel mode */
+ audio->eos_rsp = 1;
+ wake_up(&audio->write_wait);
+ wake_up(&audio->cmd_wait);
+ }
+ break;
+ case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+ case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+ pr_debug("%s[%p]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
+ __func__, audio, payload[0], payload[1], opcode);
+ break;
+ case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+ case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
+ __func__, audio, payload[0],
+ payload[1], payload[2], payload[3]);
+
+ pr_debug("%s[%p]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
+ __func__, audio, audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ audio->pcm_cfg.sample_rate = payload[0];
+ audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
+ e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
+ e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
+ audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+ break;
+ case RESET_EVENTS:
+ pr_debug("%s: Received opcode:0x%x\n", __func__, opcode);
+ audio->stopped = 1;
+ wake_up(&audio->event_wait);
+ break;
+ default:
+ break;
+ }
+}
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+ struct audio_aio_buffer_node *buf_node, int dir)
+{
+ struct dec_meta_out *meta_data = buf_node->kvaddr;
+ uint32_t temp;
+
+ if (dir) { /* input buffer - Write */
+ if (audio->buf_cfg.meta_info_enable)
+ memcpy(&buf_node->meta_info.meta_in,
+ (char *)buf_node->kvaddr, sizeof(struct dec_meta_in));
+ else
+ memset(&buf_node->meta_info.meta_in,
+ 0, sizeof(struct dec_meta_in));
+ pr_debug("%s[%p]:i/p: msw_ts 0x%lx lsw_ts 0x%lx nflags 0x%8x\n",
+ __func__, audio,
+ buf_node->meta_info.meta_in.ntimestamp.highpart,
+ buf_node->meta_info.meta_in.ntimestamp.lowpart,
+ buf_node->meta_info.meta_in.nflags);
+ } else { /* output buffer - Read */
+ memcpy((char *)buf_node->kvaddr,
+ &buf_node->meta_info.meta_out,
+ sizeof(struct dec_meta_out));
+ meta_data->meta_out_dsp[0].nflags = 0x00000000;
+ temp = meta_data->meta_out_dsp[0].msw_ts;
+ meta_data->meta_out_dsp[0].msw_ts =
+ meta_data->meta_out_dsp[0].lsw_ts;
+ meta_data->meta_out_dsp[0].lsw_ts = temp;
+
+ pr_debug("%s[%p]:o/p: msw_ts 0x%8x lsw_ts 0x%8x nflags 0x%8x, num_frames = %d\n",
+ __func__, audio,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].msw_ts,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].lsw_ts,
+ ((struct dec_meta_out *)buf_node->kvaddr)->\
+ meta_out_dsp[0].nflags,
+ ((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames);
+ }
+}
+
+/* Read buffer from DSP / Handle Ack from DSP */
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+ uint32_t *payload)
+{
+ unsigned long flags;
+ union msm_audio_event_payload event_payload;
+ struct audio_aio_buffer_node *filled_buf;
+ pr_debug("%s\n", __func__);
+
+ /* No active flush in progress */
+ if (audio->rflush)
+ return;
+
+ /* Statistics of read */
+ atomic_add(payload[4], &audio->in_bytes);
+ atomic_add(payload[9], &audio->in_samples);
+
+ spin_lock_irqsave(&audio->dsp_lock, flags);
+ if (list_empty(&audio->in_queue)) {
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ pr_warning("%s unexpected ack from dsp\n", __func__);
+ return;
+ }
+ filled_buf = list_first_entry(&audio->in_queue,
+ struct audio_aio_buffer_node, list);
+
+ pr_debug("%s token: 0x[%d], filled_buf->token: 0x[%lu]",
+ __func__, token, filled_buf->token);
+ if (token == (filled_buf->token)) {
+ list_del(&filled_buf->list);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ event_payload.aio_buf = filled_buf->buf;
+ /* Read done Buffer due to flush/normal condition
+ after EOS event, so append EOS buffer */
+ if (audio->eos_rsp == 0x1) {
+ event_payload.aio_buf.data_len =
+ insert_eos_buf(audio, filled_buf);
+ /* Reset flag back to indicate eos intimated */
+ audio->eos_rsp = 0;
+ } else {
+ filled_buf->meta_info.meta_out.num_of_frames\
+ = payload[9];
+ event_payload.aio_buf.data_len = payload[4]\
+ + payload[5] + sizeof(struct dec_meta_out);
+ pr_debug("%s[%p]:nr of frames 0x%8x len=%d\n",
+ __func__, audio,
+ filled_buf->meta_info.meta_out.num_of_frames,
+ event_payload.aio_buf.data_len);
+ extract_meta_out_info(audio, filled_buf, 0);
+ audio->eos_rsp = 0;
+ }
+ pr_debug("%s, posting read done to the app here\n", __func__);
+ audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+ event_payload);
+ kfree(filled_buf);
+ } else {
+ pr_err("%s[%p]:expected=%lx ret=%x\n",
+ __func__, audio, filled_buf->token, token);
+ spin_unlock_irqrestore(&audio->dsp_lock, flags);
+ }
+}
diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
new file mode 100644
index 000000000000..3a5411ed2625
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
@@ -0,0 +1,291 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE (4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE (1 + ((35+sizeof(struct meta_out_dsp)) * 10))
+
+/* ------------------- device --------------------- */
+static long qcelp_in_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct q6audio_in *audio = file->private_data;
+ int rc = 0;
+ int cnt = 0;
+
+ switch (cmd) {
+ case AUDIO_START: {
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+ audio->ac->session, audio->buf_alloc);
+ if (audio->enabled == 1) {
+ pr_info("%s:AUDIO_START already over\n", __func__);
+ rc = 0;
+ break;
+ }
+ rc = audio_in_buf_alloc(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: buffer allocation failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+
+ /* reduced_rate_level, rate_modulation_cmd set to zero
+ currently not configurable from user space */
+ rc = q6asm_enc_cfg_blk_qcelp(audio->ac,
+ audio->buf_cfg.frames_per_buf,
+ enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate, 0, 0);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: cmd qcelp media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ if (audio->feedback == NON_TUNNEL_MODE) {
+ rc = q6asm_media_format_block_pcm(audio->ac,
+ audio->pcm_cfg.sample_rate,
+ audio->pcm_cfg.channel_count);
+
+ if (rc < 0) {
+ pr_err("%s:session id %d: media format block failed\n",
+ __func__, audio->ac->session);
+ break;
+ }
+ }
+ pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
+ audio->ac->session, audio->enabled);
+ rc = audio_in_enable(audio);
+ if (!rc) {
+ audio->enabled = 1;
+ } else {
+ audio->enabled = 0;
+ pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ break;
+ }
+ while (cnt++ < audio->str_cfg.buffer_count)
+ q6asm_read(audio->ac); /* Push buffer to DSP */
+ rc = 0;
+ pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+ __func__, audio->ac->session, audio->enabled);
+ break;
+ }
+ case AUDIO_STOP: {
+ pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+ audio->ac->session);
+ rc = audio_in_disable(audio);
+ if (rc < 0) {
+ pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+ __func__, audio->ac->session,
+ rc);
+ break;
+ }
+ break;
+ }
+ case AUDIO_GET_QCELP_ENC_CONFIG: {
+ if (copy_to_user((void *)arg, audio->enc_cfg,
+ sizeof(struct msm_audio_qcelp_enc_config)))
+ rc = -EFAULT;
+ break;
+ }
+ case AUDIO_SET_QCELP_ENC_CONFIG: {
+ struct msm_audio_qcelp_enc_config cfg;
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ enc_cfg = audio->enc_cfg;
+ if (copy_from_user(&cfg, (void *) arg,
+ sizeof(struct msm_audio_qcelp_enc_config))) {
+ rc = -EFAULT;
+ break;
+ }
+
+ if (cfg.min_bit_rate > 4 ||
+ cfg.min_bit_rate < 1) {
+ pr_err("%s:session id %d: invalid min bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ if (cfg.max_bit_rate > 4 ||
+ cfg.max_bit_rate < 1) {
+ pr_err("%s:session id %d: invalid max bitrate\n",
+ __func__, audio->ac->session);
+ rc = -EINVAL;
+ break;
+ }
+ enc_cfg->min_bit_rate = cfg.min_bit_rate;
+ enc_cfg->max_bit_rate = cfg.max_bit_rate;
+ pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+ __func__,
+ audio->ac->session, enc_cfg->min_bit_rate,
+ enc_cfg->max_bit_rate);
+ break;
+ }
+ default:
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+static int qcelp_in_open(struct inode *inode, struct file *file)
+{
+ struct q6audio_in *audio = NULL;
+ struct msm_audio_qcelp_enc_config *enc_cfg;
+ int rc = 0;
+
+ audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+ if (audio == NULL) {
+ pr_err("%s: Could not allocate memory for qcelp driver\n",
+ __func__);
+ return -ENOMEM;
+ }
+ /* Allocate memory for encoder config param */
+ audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
+ GFP_KERNEL);
+ if (audio->enc_cfg == NULL) {
+ pr_err("%s:session id %d: Could not allocate memory for aac config param\n",
+ __func__, audio->ac->session);
+ kfree(audio);
+ return -ENOMEM;
+ }
+ enc_cfg = audio->enc_cfg;
+
+ mutex_init(&audio->lock);
+ mutex_init(&audio->read_lock);
+ mutex_init(&audio->write_lock);
+ spin_lock_init(&audio->dsp_lock);
+ init_waitqueue_head(&audio->read_wait);
+ init_waitqueue_head(&audio->write_wait);
+
+ /* Settings will be re-config at AUDIO_SET_CONFIG,
+ * but at least we need to have initial config
+ */
+ audio->str_cfg.buffer_size = FRAME_SIZE;
+ audio->str_cfg.buffer_count = FRAME_NUM;
+ audio->min_frame_size = 35;
+ audio->max_frames_per_buf = 10;
+ audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+ audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+ enc_cfg->min_bit_rate = 4;
+ enc_cfg->max_bit_rate = 4;
+ audio->pcm_cfg.channel_count = 1;
+ audio->pcm_cfg.sample_rate = 8000;
+ audio->buf_cfg.meta_info_enable = 0x01;
+ audio->buf_cfg.frames_per_buf = 0x01;
+ audio->event_abort = 0;
+
+ audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+ (void *)audio);
+
+ if (!audio->ac) {
+ pr_err("%s: Could not allocate memory for audio client\n",
+ __func__);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return -ENOMEM;
+ }
+
+ /* open qcelp encoder in T/NT mode */
+ if ((file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = NON_TUNNEL_MODE;
+ rc = q6asm_open_read_write(audio->ac, FORMAT_V13K,
+ FORMAT_LINEAR_PCM);
+ if (rc < 0) {
+ pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+ audio->ac->session);
+ } else if (!(file->f_mode & FMODE_WRITE) &&
+ (file->f_mode & FMODE_READ)) {
+ audio->feedback = TUNNEL_MODE;
+ rc = q6asm_open_read(audio->ac, FORMAT_V13K);
+ if (rc < 0) {
+ pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ /* register for tx overflow (valid for tunnel mode only) */
+ rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+ if (rc < 0) {
+ pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+ __func__, audio->ac->session, rc);
+ rc = -ENODEV;
+ goto fail;
+ }
+ pr_info("%s:session id %d: T mode encoder success\n", __func__,
+ audio->ac->session);
+ } else {
+ pr_err("%s:session id %d: Unexpected mode\n", __func__,
+ audio->ac->session);
+ rc = -EACCES;
+ goto fail;
+ }
+
+ audio->opened = 1;
+ atomic_set(&audio->in_count, PCM_BUF_COUNT);
+ atomic_set(&audio->out_count, 0x00);
+ audio->enc_ioctl = qcelp_in_ioctl;
+ file->private_data = audio;
+
+ pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+ return 0;
+fail:
+ q6asm_audio_client_free(audio->ac);
+ kfree(audio->enc_cfg);
+ kfree(audio);
+ return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+ .owner = THIS_MODULE,
+ .open = qcelp_in_open,
+ .release = audio_in_release,
+ .read = audio_in_read,
+ .write = audio_in_write,
+ .unlocked_ioctl = audio_in_ioctl,
+};
+
+struct miscdevice audio_qcelp_in_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "msm_qcelp_in",
+ .fops = &audio_in_fops,
+};
+
+static int __init qcelp_in_init(void)
+{
+ return misc_register(&audio_qcelp_in_misc);
+}
+
+device_initcall(qcelp_in_init);
diff --git a/drivers/misc/qfp_fuse.c b/drivers/misc/qfp_fuse.c
index 428d9ffced93..9c97df998f31 100644
--- a/drivers/misc/qfp_fuse.c
+++ b/drivers/misc/qfp_fuse.c
@@ -355,7 +355,7 @@ static int qfp_get_resource(struct platform_device *pdev,
if (pdev->dev.of_node) {
struct device_node *np = pdev->dev.of_node;
- if (of_property_read_u32(np, "qti,blow-status-offset",
+ if (of_property_read_u32(np, "qcom,blow-status-offset",
&blow_status_offset) == 0) {
if ((res->start + blow_status_offset) > res->end) {
pr_err("Invalid blow-status-offset\n");
@@ -372,7 +372,7 @@ static int qfp_get_resource(struct platform_device *pdev,
return -EINVAL;
}
- of_property_read_u32(np, "qti,blow-timer", &blow_timer);
+ of_property_read_u32(np, "qcom,blow-timer", &blow_timer);
} else {
regulator_name = pdev->dev.platform_data;
@@ -460,7 +460,7 @@ static int qfp_fuse_remove(struct platform_device *plat)
}
static struct of_device_id __attribute__ ((unused)) qfp_fuse_of_match[] = {
- { .compatible = "qti,qfp-fuse", },
+ { .compatible = "qcom,qfp-fuse", },
{}
};
diff --git a/drivers/misc/qpnp-misc.c b/drivers/misc/qpnp-misc.c
index c5621f8244d7..4b5d7a03aa18 100644
--- a/drivers/misc/qpnp-misc.c
+++ b/drivers/misc/qpnp-misc.c
@@ -106,9 +106,9 @@ int qpnp_misc_irqs_available(struct device *consumer_dev)
return -EINVAL;
}
- misc_node = of_parse_phandle(consumer_dev->of_node, "qti,misc-ref", 0);
+ misc_node = of_parse_phandle(consumer_dev->of_node, "qcom,misc-ref", 0);
if (!misc_node) {
- pr_debug("Could not find qti,misc-ref property in %s\n",
+ pr_debug("Could not find qcom,misc-ref property in %s\n",
consumer_dev->of_node->full_name);
return 0;
}
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 0d2f30b4a1df..91400d1a8f02 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -34,6 +34,7 @@
#include <linux/firmware.h>
#include <linux/freezer.h>
#include <linux/scatterlist.h>
+#include <linux/delay.h>
#include <mach/board.h>
#include <mach/msm_bus.h>
#include <mach/msm_bus_board.h>
@@ -156,6 +157,7 @@ struct qseecom_control {
uint32_t qsee_version;
struct device *pdev;
bool commonlib_loaded;
+ struct ion_handle *cmnlib_ion_handle;
struct ce_hw_usage_info ce_info;
int qsee_bw_count;
@@ -203,25 +205,15 @@ struct qseecom_dev_handle {
enum qseecom_bandwidth_request_mode mode;
};
-enum qseecom_set_clear_key_flag {
- QSEECOM_CLEAR_CE_KEY_CMD = 0,
- QSEECOM_SET_CE_KEY_CMD,
-};
-
-struct qseecom_set_key_parameter {
- uint32_t ce_hw;
- uint32_t pipe;
- uint32_t flags;
- uint8_t key_id[QSEECOM_KEY_ID_SIZE];
- unsigned char hash32[QSEECOM_HASH_SIZE];
- enum qseecom_set_clear_key_flag set_clear_key_flag;
-};
-
struct qseecom_sg_entry {
uint32_t phys_addr;
uint32_t len;
};
+uint8_t *key_id_array[QSEECOM_KEY_ID_SIZE] = {
+ "Disk Encryption"
+};
+
/* Function proto types */
static int qsee_vote_for_clock(struct qseecom_dev_handle *, int32_t);
static void qsee_disable_clock_vote(struct qseecom_dev_handle *, int32_t);
@@ -1467,7 +1459,7 @@ static int qseecom_receive_req(struct qseecom_dev_handle *data)
if (wait_event_freezable(this_lstnr->rcv_req_wq,
__qseecom_listener_has_rcvd_req(data,
this_lstnr))) {
- pr_warning("Interrupted: exiting Listener Service = %d\n",
+ pr_debug("Interrupted: exiting Listener Service = %d\n",
(uint32_t)data->listener.id);
/* woken up for different reason */
return -ERESTARTSYS;
@@ -1669,32 +1661,52 @@ static int __qseecom_load_fw(struct qseecom_dev_handle *data, char *appname)
static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
{
- int32_t ret = 0;
+ int ret = 0;
+ int len = 0;
uint32_t fw_size = 0;
struct qseecom_load_app_ireq load_req = {0, 0, 0, 0};
struct qseecom_command_scm_resp resp;
u8 *img_data = NULL;
+ ion_phys_addr_t pa;
if (__qseecom_get_fw_size("cmnlib", &fw_size))
return -EIO;
- img_data = kzalloc(fw_size, GFP_KERNEL);
- if (!img_data) {
- pr_err("Mem allocation for lib image data failed\n");
+ qseecom.cmnlib_ion_handle = ion_alloc(qseecom.ion_clnt, fw_size,
+ SZ_4K, ION_HEAP(ION_QSECOM_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL(qseecom.cmnlib_ion_handle)) {
+ pr_err("ION alloc failed\n");
return -ENOMEM;
}
+
+ img_data = (u8 *)ion_map_kernel(qseecom.ion_clnt,
+ qseecom.cmnlib_ion_handle);
+ if (IS_ERR_OR_NULL(img_data)) {
+ pr_err("ION memory mapping for cmnlib failed\n");
+ ret = -ENOMEM;
+ goto exit_ion_free;
+ }
ret = __qseecom_get_fw_data("cmnlib", img_data, &load_req);
if (ret) {
- kzfree(img_data);
- return -EIO;
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
+ }
+ /* Get the physical address of the ION BUF */
+ ret = ion_phys(qseecom.ion_clnt, qseecom.cmnlib_ion_handle,
+ &pa, &len);
+ load_req.phy_addr = (s32)pa;
+ if (ret) {
+ pr_err("physical memory retrieval failure\n");
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
}
/* Populate the remaining parameters */
load_req.qsee_cmd_id = QSEOS_LOAD_SERV_IMAGE_COMMAND;
/* Vote for the SFPB clock */
ret = __qseecom_enable_clk_scale_up(data);
if (ret) {
- kzfree(img_data);
- return -EIO;
+ ret = -EIO;
+ goto exit_ion_unmap_kernel;
}
__cpuc_flush_dcache_area((void *)img_data, fw_size);
@@ -1705,31 +1717,41 @@ static int qseecom_load_commonlib_image(struct qseecom_dev_handle *data)
if (ret) {
pr_err("scm_call to load failed : ret %d\n", ret);
ret = -EIO;
- } else {
- switch (resp.result) {
- case QSEOS_RESULT_SUCCESS:
- break;
- case QSEOS_RESULT_FAILURE:
- pr_err("scm call failed w/response result%d\n",
- resp.result);
- ret = -EINVAL;
- break;
- case QSEOS_RESULT_INCOMPLETE:
- ret = __qseecom_process_incomplete_cmd(data, &resp);
- if (ret)
- pr_err("process_incomplete_cmd failed err: %d\n",
- ret);
- break;
- default:
- pr_err("scm call return unknown response %d\n",
- resp.result);
- ret = -EINVAL;
- break;
+ goto exit_disable_clk_vote;
+ }
+
+ switch (resp.result) {
+ case QSEOS_RESULT_SUCCESS:
+ break;
+ case QSEOS_RESULT_FAILURE:
+ pr_err("scm call failed w/response result%d\n", resp.result);
+ ret = -EINVAL;
+ goto exit_disable_clk_vote;
+ case QSEOS_RESULT_INCOMPLETE:
+ ret = __qseecom_process_incomplete_cmd(data, &resp);
+ if (ret) {
+ pr_err("process_incomplete_cmd failed err: %d\n", ret);
+ goto exit_disable_clk_vote;
}
+ break;
+ default:
+ pr_err("scm call return unknown response %d\n", resp.result);
+ ret = -EINVAL;
+ goto exit_disable_clk_vote;
}
- kzfree(img_data);
__qseecom_disable_clk_scale_down(data);
return ret;
+
+exit_disable_clk_vote:
+ __qseecom_disable_clk_scale_down(data);
+
+exit_ion_unmap_kernel:
+ ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+
+exit_ion_free:
+ ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ qseecom.cmnlib_ion_handle = NULL;
+ return ret;
}
static int qseecom_unload_commonlib_image(void)
@@ -1761,6 +1783,11 @@ static int qseecom_unload_commonlib_image(void)
break;
}
}
+
+ ion_unmap_kernel(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ ion_free(qseecom.ion_clnt, qseecom.cmnlib_ion_handle);
+ qseecom.cmnlib_ion_handle = NULL;
+
return ret;
}
@@ -2575,25 +2602,20 @@ static int __qseecom_get_ce_pipe_info(
static int __qseecom_generate_and_save_key(struct qseecom_dev_handle *data,
enum qseecom_key_management_usage_type usage,
- uint8_t *key_id, uint32_t flags)
+ struct qseecom_key_generate_ireq *ireq)
{
- struct qseecom_key_generate_ireq ireq;
struct qseecom_command_scm_resp resp;
int ret;
- if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) {
+ if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ usage >= QSEOS_KM_USAGE_MAX) {
pr_err("Error:: unsupported usage %d\n", usage);
return -EFAULT;
}
-
- memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
- ireq.flags = flags;
- ireq.qsee_command_id = QSEOS_GENERATE_KEY;
-
__qseecom_enable_clk(CLK_QSEE);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
- &ireq, sizeof(struct qseecom_key_generate_ireq),
+ ireq, sizeof(struct qseecom_key_generate_ireq),
&resp, sizeof(resp));
if (ret) {
pr_err("scm call to generate key failed : %d\n", ret);
@@ -2631,25 +2653,20 @@ static int __qseecom_generate_and_save_key(struct qseecom_dev_handle *data,
static int __qseecom_delete_saved_key(struct qseecom_dev_handle *data,
enum qseecom_key_management_usage_type usage,
- uint8_t *key_id, uint32_t flags)
+ struct qseecom_key_delete_ireq *ireq)
{
- struct qseecom_key_delete_ireq ireq;
struct qseecom_command_scm_resp resp;
int ret;
- if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) {
+ if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ usage >= QSEOS_KM_USAGE_MAX) {
pr_err("Error:: unsupported usage %d\n", usage);
return -EFAULT;
}
-
- memcpy(ireq.key_id, key_id, QSEECOM_KEY_ID_SIZE);
- ireq.flags = flags;
- ireq.qsee_command_id = QSEOS_DELETE_KEY;
-
__qseecom_enable_clk(CLK_QSEE);
ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
- &ireq, sizeof(struct qseecom_key_delete_ireq),
+ ireq, sizeof(struct qseecom_key_delete_ireq),
&resp, sizeof(struct qseecom_command_scm_resp));
if (ret) {
pr_err("scm call to delete key failed : %d\n", ret);
@@ -2679,39 +2696,23 @@ static int __qseecom_delete_saved_key(struct qseecom_dev_handle *data,
static int __qseecom_set_clear_ce_key(struct qseecom_dev_handle *data,
enum qseecom_key_management_usage_type usage,
- struct qseecom_set_key_parameter *set_key_para)
+ struct qseecom_key_select_ireq *ireq)
{
- struct qseecom_key_select_ireq ireq;
struct qseecom_command_scm_resp resp;
int ret;
- if (usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) {
- pr_err("Error:: unsupported usage %d\n", usage);
- return -EFAULT;
+ if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ usage >= QSEOS_KM_USAGE_MAX) {
+ pr_err("Error:: unsupported usage %d\n", usage);
+ return -EFAULT;
}
__qseecom_enable_clk(CLK_QSEE);
if (qseecom.qsee.instance != qseecom.ce_drv.instance)
__qseecom_enable_clk(CLK_CE_DRV);
- memcpy(ireq.key_id, set_key_para->key_id, QSEECOM_KEY_ID_SIZE);
- ireq.qsee_command_id = QSEOS_SET_KEY;
- ireq.ce = set_key_para->ce_hw;
- ireq.pipe = set_key_para->pipe;
- ireq.flags = set_key_para->flags;
-
- /* set both PIPE_ENC and PIPE_ENC_XTS*/
- ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
-
- if (set_key_para->set_clear_key_flag ==
- QSEECOM_SET_CE_KEY_CMD)
- memcpy((void *)ireq.hash, (void *)set_key_para->hash32,
- QSEECOM_HASH_SIZE);
- else
- memset((void *)ireq.hash, 0, QSEECOM_HASH_SIZE);
-
ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
- &ireq, sizeof(struct qseecom_key_select_ireq),
+ ireq, sizeof(struct qseecom_key_select_ireq),
&resp, sizeof(struct qseecom_command_scm_resp));
if (ret) {
pr_err("scm call to set QSEOS_PIPE_ENC key failed : %d\n", ret);
@@ -2744,16 +2745,63 @@ static int __qseecom_set_clear_ce_key(struct qseecom_dev_handle *data,
return ret;
}
+static int __qseecom_update_current_key_user_info(
+ struct qseecom_dev_handle *data,
+ enum qseecom_key_management_usage_type usage,
+ struct qseecom_key_userinfo_update_ireq *ireq)
+{
+ struct qseecom_command_scm_resp resp;
+ int ret;
+
+ if (usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ usage >= QSEOS_KM_USAGE_MAX) {
+ pr_err("Error:: unsupported usage %d\n", usage);
+ return -EFAULT;
+ }
+
+ __qseecom_enable_clk(CLK_QSEE);
+
+ ret = scm_call(SCM_SVC_TZSCHEDULER, 1,
+ ireq, sizeof(struct qseecom_key_userinfo_update_ireq),
+ &resp, sizeof(struct qseecom_command_scm_resp));
+ if (ret) {
+ pr_err("scm call to update key userinfo failed : %d\n", ret);
+ __qseecom_disable_clk(CLK_QSEE);
+ if (qseecom.qsee.instance != qseecom.ce_drv.instance)
+ __qseecom_disable_clk(CLK_CE_DRV);
+ return ret;
+ }
+
+ switch (resp.result) {
+ case QSEOS_RESULT_SUCCESS:
+ break;
+ case QSEOS_RESULT_INCOMPLETE:
+ ret = __qseecom_process_incomplete_cmd(data, &resp);
+ if (ret)
+ pr_err("process_incomplete_cmd FAILED, resp.result %d\n",
+ resp.result);
+ break;
+ case QSEOS_RESULT_FAILURE:
+ default:
+ pr_err("Set key scm call failed resp.result %d\n", resp.result);
+ ret = -EINVAL;
+ break;
+ }
+
+ __qseecom_disable_clk(CLK_QSEE);
+ return ret;
+}
+
static int qseecom_create_key(struct qseecom_dev_handle *data,
void __user *argp)
{
uint32_t ce_hw = 0;
uint32_t pipe = 0;
- uint8_t key_id[QSEECOM_KEY_ID_SIZE] = {0};
int ret = 0;
uint32_t flags = 0;
- struct qseecom_set_key_parameter set_key_para;
struct qseecom_create_key_req create_key_req;
+ struct qseecom_key_generate_ireq generate_key_ireq;
+ struct qseecom_key_select_ireq set_key_ireq;
ret = copy_from_user(&create_key_req, argp, sizeof(create_key_req));
if (ret) {
@@ -2761,7 +2809,8 @@ static int qseecom_create_key(struct qseecom_dev_handle *data,
return ret;
}
- if (create_key_req.usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) {
+ if (create_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ create_key_req.usage >= QSEOS_KM_USAGE_MAX) {
pr_err("Error:: unsupported usage %d\n", create_key_req.usage);
return -EFAULT;
}
@@ -2772,23 +2821,41 @@ static int qseecom_create_key(struct qseecom_dev_handle *data,
return -EINVAL;
}
+ generate_key_ireq.flags = flags;
+ generate_key_ireq.qsee_command_id = QSEOS_GENERATE_KEY;
+ memset((void *)generate_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
+ memset((void *)generate_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
+ memcpy((void *)generate_key_ireq.key_id,
+ (void *)key_id_array[create_key_req.usage - 1],
+ QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)generate_key_ireq.hash32,
+ (void *)create_key_req.hash32, QSEECOM_HASH_SIZE);
+
ret = __qseecom_generate_and_save_key(data, create_key_req.usage,
- key_id, flags);
+ &generate_key_ireq);
if (ret) {
pr_err("Failed to generate key on storage: %d\n", ret);
return -EFAULT;
}
- set_key_para.ce_hw = ce_hw;
- set_key_para.pipe = pipe;
- memcpy(set_key_para.key_id, key_id, QSEECOM_KEY_ID_SIZE);
- set_key_para.flags = flags;
- set_key_para.set_clear_key_flag = QSEECOM_SET_CE_KEY_CMD;
- memcpy((void *)set_key_para.hash32, (void *)create_key_req.hash32,
+ set_key_ireq.qsee_command_id = QSEOS_SET_KEY;
+ set_key_ireq.ce = ce_hw;
+ set_key_ireq.pipe = pipe;
+ set_key_ireq.flags = flags;
+
+ /* set both PIPE_ENC and PIPE_ENC_XTS*/
+ set_key_ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
+ memset((void *)set_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
+ memset((void *)set_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
+ memcpy((void *)set_key_ireq.key_id,
+ (void *)key_id_array[create_key_req.usage - 1],
+ QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)set_key_ireq.hash32, (void *)create_key_req.hash32,
QSEECOM_HASH_SIZE);
ret = __qseecom_set_clear_ce_key(data, create_key_req.usage,
- &set_key_para);
+ &set_key_ireq);
+ msleep(2000);
if (ret) {
pr_err("Failed to create key: pipe %d, ce %d: %d\n",
pipe, ce_hw, ret);
@@ -2803,12 +2870,12 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data,
{
uint32_t ce_hw = 0;
uint32_t pipe = 0;
- uint8_t key_id[QSEECOM_KEY_ID_SIZE] = {0};
int ret = 0;
uint32_t flags = 0;
int i;
struct qseecom_wipe_key_req wipe_key_req;
- struct qseecom_set_key_parameter clear_key_para;
+ struct qseecom_key_delete_ireq delete_key_ireq;
+ struct qseecom_key_select_ireq clear_key_ireq;
ret = copy_from_user(&wipe_key_req, argp, sizeof(wipe_key_req));
if (ret) {
@@ -2816,7 +2883,8 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data,
return ret;
}
- if (wipe_key_req.usage != QSEOS_KM_USAGE_DISK_ENCRYPTION) {
+ if (wipe_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ wipe_key_req.usage >= QSEOS_KM_USAGE_MAX) {
pr_err("Error:: unsupported usage %d\n", wipe_key_req.usage);
return -EFAULT;
}
@@ -2827,22 +2895,32 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data,
return -EINVAL;
}
- ret = __qseecom_delete_saved_key(data, wipe_key_req.usage, key_id,
- flags);
+ delete_key_ireq.flags = flags;
+ delete_key_ireq.qsee_command_id = QSEOS_DELETE_KEY;
+ memset((void *)delete_key_ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)delete_key_ireq.key_id,
+ (void *)key_id_array[wipe_key_req.usage - 1],
+ QSEECOM_KEY_ID_SIZE);
+ memset((void *)delete_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
+
+ ret = __qseecom_delete_saved_key(data, wipe_key_req.usage,
+ &delete_key_ireq);
if (ret) {
pr_err("Failed to delete key from ssd storage: %d\n", ret);
return -EFAULT;
}
- /* an invalid key_id 0xff is used to indicate clear key*/
+ clear_key_ireq.qsee_command_id = QSEOS_SET_KEY;
+ clear_key_ireq.ce = ce_hw;
+ clear_key_ireq.pipe = pipe;
+ clear_key_ireq.flags = flags;
+ clear_key_ireq.pipe_type = QSEOS_PIPE_ENC|QSEOS_PIPE_ENC_XTS;
for (i = 0; i < QSEECOM_KEY_ID_SIZE; i++)
- clear_key_para.key_id[i] = 0xff;
- clear_key_para.ce_hw = ce_hw;
- clear_key_para.pipe = pipe;
- clear_key_para.flags = flags;
- clear_key_para.set_clear_key_flag = QSEECOM_CLEAR_CE_KEY_CMD;
+ clear_key_ireq.key_id[i] = 0xff;
+ memset((void *)clear_key_ireq.hash32, 0, QSEECOM_HASH_SIZE);
+
ret = __qseecom_set_clear_ce_key(data, wipe_key_req.usage,
- &clear_key_para);
+ &clear_key_ireq);
if (ret) {
pr_err("Failed to wipe key: pipe %d, ce %d: %d\n",
pipe, ce_hw, ret);
@@ -2852,6 +2930,48 @@ static int qseecom_wipe_key(struct qseecom_dev_handle *data,
return ret;
}
+static int qseecom_update_key_user_info(struct qseecom_dev_handle *data,
+ void __user *argp)
+{
+ int ret = 0;
+ uint32_t flags = 0;
+ struct qseecom_update_key_userinfo_req update_key_req;
+ struct qseecom_key_userinfo_update_ireq ireq;
+
+ ret = copy_from_user(&update_key_req, argp, sizeof(update_key_req));
+ if (ret) {
+ pr_err("copy_from_user failed\n");
+ return ret;
+ }
+
+ if (update_key_req.usage < QSEOS_KM_USAGE_DISK_ENCRYPTION ||
+ update_key_req.usage >= QSEOS_KM_USAGE_MAX) {
+ pr_err("Error:: unsupported usage %d\n", update_key_req.usage);
+ return -EFAULT;
+ }
+
+ ireq.qsee_command_id = QSEOS_UPDATE_KEY_USERINFO;
+ ireq.flags = flags;
+ memset(ireq.key_id, 0, QSEECOM_KEY_ID_SIZE);
+ memset((void *)ireq.current_hash32, 0, QSEECOM_HASH_SIZE);
+ memset((void *)ireq.new_hash32, 0, QSEECOM_HASH_SIZE);
+ memcpy(ireq.key_id, key_id_array[update_key_req.usage - 1],
+ QSEECOM_KEY_ID_SIZE);
+ memcpy((void *)ireq.current_hash32,
+ (void *)update_key_req.current_hash32, QSEECOM_HASH_SIZE);
+ memcpy((void *)ireq.new_hash32,
+ (void *)update_key_req.new_hash32, QSEECOM_HASH_SIZE);
+
+ ret = __qseecom_update_current_key_user_info(data, update_key_req.usage,
+ &ireq);
+ msleep(2000);
+ if (ret) {
+ pr_err("Failed to update key info: %d\n", ret);
+ return -EFAULT;
+ }
+ return ret;
+
+}
static int qseecom_is_es_activated(void __user *argp)
{
struct qseecom_is_es_activated_req req;
@@ -3024,7 +3144,7 @@ static long qseecom_ioctl(struct file *file, unsigned cmd,
ret = qseecom_receive_req(data);
atomic_dec(&data->ioctl_count);
wake_up_all(&data->abort_wq);
- if (ret)
+ if (ret && (ret != -ERESTARTSYS))
pr_err("failed qseecom_receive_req: %d\n", ret);
break;
}
@@ -3259,14 +3379,12 @@ static long qseecom_ioctl(struct file *file, unsigned cmd,
return -EINVAL;
}
data->released = true;
- mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_create_key(data, argp);
if (ret)
pr_err("failed to create encryption key: %d\n", ret);
atomic_dec(&data->ioctl_count);
- mutex_unlock(&app_access_lock);
break;
}
case QSEECOM_IOCTL_WIPE_KEY_REQ: {
@@ -3282,13 +3400,31 @@ static long qseecom_ioctl(struct file *file, unsigned cmd,
return -EINVAL;
}
data->released = true;
- mutex_lock(&app_access_lock);
atomic_inc(&data->ioctl_count);
ret = qseecom_wipe_key(data, argp);
if (ret)
pr_err("failed to wipe encryption key: %d\n", ret);
atomic_dec(&data->ioctl_count);
- mutex_unlock(&app_access_lock);
+ break;
+ }
+ case QSEECOM_IOCTL_UPDATE_KEY_USER_INFO_REQ: {
+ if (data->type != QSEECOM_GENERIC) {
+ pr_err("update key req: invalid handle (%d)\n",
+ data->type);
+ ret = -EINVAL;
+ break;
+ }
+ if (qseecom.qsee_version < QSEE_VERSION_05) {
+ pr_err("Update Key feature unsupported in qsee ver %u\n",
+ qseecom.qsee_version);
+ return -EINVAL;
+ }
+ data->released = true;
+ atomic_inc(&data->ioctl_count);
+ ret = qseecom_update_key_user_info(data, argp);
+ if (ret)
+ pr_err("failed to update key user info: %d\n", ret);
+ atomic_dec(&data->ioctl_count);
break;
}
case QSEECOM_IOCTL_SAVE_PARTITION_HASH_REQ: {
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 44e7c40dc24b..629661a62bab 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -3151,6 +3151,8 @@ static const struct mmc_fixup blk_fixups[] =
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
MMC_FIXUP("VZL00M", CID_MANFID_SAMSUNG, CID_OEMID_ANY, add_quirk_mmc,
MMC_QUIRK_SEC_ERASE_TRIM_BROKEN),
+ MMC_FIXUP(CID_NAME_ANY, CID_MANFID_HYNIX, CID_OEMID_ANY, add_quirk_mmc,
+ MMC_QUIRK_BROKEN_DATA_TIMEOUT),
/* Some INAND MCP devices advertise incorrect timeout values */
MMC_FIXUP("SEM04G", 0x45, CID_OEMID_ANY, add_quirk_mmc,
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 3e2bf62d9385..aa32d4401854 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -1341,6 +1341,11 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card)
data->timeout_ns = 4000000000u; /* 4s */
data->timeout_clks = 0;
}
+ /* Some emmc cards require a longer read/write time */
+ if (card->quirks & MMC_QUIRK_BROKEN_DATA_TIMEOUT) {
+ if (data->timeout_ns < 4000000000u)
+ data->timeout_ns = 4000000000u; /* 4s */
+ }
}
EXPORT_SYMBOL(mmc_set_data_timeout);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 09ca1750cb06..b386dd333ea8 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1249,8 +1249,7 @@ static int mmc_change_bus_speed(struct mmc_host *host, unsigned long *freq)
mmc_set_clock(host, (unsigned int) (*freq));
}
- if ((mmc_card_hs400(card) || mmc_card_hs200(card))
- && card->host->ops->execute_tuning) {
+ if (mmc_card_hs200(card) && card->host->ops->execute_tuning) {
/*
* We try to probe host driver for tuning for any
* frequency, it is host driver responsibility to
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 035011bff8bd..51f7571db870 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -48,6 +48,7 @@
#include <linux/pm_qos.h>
#include <linux/iopoll.h>
#include <linux/clk/msm-clk.h>
+#include <linux/irqchip/msm-mpm-irq.h>
#include <asm/cacheflush.h>
#include <asm/div64.h>
@@ -57,7 +58,6 @@
#include <mach/msm_iomap.h>
#include <mach/dma.h>
#include <mach/sdio_al.h>
-#include <mach/mpm.h>
#include <mach/msm_bus.h>
#include "msm_sdcc.h"
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index d20368a38c9d..93abfe181563 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -37,9 +37,9 @@
#include <linux/pm_runtime.h>
#include <linux/mmc/slot-gpio.h>
#include <linux/dma-mapping.h>
+#include <linux/irqchip/msm-mpm-irq.h>
#include <mach/gpio.h>
#include <mach/msm_bus.h>
-#include <mach/mpm.h>
#include <linux/iopoll.h>
#include "sdhci-pltfm.h"
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 61b5805e9bc7..c705d0197114 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -812,12 +812,6 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
if (host->quirks & SDHCI_QUIRK_BROKEN_TIMEOUT_VAL)
return 0xE;
- /* During initialization, don't use max timeout as the clock is slow */
- if ((host->quirks2 & SDHCI_QUIRK2_USE_RESERVED_MAX_TIMEOUT) &&
- (host->clock > 400000)) {
- return 0xF;
- }
-
/* Unspecified timeout, assume max */
if (!data && !cmd->cmd_timeout_ms)
return 0xE;
@@ -1724,7 +1718,10 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
* is no on-going data transfer. If so, we need to execute
* tuning procedure before sending command.
*/
- if ((host->flags & SDHCI_NEEDS_RETUNING) &&
+ if ((mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK) &&
+ (mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK_HS400) &&
+ (mrq->cmd->opcode != MMC_SEND_TUNING_BLOCK_HS200) &&
+ (host->flags & SDHCI_NEEDS_RETUNING) &&
!(present_state & (SDHCI_DOING_WRITE | SDHCI_DOING_READ))) {
if (mmc->card) {
/* eMMC uses cmd21 but sd and sdio use cmd19 */
@@ -1732,6 +1729,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
mmc->card->type == MMC_TYPE_MMC ?
MMC_SEND_TUNING_BLOCK_HS200 :
MMC_SEND_TUNING_BLOCK;
+ host->mrq = NULL;
spin_unlock_irqrestore(&host->lock, flags);
sdhci_execute_tuning(mmc, tuning_opcode);
spin_lock_irqsave(&host->lock, flags);
@@ -2808,6 +2806,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask)
}
if (host->cmd->error) {
+ if (host->cmd->error == -EILSEQ)
+ host->flags |= SDHCI_NEEDS_RETUNING;
tasklet_schedule(&host->finish_tasklet);
return;
}
@@ -2945,8 +2945,11 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
SDHCI_COMMAND));
if ((command != MMC_SEND_TUNING_BLOCK_HS400) &&
(command != MMC_SEND_TUNING_BLOCK_HS200) &&
- (command != MMC_SEND_TUNING_BLOCK))
+ (command != MMC_SEND_TUNING_BLOCK)) {
pr_msg = true;
+ if (intmask & SDHCI_INT_DATA_CRC)
+ host->flags |= SDHCI_NEEDS_RETUNING;
+ }
} else {
pr_msg = true;
}
diff --git a/drivers/mtd/devices/msm_qpic_nand.c b/drivers/mtd/devices/msm_qpic_nand.c
index 8521f2fed308..eb671295aba3 100644
--- a/drivers/mtd/devices/msm_qpic_nand.c
+++ b/drivers/mtd/devices/msm_qpic_nand.c
@@ -32,8 +32,8 @@
#include <linux/of.h>
#include <linux/ctype.h>
#include <mach/sps.h>
-#include <mach/msm_smem.h>
#include <mach/msm_bus.h>
+#include <soc/qcom/smem.h>
#define PAGE_SIZE_2K 2048
#define PAGE_SIZE_4K 4096
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index 315abed3ccbc..5fd2f3df557e 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -243,7 +243,7 @@ int ecm_ipa_init(struct ecm_ipa_params *params)
ecm_ipa_ctx->net = net;
ecm_ipa_ctx->tx_enable = true;
ecm_ipa_ctx->rx_enable = true;
- ecm_ipa_ctx->rm_enable = true;
+ ecm_ipa_ctx->rm_enable = false;
ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW;
atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0);
diff --git a/drivers/net/ethernet/msm/emac/emac_ethtool.c b/drivers/net/ethernet/msm/emac/emac_ethtool.c
index 28b7b3791692..ab928f3fe4f6 100644
--- a/drivers/net/ethernet/msm/emac/emac_ethtool.c
+++ b/drivers/net/ethernet/msm/emac/emac_ethtool.c
@@ -93,7 +93,7 @@ static int emac_get_settings(struct net_device *netdev,
ecmd->advertising |= hw->autoneg_advertised;
ecmd->port = PORT_TP;
- ecmd->phy_address = 0;
+ ecmd->phy_address = hw->phy_addr;
ecmd->transceiver = XCVR_INTERNAL;
ecmd->autoneg = AUTONEG_ENABLE;
diff --git a/drivers/net/ethernet/msm/emac/emac_hw.c b/drivers/net/ethernet/msm/emac/emac_hw.c
index 7061e149eb33..bdbc501cd020 100644
--- a/drivers/net/ethernet/msm/emac/emac_hw.c
+++ b/drivers/net/ethernet/msm/emac/emac_hw.c
@@ -100,9 +100,11 @@ int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
*phy_data = 0;
clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;
- retval = emac_disable_mdio_autopoll(hw);
- if (retval)
- return retval;
+ if (hw->adpt->no_ephy == false) {
+ retval = emac_disable_mdio_autopoll(hw);
+ if (retval)
+ return retval;
+ }
emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
(dev << PHY_ADDR_SHFT));
@@ -142,7 +144,9 @@ int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev, bool fast,
if (i == MDIO_WAIT_TIMES)
retval = -EIO;
- emac_enable_mdio_autopoll(hw);
+ if (hw->adpt->no_ephy == false)
+ emac_enable_mdio_autopoll(hw);
+
return retval;
}
@@ -154,9 +158,11 @@ int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
clk_sel = fast ? MDIO_CLK_25_4 : MDIO_CLK_25_28;
- retval = emac_disable_mdio_autopoll(hw);
- if (retval)
- return retval;
+ if (hw->adpt->no_ephy == false) {
+ retval = emac_disable_mdio_autopoll(hw);
+ if (retval)
+ return retval;
+ }
emac_reg_update32(hw, EMAC, EMAC_PHY_STS, PHY_ADDR_BMSK,
(dev << PHY_ADDR_SHFT));
@@ -195,17 +201,20 @@ int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
if (i == MDIO_WAIT_TIMES)
retval = -EIO;
- emac_enable_mdio_autopoll(hw);
+ if (hw->adpt->no_ephy == false)
+ emac_enable_mdio_autopoll(hw);
+
return retval;
}
-int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data)
+int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr,
+ u16 reg_addr, u16 *phy_data)
{
unsigned long flags;
int retval;
spin_lock_irqsave(&hw->mdio_lock, flags);
- retval = emac_hw_read_phy_reg(hw, false, hw->phy_addr, true,
+ retval = emac_hw_read_phy_reg(hw, false, phy_addr, true,
reg_addr, phy_data);
spin_unlock_irqrestore(&hw->mdio_lock, flags);
@@ -218,13 +227,14 @@ int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data)
return retval;
}
-int emac_write_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 phy_data)
+int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr,
+ u16 reg_addr, u16 phy_data)
{
unsigned long flags;
int retval;
spin_lock_irqsave(&hw->mdio_lock, flags);
- retval = emac_hw_write_phy_reg(hw, false, hw->phy_addr, true,
+ retval = emac_hw_write_phy_reg(hw, false, phy_addr, true,
reg_addr, phy_data);
spin_unlock_irqrestore(&hw->mdio_lock, flags);
@@ -409,15 +419,19 @@ int emac_hw_init_phy(struct emac_hw *hw)
spin_lock_init(&hw->mdio_lock);
if (hw->adpt->no_ephy == false) {
- retval = emac_read_phy_reg(hw, MII_PHYSID1, &phy_id[0]);
+ retval = emac_read_phy_reg(hw, hw->phy_addr,
+ MII_PHYSID1, &phy_id[0]);
if (retval)
return retval;
- retval = emac_read_phy_reg(hw, MII_PHYSID2, &phy_id[1]);
+ retval = emac_read_phy_reg(hw, hw->phy_addr,
+ MII_PHYSID2, &phy_id[1]);
if (retval)
return retval;
hw->phy_id[0] = phy_id[0];
hw->phy_id[1] = phy_id[1];
+ } else {
+ emac_disable_mdio_autopoll(hw);
}
hw->autoneg_advertised = EMAC_LINK_SPEED_DEFAULT;
@@ -483,11 +497,13 @@ static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg,
if (speed & EMAC_LINK_SPEED_1GB_FULL)
ctrl1000 |= ADVERTISE_1000FULL;
- retval |= emac_write_phy_reg(hw, MII_ADVERTISE, adv);
- retval |= emac_write_phy_reg(hw, MII_CTRL1000, ctrl1000);
+ retval |= emac_write_phy_reg(hw, hw->phy_addr,
+ MII_ADVERTISE, adv);
+ retval |= emac_write_phy_reg(hw, hw->phy_addr,
+ MII_CTRL1000, ctrl1000);
bmcr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART;
- retval |= emac_write_phy_reg(hw, MII_BMCR, bmcr);
+ retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
} else {
bmcr = BMCR_RESET;
switch (speed) {
@@ -507,7 +523,7 @@ static int emac_hw_setup_phy_link(struct emac_hw *hw, u32 speed, bool autoneg,
return -EINVAL;
}
- retval |= emac_write_phy_reg(hw, MII_BMCR, bmcr);
+ retval |= emac_write_phy_reg(hw, hw->phy_addr, MII_BMCR, bmcr);
}
return retval;
@@ -558,7 +574,7 @@ int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up)
}
}
- retval = emac_read_phy_reg(hw, MII_BMSR, &bmsr);
+ retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
if (retval)
return retval;
@@ -568,7 +584,7 @@ int emac_check_phy_link(struct emac_hw *hw, u32 *speed, bool *link_up)
return 0;
}
*link_up = true;
- retval = emac_read_phy_reg(hw, MII_PSSR, &pssr);
+ retval = emac_read_phy_reg(hw, hw->phy_addr, MII_PSSR, &pssr);
if (retval)
return retval;
@@ -621,8 +637,8 @@ int emac_hw_get_lpa_speed(struct emac_hw *hw, u32 *speed)
}
}
- retval = emac_read_phy_reg(hw, MII_LPA, &lpa);
- retval |= emac_read_phy_reg(hw, MII_STAT1000, &stat1000);
+ retval = emac_read_phy_reg(hw, hw->phy_addr, MII_LPA, &lpa);
+ retval |= emac_read_phy_reg(hw, hw->phy_addr, MII_STAT1000, &stat1000);
if (retval)
return retval;
@@ -1147,12 +1163,13 @@ static int emac_get_fc_mode(struct emac_hw *hw, enum emac_fc_mode *mode)
int retval = 0;
for (i = 0; i < EMAC_MAX_SETUP_LNK_CYCLE; i++) {
- retval = emac_read_phy_reg(hw, MII_BMSR, &bmsr);
+ retval = emac_read_phy_reg(hw, hw->phy_addr, MII_BMSR, &bmsr);
if (retval)
return retval;
if (bmsr & BMSR_LSTATUS) {
- retval = emac_read_phy_reg(hw, MII_PSSR, &pssr);
+ retval = emac_read_phy_reg(hw, hw->phy_addr,
+ MII_PSSR, &pssr);
if (retval)
return retval;
@@ -1322,7 +1339,7 @@ void emac_hw_start_mac(struct emac_hw *hw)
mac |= (CRCE | PCRCE);
mac |= ((hw->preamble << PRLEN_SHFT) & PRLEN_BMSK);
mac |= BROAD_EN;
- mac |= (FLCHK | PROM_MODE | RX_CHKSUM_EN);
+ mac |= (FLCHK | RX_CHKSUM_EN);
mac &= ~(HUGEN | VLAN_STRIP | TPAUSE | SIMR | HUGE | MULTI_ALL |
DEBUG_MODE | SINGLE_PAUSE_MODE);
diff --git a/drivers/net/ethernet/msm/emac/emac_hw.h b/drivers/net/ethernet/msm/emac/emac_hw.h
index 3802b11b20cb..35ea8ee606f5 100644
--- a/drivers/net/ethernet/msm/emac/emac_hw.h
+++ b/drivers/net/ethernet/msm/emac/emac_hw.h
@@ -33,8 +33,10 @@ extern int emac_hw_read_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
bool fast, u16 reg_addr, u16 *phy_data);
extern int emac_hw_write_phy_reg(struct emac_hw *hw, bool ext, u8 dev,
bool fast, u16 reg_addr, u16 phy_data);
-extern int emac_read_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 *phy_data);
-extern int emac_write_phy_reg(struct emac_hw *hw, u16 reg_addr, u16 phy_data);
+extern int emac_read_phy_reg(struct emac_hw *hw, u16 phy_addr,
+ u16 reg_addr, u16 *phy_data);
+extern int emac_write_phy_reg(struct emac_hw *hw, u16 phy_addr,
+ u16 reg_addr, u16 phy_data);
extern int emac_setup_phy_link(struct emac_hw *hw, u32 speed,
bool autoneg, bool fc);
extern int emac_setup_phy_link_speed(struct emac_hw *hw, u32 speed,
diff --git a/drivers/net/ethernet/msm/emac/emac_main.c b/drivers/net/ethernet/msm/emac/emac_main.c
index 61c0868592cf..ca0af609724d 100644
--- a/drivers/net/ethernet/msm/emac/emac_main.c
+++ b/drivers/net/ethernet/msm/emac/emac_main.c
@@ -86,8 +86,8 @@ static struct emac_irq_info emac_irq[EMAC_NUM_IRQ] = {
};
static struct emac_gpio_info emac_gpio[EMAC_NUM_GPIO] = {
- { 0, "qti,emac-gpio-mdc" },
- { 0, "qti,emac-gpio-mdio" },
+ { 0, "qcom,emac-gpio-mdc" },
+ { 0, "qcom,emac-gpio-mdio" },
};
static struct emac_clk_info emac_clk[EMAC_NUM_CLK] = {
@@ -110,6 +110,8 @@ void emac_reinit_locked(struct emac_adapter *adpt)
}
emac_down(adpt, EMAC_HW_CTRL_RESET_MAC);
+ if (adpt->phy_mode == PHY_INTERFACE_MODE_SGMII)
+ emac_hw_reset_sgmii(&adpt->hw);
emac_up(adpt);
CLI_ADPT_FLAG(STATE_RESETTING);
@@ -961,7 +963,15 @@ static irqreturn_t emac_sgmii_interrupt(int irq, void *data)
if (status & SGMII_ISR_AN_MASK)
emac_check_lsc(adpt);
- emac_hw_clear_sgmii_intr_status(hw, status);
+ if (emac_hw_clear_sgmii_intr_status(hw, status) != 0) {
+ emac_warn(adpt, intr,
+ "failed to clear sgmii intr, status=0x%x\n",
+ status);
+ /* reset */
+ SET_ADPT_FLAG(TASK_REINIT_REQ);
+ emac_task_schedule(adpt);
+ break;
+ }
} while (1);
return IRQ_HANDLED;
@@ -1598,12 +1608,9 @@ static int emac_mii_ioctl(struct net_device *netdev,
struct mii_ioctl_data *data = if_mii(ifr);
int retval = 0;
- if (!netif_running(netdev))
- return -EINVAL;
-
switch (cmd) {
case SIOCGMIIPHY:
- data->phy_id = 0;
+ data->phy_id = hw->phy_addr;
break;
case SIOCGMIIREG:
@@ -1617,7 +1624,18 @@ static int emac_mii_ioctl(struct net_device *netdev,
break;
}
- retval = emac_read_phy_reg(hw, data->reg_num, &data->val_out);
+ if (data->phy_id >= PHY_MAX_ADDR) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (adpt->no_ephy == false && data->phy_id != hw->phy_addr) {
+ retval = -EFAULT;
+ break;
+ }
+
+ retval = emac_read_phy_reg(hw, data->phy_id,
+ data->reg_num, &data->val_out);
break;
case SIOCSMIIREG:
@@ -1631,7 +1649,19 @@ static int emac_mii_ioctl(struct net_device *netdev,
break;
}
- retval = emac_write_phy_reg(hw, data->reg_num, data->val_in);
+ if (data->phy_id >= PHY_MAX_ADDR) {
+ retval = -EFAULT;
+ break;
+ }
+
+ if (adpt->no_ephy == false && data->phy_id != hw->phy_addr) {
+ retval = -EFAULT;
+ break;
+ }
+
+ retval = emac_write_phy_reg(hw, data->phy_id,
+ data->reg_num, data->val_in);
+
break;
}
@@ -2280,10 +2310,10 @@ static int emac_get_resources(struct platform_device *pdev,
return retval;
/* get time stamp enable flag */
- adpt->tstamp_en = of_property_read_bool(node, "qti,emac-tstamp-en");
+ adpt->tstamp_en = of_property_read_bool(node, "qcom,emac-tstamp-en");
/* get no_ephy attribute */
- adpt->no_ephy = of_property_read_bool(node, "qti,no-external-phy");
+ adpt->no_ephy = of_property_read_bool(node, "qcom,no-external-phy");
/* get phy address on MDIO bus */
if (adpt->no_ephy == false) {
@@ -2506,8 +2536,6 @@ static int emac_probe(struct platform_device *pdev)
netdev->vlan_features |= NETIF_F_SG | NETIF_F_HW_CSUM |
NETIF_F_TSO | NETIF_F_TSO6;
- netdev->flags |= IFF_PROMISC;
-
setup_timer(&adpt->emac_timer, &emac_timer_routine,
(unsigned long)adpt);
INIT_WORK(&adpt->emac_task, emac_task_routine);
@@ -2582,7 +2610,7 @@ static const struct dev_pm_ops emac_pm_ops = {
static struct of_device_id emac_dt_match[] = {
{
- .compatible = "qti,emac",
+ .compatible = "qcom,emac",
},
{}
};
diff --git a/drivers/net/ethernet/msm/rndis_ipa.c b/drivers/net/ethernet/msm/rndis_ipa.c
index 8e788e365cb7..1fb7745621ff 100644
--- a/drivers/net/ethernet/msm/rndis_ipa.c
+++ b/drivers/net/ethernet/msm/rndis_ipa.c
@@ -154,8 +154,10 @@ struct rndis_loopback_pipe {
* @directory: debugfs directory for various debugging switches
* @tx_filter: flag that enable/disable Tx path to continue to IPA
* @tx_dropped: number of filtered out Tx packets
+ * @tx_dump_enable: dump all Tx packets
* @rx_filter: flag that enable/disable Rx path to continue to IPA
* @rx_dropped: number of filtered out Rx packets
+ * @rx_dump_enable: dump all Rx packets
* @icmp_filter: allow all ICMP packet to pass through the filters
* @rm_enable: flag that enable/disable Resource manager request prior to Tx
* @loopback_enable: flag that enable/disable USB stub loopback
@@ -180,8 +182,10 @@ struct rndis_ipa_dev {
struct net_device *net;
u32 tx_filter;
u32 tx_dropped;
+ u32 tx_dump_enable;
u32 rx_filter;
u32 rx_dropped;
+ u32 rx_dump_enable;
u32 icmp_filter;
u32 rm_enable;
bool loopback_enable;
@@ -270,6 +274,7 @@ static ssize_t rndis_ipa_debugfs_loopback_read(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos);
static ssize_t rndis_ipa_debugfs_atomic_read(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos);
+static void rndis_ipa_dump_skb(struct sk_buff *skb);
static int rndis_ipa_debugfs_init(struct rndis_ipa_dev *rndis_ipa_ctx);
static void rndis_ipa_debugfs_destroy(struct rndis_ipa_dev *rndis_ipa_ctx);
static int rndis_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl,
@@ -312,7 +317,7 @@ const struct file_operations rndis_ipa_aggr_ops = {
static struct ipa_ep_cfg ipa_to_usb_ep_cfg = {
.mode = {
.mode = IPA_BASIC,
- .dst = IPA_CLIENT_A5_LAN_WAN_CONS,
+ .dst = IPA_CLIENT_APPS_LAN_CONS,
},
.hdr = {
.hdr_len = ETH_ALEN + sizeof(struct rndis_pkt_hdr),
@@ -357,7 +362,7 @@ static struct ipa_ep_cfg ipa_to_usb_ep_cfg = {
static struct ipa_ep_cfg usb_to_ipa_ep_cfg = {
.mode = {
.mode = IPA_BASIC,
- .dst = IPA_CLIENT_A5_LAN_WAN_CONS,
+ .dst = IPA_CLIENT_APPS_LAN_CONS,
},
.hdr = {
.hdr_len = ETH_ALEN,
@@ -470,9 +475,11 @@ int rndis_ipa_init(struct ipa_usb_init_params *params)
rndis_ipa_ctx->tx_filter = false;
rndis_ipa_ctx->rx_filter = false;
rndis_ipa_ctx->icmp_filter = true;
- rndis_ipa_ctx->rm_enable = true;
+ rndis_ipa_ctx->rm_enable = false;
rndis_ipa_ctx->tx_dropped = 0;
rndis_ipa_ctx->rx_dropped = 0;
+ rndis_ipa_ctx->tx_dump_enable = false;
+ rndis_ipa_ctx->rx_dump_enable = false;
rndis_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
rndis_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW;
atomic_set(&rndis_ipa_ctx->outstanding_pkts, 0);
@@ -747,6 +754,9 @@ static netdev_tx_t rndis_ipa_start_xmit(struct sk_buff *skb,
goto out;
}
+ if (unlikely(rndis_ipa_ctx->tx_dump_enable))
+ rndis_ipa_dump_skb(skb);
+
if (unlikely(rndis_ipa_ctx->state != RNDIS_IPA_CONNECTED_AND_UP)) {
RNDIS_IPA_ERROR("Missing pipe connected and/or iface up\n");
return -NETDEV_TX_BUSY;
@@ -820,6 +830,9 @@ static void rndis_ipa_tx_complete_notify(void *private,
NULL_CHECK_NO_RETVAL(private);
+ RNDIS_IPA_DEBUG("packet Tx-complete, len=%d, skb->protocol=%d",
+ skb->len, skb->protocol);
+
if (unlikely((evt != IPA_WRITE_DONE))) {
RNDIS_IPA_ERROR("unsupported event on TX call-back\n");
return;
@@ -929,6 +942,9 @@ static void rndis_ipa_packet_receive_notify(void *private,
RNDIS_IPA_DEBUG("packet Rx, len=%d",
skb->len);
+ if (unlikely(rndis_ipa_ctx->rx_dump_enable))
+ rndis_ipa_dump_skb(skb);
+
if (unlikely(rndis_ipa_ctx->state != RNDIS_IPA_CONNECTED_AND_UP)) {
RNDIS_IPA_DEBUG("use connect()/up() before receive()\n");
RNDIS_IPA_DEBUG("packet dropped (length=%d)\n",
@@ -1752,6 +1768,24 @@ static const char *rndis_ipa_state_string(enum rndis_ipa_state state)
}
}
+static void rndis_ipa_dump_skb(struct sk_buff *skb)
+{
+ int i;
+ u32 *cur = (u32 *)skb->data;
+ u8 *byte;
+
+ RNDIS_IPA_DEBUG("packet dump start for skb->len=%d\n",
+ skb->len);
+
+ for (i = 0; i < (skb->len/4); i++) {
+ byte = (u8 *)(cur + i);
+ pr_info("%2d %08x %02x %02x %02x %02x\n",
+ i, *(cur + i),
+ byte[0], byte[1], byte[2], byte[3]);
+ }
+ RNDIS_IPA_DEBUG("packet dump ended for skb->len=%d\n",
+ skb->len);
+}
/**
* Creates the root folder for the driver
@@ -1909,6 +1943,22 @@ static int rndis_ipa_debugfs_init(struct rndis_ipa_dev *rndis_ipa_ctx)
goto fail_file;
}
+ file = debugfs_create_bool("tx_dump_enable", flags_read_write,
+ rndis_ipa_ctx->directory,
+ &rndis_ipa_ctx->tx_dump_enable);
+ if (!file) {
+ RNDIS_IPA_ERROR("fail to create tx_dump_enable file\n");
+ goto fail_file;
+ }
+
+ file = debugfs_create_bool("rx_dump_enable", flags_read_write,
+ rndis_ipa_ctx->directory,
+ &rndis_ipa_ctx->rx_dump_enable);
+ if (!file) {
+ RNDIS_IPA_ERROR("fail to create rx_dump_enable file\n");
+ goto fail_file;
+ }
+
RNDIS_IPA_LOG_EXIT();
return 0;
diff --git a/drivers/net/wireless/cnss/Makefile b/drivers/net/wireless/cnss/Makefile
index 70855ad02876..daf6f261088d 100644
--- a/drivers/net/wireless/cnss/Makefile
+++ b/drivers/net/wireless/cnss/Makefile
@@ -1,3 +1,4 @@
-# Makefile for cnss platform driver
+# Makefile for CNSS platform driver
-obj-$(CONFIG_CNSS) +=cnss.o
+cnsscore-objs += cnss.o ../wcnss/qcomwlan_secif.o
+obj-$(CONFIG_CNSS) += cnsscore.o
diff --git a/drivers/net/wireless/cnss/cnss.c b/drivers/net/wireless/cnss/cnss.c
index 19d45d2a6b01..967196773cfb 100644
--- a/drivers/net/wireless/cnss/cnss.c
+++ b/drivers/net/wireless/cnss/cnss.c
@@ -37,6 +37,9 @@
#define QCA6174_VENDOR_ID (0x168C)
#define QCA6174_DEVICE_ID (0x003E)
+#define QCA6174_REV_ID_OFFSET (0x08)
+#define QCA6174_FW_1_1 (0x11)
+#define QCA6174_FW_1_3 (0x13)
#define WLAN_VREG_NAME "vdd-wlan"
#define WLAN_EN_GPIO_NAME "wlan-en-gpio"
@@ -75,6 +78,8 @@ static struct cnss_data {
struct cnss_wlan_gpio_info gpio_info;
bool pcie_link_state;
struct pci_saved_state *saved_state;
+ u16 revision_id;
+ struct cnss_fw_files fw_files;
} *penv;
static int cnss_wlan_vreg_set(struct cnss_wlan_vreg_info *vreg_info, bool state)
@@ -223,6 +228,56 @@ static u8 cnss_get_pci_dev_bus_number(struct pci_dev *pdev)
return pdev->bus->number;
}
+void cnss_setup_fw_files(u16 revision)
+{
+ switch (revision) {
+
+ case QCA6174_FW_1_1:
+ strlcpy(penv->fw_files.image_file, "athwlan11.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.board_data, "bdatawlan11.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.otp_data, "otp11.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.utf_file, "utf11.bin",
+ CNSS_MAX_FILE_NAME);
+ break;
+
+ case QCA6174_FW_1_3:
+ strlcpy(penv->fw_files.image_file, "athwlan13.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.board_data, "bdatawlan13.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.otp_data, "otp13.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.utf_file, "utf13.bin",
+ CNSS_MAX_FILE_NAME);
+ break;
+
+ default:
+ strlcpy(penv->fw_files.image_file, "athwlan.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.board_data, "bdatawlan.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.otp_data, "otp.bin",
+ CNSS_MAX_FILE_NAME);
+ strlcpy(penv->fw_files.utf_file, "utf.bin",
+ CNSS_MAX_FILE_NAME);
+ break;
+ }
+}
+
+int cnss_get_fw_files(struct cnss_fw_files *pfw_files)
+{
+ if (!penv || !pfw_files)
+ return -ENODEV;
+
+ *pfw_files = penv->fw_files;
+
+ return 0;
+}
+EXPORT_SYMBOL(cnss_get_fw_files);
+
static int cnss_wlan_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
@@ -233,6 +288,9 @@ static int cnss_wlan_pci_probe(struct pci_dev *pdev,
penv->pdev = pdev;
penv->id = id;
+ pci_read_config_word(pdev, QCA6174_REV_ID_OFFSET, &penv->revision_id);
+ cnss_setup_fw_files(penv->revision_id);
+
if (penv->pcie_link_state) {
pci_save_state(pdev);
penv->saved_state = pci_store_saved_state(pdev);
@@ -750,7 +808,7 @@ static int cnss_remove(struct platform_device *pdev)
}
static const struct of_device_id cnss_dt_match[] = {
- {.compatible = "qti,cnss"},
+ {.compatible = "qcom,cnss"},
{}
};
diff --git a/drivers/net/wireless/wcnss/qcomwlan_secif.c b/drivers/net/wireless/wcnss/qcomwlan_secif.c
index c16e48fc09aa..fd63b62c5ec8 100644
--- a/drivers/net/wireless/wcnss/qcomwlan_secif.c
+++ b/drivers/net/wireless/wcnss/qcomwlan_secif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -61,3 +61,15 @@ void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm)
}
EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher);
+void wcnss_wlan_crypto_free_cipher(struct crypto_cipher *tfm)
+{
+ crypto_free_cipher(tfm);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_free_cipher);
+
+struct crypto_cipher *
+wcnss_wlan_crypto_alloc_cipher(const char *alg_name, u32 type, u32 mask)
+{
+ return crypto_alloc_cipher(alg_name, type, mask);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_cipher);
diff --git a/drivers/net/wireless/wcnss/wcnss_vreg.c b/drivers/net/wireless/wcnss/wcnss_vreg.c
index abbfd2fc997c..1955cfaf3409 100644
--- a/drivers/net/wireless/wcnss/wcnss_vreg.c
+++ b/drivers/net/wireless/wcnss/wcnss_vreg.c
@@ -16,6 +16,7 @@
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/mfd/pm8xxx/gpio.h>
#include <linux/wcnss_wlan.h>
@@ -25,7 +26,6 @@
#include <linux/clk.h>
#include <mach/msm_xo.h>
#include <mach/msm_iomap.h>
-#include <mach/rpm-regulator-smd.h>
static void __iomem *msm_wcnss_base;
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
index b520a46bac27..37c64e028577 100644
--- a/drivers/net/wireless/wcnss/wcnss_wlan.c
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -45,6 +45,7 @@
#endif
#define DEVICE "wcnss_wlan"
+#define CTRL_DEVICE "wcnss_ctrl"
#define VERSION "1.01"
#define WCNSS_PIL_DEVICE "wcnss"
@@ -147,6 +148,9 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define MSM_PRONTO_TXP_PHY_ABORT 0xfb080488
#define MSM_PRONTO_BRDG_ERR_SRC 0xfb080fb0
+#define MSM_PRONTO_ALARMS_TXCTL 0xfb0120a8
+#define MSM_PRONTO_ALARMS_TACTL 0xfb012448
+
#define WCNSS_DEF_WLAN_RX_BUFF_COUNT 1024
#define WCNSS_VBATT_THRESHOLD 3500000
#define WCNSS_VBATT_GUARD 200
@@ -157,6 +161,16 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define WCNSS_MAX_FRAME_SIZE (4*1024)
#define WCNSS_VERSION_LEN 30
#define WCNSS_MAX_BUILD_VER_LEN 256
+#define WCNSS_MAX_CMD_LEN (128)
+#define WCNSS_MIN_CMD_LEN (3)
+#define WCNSS_MIN_SERIAL_LEN (6)
+
+/* control messages from userspace */
+#define WCNSS_USR_CTRL_MSG_START 0x00000000
+#define WCNSS_USR_SERIAL_NUM (WCNSS_USR_CTRL_MSG_START + 1)
+#define WCNSS_USR_HAS_CAL_DATA (WCNSS_USR_CTRL_MSG_START + 2)
+
+#define MAC_ADDRESS_STR "%02x:%02x:%02x:%02x:%02x:%02x"
/* message types */
#define WCNSS_CTRL_MSG_START 0x01000000
@@ -172,6 +186,8 @@ static DEFINE_SPINLOCK(reg_spinlock);
#define WCNSS_BUILD_VER_REQ (WCNSS_CTRL_MSG_START + 9)
#define WCNSS_BUILD_VER_RSP (WCNSS_CTRL_MSG_START + 10)
+/* max 20mhz channel count */
+#define WCNSS_MAX_CH_NUM 45
#define VALID_VERSION(version) \
((strncmp(version, "INVALID", WCNSS_VERSION_LEN)) ? 1 : 0)
@@ -338,6 +354,8 @@ static struct {
void __iomem *wlan_tx_status;
void __iomem *wlan_tx_phy_aborts;
void __iomem *wlan_brdg_err_source;
+ void __iomem *alarms_txctl;
+ void __iomem *alarms_tactl;
void __iomem *fiq_reg;
int nv_downloaded;
unsigned char *fw_cal_data;
@@ -352,13 +370,62 @@ static struct {
int device_opened;
int iris_xo_mode_set;
int fw_vbatt_state;
+ char wlan_nv_macAddr[WLAN_MAC_ADDR_SIZE];
+ int ctrl_device_opened;
struct mutex dev_lock;
+ struct mutex ctrl_lock;
wait_queue_head_t read_wait;
struct qpnp_adc_tm_btm_param vbat_monitor_params;
struct qpnp_adc_tm_chip *adc_tm_dev;
struct mutex vbat_monitor_mutex;
+ u16 unsafe_ch_count;
+ u16 unsafe_ch_list[WCNSS_MAX_CH_NUM];
} *penv = NULL;
+static ssize_t wcnss_wlan_macaddr_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ char macAddr[WLAN_MAC_ADDR_SIZE];
+
+ if (!penv)
+ return -ENODEV;
+
+ pr_debug("%s: Receive MAC Addr From user space: %s\n", __func__, buf);
+
+ if (WLAN_MAC_ADDR_SIZE != sscanf(buf, MAC_ADDRESS_STR,
+ (int *)&macAddr[0], (int *)&macAddr[1],
+ (int *)&macAddr[2], (int *)&macAddr[3],
+ (int *)&macAddr[4], (int *)&macAddr[5])) {
+
+ pr_err("%s: Failed to Copy MAC\n", __func__);
+ return -EINVAL;
+ }
+
+ memcpy(penv->wlan_nv_macAddr, macAddr, sizeof(penv->wlan_nv_macAddr));
+
+ pr_info("%s: Write MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
+ penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
+ penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
+
+ return count;
+}
+
+static ssize_t wcnss_wlan_macaddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!penv)
+ return -ENODEV;
+
+ return scnprintf(buf, PAGE_SIZE, MAC_ADDRESS_STR,
+ penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
+ penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
+ penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
+}
+
+static DEVICE_ATTR(wcnss_mac_addr, S_IRUSR | S_IWUSR,
+ wcnss_wlan_macaddr_show, wcnss_wlan_macaddr_store);
+
static ssize_t wcnss_serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -485,45 +552,39 @@ void wcnss_pronto_log_debug_regs(void)
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SPARE_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SPARE %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_SPARE %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CPU_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CPU_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CPU_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_AHB_CBCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_AHB_CBCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_AHB_CBCR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_CFG %08x\n", __func__, reg);
+ pr_err("PRONTO_PMU_CFG %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_CSR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_CSR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_CSR %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_SOFT_RESET_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_SOFT_RESET %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_SOFT_RESET %08x\n", reg);
reg_addr = penv->pronto_saw2_base + PRONTO_SAW2_SPM_STS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_SAW2_SPM_STS %08x\n", __func__, reg);
+ pr_err("PRONTO_SAW2_SPM_STS %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_COM_GDSCR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PMU_COM_GDSCR %08x\n",
- __func__, reg);
+ pr_err("PRONTO_PMU_COM_GDSCR %08x\n", reg);
reg >>= 31;
if (!reg) {
- pr_info_ratelimited("%s: Cannot log, Pronto common SS is power collapsed\n",
- __func__);
+ pr_err("Cannot log, Pronto common SS is power collapsed\n");
return;
}
reg &= ~(PRONTO_PMU_COM_GDSCR_SW_COLLAPSE
@@ -537,35 +598,35 @@ void wcnss_pronto_log_debug_regs(void)
reg_addr = penv->pronto_a2xb_base + A2XB_CFG_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_CFG_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_CFG_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_INT_SRC_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_INT_SRC_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_INT_SRC_OFFSET %08x\n", reg);
reg_addr = penv->pronto_a2xb_base + A2XB_ERR_INFO_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: A2XB_ERR_INFO_OFFSET %08x\n", __func__, reg);
+ pr_err("A2XB_ERR_INFO_OFFSET %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_INVALID_ADDR_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_INVALID_ADDR %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_INVALID_ADDR %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR0_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR0 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR0 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR1_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR1 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR1 %08x\n", reg);
reg_addr = penv->pronto_ccpu_base + CCU_PRONTO_LAST_ADDR2_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: CCU_CCPU_LAST_ADDR2 %08x\n", __func__, reg);
+ pr_err("CCU_CCPU_LAST_ADDR2 %08x\n", reg);
reg_addr = penv->pronto_pll_base + PRONTO_PLL_STATUS_OFFSET;
reg = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PRONTO_PLL_STATUS %08x\n", __func__, reg);
+ pr_err("PRONTO_PLL_STATUS %08x\n", reg);
tst_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_OFFSET;
tst_ctrl_addr = penv->pronto_a2xb_base + A2XB_TSTBUS_CTRL_OFFSET;
@@ -575,24 +636,21 @@ void wcnss_pronto_log_debug_regs(void)
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_RDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Read data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Read data FIFO testbus %08x\n", reg);
/* command FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_CMDFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Command FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Command FIFO testbus %08x\n", reg);
/* write data FIFO */
reg = 0;
reg = reg | WCNSS_TSTBUS_CTRL_EN | WCNSS_TSTBUS_CTRL_WRFIFO;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: Rrite data FIFO testbus %08x\n",
- __func__, reg);
+ pr_err("Rrite data FIFO testbus %08x\n", reg);
/* AXIM SEL CFG0 */
reg = 0;
@@ -600,8 +658,7 @@ void wcnss_pronto_log_debug_regs(void)
WCNSS_TSTBUS_CTRL_AXIM_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG0 testbus %08x\n", reg);
/* AXIM SEL CFG1 */
reg = 0;
@@ -609,8 +666,7 @@ void wcnss_pronto_log_debug_regs(void)
WCNSS_TSTBUS_CTRL_AXIM_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: AXIM SEL CFG1 testbus %08x\n",
- __func__, reg);
+ pr_err("AXIM SEL CFG1 testbus %08x\n", reg);
/* CTRL SEL CFG0 */
reg = 0;
@@ -618,8 +674,7 @@ void wcnss_pronto_log_debug_regs(void)
WCNSS_TSTBUS_CTRL_CTRL_CFG0;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG0 testbus %08x\n",
- __func__, reg);
+ pr_err("CTRL SEL CFG0 testbus %08x\n", reg);
/* CTRL SEL CFG1 */
reg = 0;
@@ -627,7 +682,7 @@ void wcnss_pronto_log_debug_regs(void)
WCNSS_TSTBUS_CTRL_CTRL_CFG1;
writel_relaxed(reg, tst_ctrl_addr);
reg = readl_relaxed(tst_addr);
- pr_info_ratelimited("%s: CTRL SEL CFG1 testbus %08x\n", __func__, reg);
+ pr_err("CTRL SEL CFG1 testbus %08x\n", reg);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_BCR_OFFSET;
@@ -638,38 +693,62 @@ void wcnss_pronto_log_debug_regs(void)
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_WLAN_AHB_CBCR_OFFSET;
reg3 = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PMU_WLAN_AHB_CBCR %08x\n", __func__, reg3);
+ pr_err("PMU_WLAN_AHB_CBCR %08x\n", reg3);
reg_addr = penv->msm_wcnss_base + PRONTO_PMU_CPU_AHB_CMD_RCGR_OFFSET;
reg4 = readl_relaxed(reg_addr);
- pr_info_ratelimited("%s: PMU_CPU_CMD_RCGR %08x\n", __func__, reg4);
+ pr_err("PMU_CPU_CMD_RCGR %08x\n", reg4);
if ((reg & PRONTO_PMU_WLAN_BCR_BLK_ARES) ||
(reg2 & PRONTO_PMU_WLAN_GDSCR_SW_COLLAPSE) ||
(!(reg4 & PRONTO_PMU_CPU_AHB_CMD_RCGR_ROOT_EN)) ||
(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_OFF) ||
(!(reg3 & PRONTO_PMU_WLAN_AHB_CBCR_CLK_EN))) {
- pr_info_ratelimited("%s: Cannot log, wlan domain is power collapsed\n",
- __func__);
+ pr_err("Cannot log, wlan domain is power collapsed\n");
return;
}
reg = readl_relaxed(penv->wlan_tx_phy_aborts);
- pr_info_ratelimited("%s: WLAN_TX_PHY_ABORTS %08x\n", __func__, reg);
+ pr_err("WLAN_TX_PHY_ABORTS %08x\n", reg);
reg = readl_relaxed(penv->wlan_brdg_err_source);
- pr_info_ratelimited("%s: WLAN_BRDG_ERR_SOURCE %08x\n", __func__, reg);
+ pr_err("WLAN_BRDG_ERR_SOURCE %08x\n", reg);
reg = readl_relaxed(penv->wlan_tx_status);
- pr_info_ratelimited("%s: WLAN_TXP_STATUS %08x\n", __func__, reg);
+ pr_err("WLAN_TXP_STATUS %08x\n", reg);
+
+ reg = readl_relaxed(penv->alarms_txctl);
+ pr_err("ALARMS_TXCTL %08x\n", reg);
+
+ reg = readl_relaxed(penv->alarms_tactl);
+ pr_err("ALARMS_TACTL %08x\n", reg);
}
EXPORT_SYMBOL(wcnss_pronto_log_debug_regs);
#ifdef CONFIG_WCNSS_REGISTER_DUMP_ON_BITE
void wcnss_log_debug_regs_on_bite(void)
{
- if (wcnss_hardware_type() == WCNSS_PRONTO_HW)
- wcnss_pronto_log_debug_regs();
+ struct platform_device *pdev = wcnss_get_platform_device();
+ struct clk *measure;
+ struct clk *wcnss_debug_mux;
+ unsigned long clk_rate;
+
+ if (wcnss_hardware_type() != WCNSS_PRONTO_HW)
+ return;
+
+ measure = clk_get(&pdev->dev, "measure");
+ wcnss_debug_mux = clk_get(&pdev->dev, "wcnss_debug");
+
+ if (!IS_ERR(measure) && !IS_ERR(wcnss_debug_mux)) {
+ clk_set_parent(measure, wcnss_debug_mux);
+ clk_rate = clk_get_rate(measure);
+ pr_debug("wcnss: clock frequency is: %luHz\n", clk_rate);
+
+ if (clk_rate)
+ wcnss_pronto_log_debug_regs();
+ else
+ pr_err("clock frequency is zero, cannot access PMU or other registers\n");
+ }
}
#endif
@@ -707,8 +786,14 @@ static int wcnss_create_sysfs(struct device *dev)
if (ret)
goto remove_thermal;
+ ret = device_create_file(dev, &dev_attr_wcnss_mac_addr);
+ if (ret)
+ goto remove_version;
+
return 0;
+remove_version:
+ device_remove_file(dev, &dev_attr_wcnss_version);
remove_thermal:
device_remove_file(dev, &dev_attr_thermal_mitigation);
remove_serial:
@@ -723,6 +808,7 @@ static void wcnss_remove_sysfs(struct device *dev)
device_remove_file(dev, &dev_attr_serial_number);
device_remove_file(dev, &dev_attr_thermal_mitigation);
device_remove_file(dev, &dev_attr_wcnss_version);
+ device_remove_file(dev, &dev_attr_wcnss_mac_addr);
}
}
static void wcnss_smd_notify_event(void *data, unsigned int event)
@@ -994,6 +1080,20 @@ unsigned int wcnss_get_serial_number(void)
}
EXPORT_SYMBOL(wcnss_get_serial_number);
+int wcnss_get_wlan_mac_address(char mac_addr[WLAN_MAC_ADDR_SIZE])
+{
+ if (!penv)
+ return -ENODEV;
+
+ memcpy(mac_addr, penv->wlan_nv_macAddr, WLAN_MAC_ADDR_SIZE);
+ pr_debug("%s: Get MAC Addr:" MAC_ADDRESS_STR "\n", __func__,
+ penv->wlan_nv_macAddr[0], penv->wlan_nv_macAddr[1],
+ penv->wlan_nv_macAddr[2], penv->wlan_nv_macAddr[3],
+ penv->wlan_nv_macAddr[4], penv->wlan_nv_macAddr[5]);
+ return 0;
+}
+EXPORT_SYMBOL(wcnss_get_wlan_mac_address);
+
static int enable_wcnss_suspend_notify;
static int enable_wcnss_suspend_notify_set(const char *val,
@@ -1137,6 +1237,35 @@ u32 wcnss_get_wlan_rx_buff_count(void)
}
EXPORT_SYMBOL(wcnss_get_wlan_rx_buff_count);
+int wcnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count)
+{
+ if (penv && unsafe_ch_list &&
+ (ch_count <= WCNSS_MAX_CH_NUM)) {
+ memcpy((char *)penv->unsafe_ch_list,
+ (char *)unsafe_ch_list, ch_count * sizeof(u16));
+ penv->unsafe_ch_count = ch_count;
+ return 0;
+ } else
+ return -ENODEV;
+}
+EXPORT_SYMBOL(wcnss_set_wlan_unsafe_channel);
+
+int wcnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 buffer_size,
+ u16 *ch_count)
+{
+ if (penv) {
+ if (buffer_size < penv->unsafe_ch_count * sizeof(u16))
+ return -ENODEV;
+ memcpy((char *)unsafe_ch_list,
+ (char *)penv->unsafe_ch_list,
+ penv->unsafe_ch_count * sizeof(u16));
+ *ch_count = penv->unsafe_ch_count;
+ return 0;
+ } else
+ return -ENODEV;
+}
+EXPORT_SYMBOL(wcnss_get_wlan_unsafe_channel);
+
static int wcnss_smd_tx(void *data, int len)
{
int ret = 0;
@@ -1793,6 +1922,80 @@ static struct notifier_block wcnss_pm_notifier = {
.notifier_call = wcnss_pm_notify,
};
+static int wcnss_ctrl_open(struct inode *inode, struct file *file)
+{
+ int rc = 0;
+
+ if (!penv || penv->ctrl_device_opened)
+ return -EFAULT;
+
+ penv->ctrl_device_opened = 1;
+
+ return rc;
+}
+
+
+void process_usr_ctrl_cmd(u8 *buf, size_t len)
+{
+ u16 cmd = buf[0] << 8 | buf[1];
+
+ switch (cmd) {
+
+ case WCNSS_USR_SERIAL_NUM:
+ if (WCNSS_MIN_SERIAL_LEN > len) {
+ pr_err("%s: Invalid serial number\n", __func__);
+ return;
+ }
+ penv->serial_number = buf[2] << 24 | buf[3] << 16
+ | buf[4] << 8 | buf[5];
+ break;
+
+ case WCNSS_USR_HAS_CAL_DATA:
+ if (1 < buf[2])
+ pr_err("%s: Invalid data for cal %d\n", __func__,
+ buf[2]);
+ has_calibrated_data = buf[2];
+ break;
+
+ default:
+ pr_err("%s: Invalid command %d\n", __func__, cmd);
+ break;
+ }
+}
+
+static ssize_t wcnss_ctrl_write(struct file *fp, const char __user
+ *user_buffer, size_t count, loff_t *position)
+{
+ int rc = 0;
+ u8 buf[WCNSS_MAX_CMD_LEN];
+
+ if (!penv || !penv->ctrl_device_opened || WCNSS_MAX_CMD_LEN < count
+ || WCNSS_MIN_CMD_LEN > count)
+ return -EFAULT;
+
+ mutex_lock(&penv->ctrl_lock);
+ rc = copy_from_user(buf, user_buffer, count);
+ if (0 == rc)
+ process_usr_ctrl_cmd(buf, count);
+
+ mutex_unlock(&penv->ctrl_lock);
+
+ return rc;
+}
+
+
+static const struct file_operations wcnss_ctrl_fops = {
+ .owner = THIS_MODULE,
+ .open = wcnss_ctrl_open,
+ .write = wcnss_ctrl_write,
+};
+
+static struct miscdevice wcnss_usr_ctrl = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = CTRL_DEVICE,
+ .fops = &wcnss_ctrl_fops,
+};
+
static int
wcnss_trigger_config(struct platform_device *pdev)
{
@@ -1961,6 +2164,18 @@ wcnss_trigger_config(struct platform_device *pdev)
pr_err("%s: ioremap wlan TX STATUS failed\n", __func__);
goto fail_ioremap9;
}
+ penv->alarms_txctl = ioremap(MSM_PRONTO_ALARMS_TXCTL, SZ_8);
+ if (!penv->alarms_txctl) {
+ ret = -ENOMEM;
+ pr_err("%s: ioremap alarms TXCTL failed\n", __func__);
+ goto fail_ioremap10;
+ }
+ penv->alarms_tactl = ioremap(MSM_PRONTO_ALARMS_TACTL, SZ_8);
+ if (!penv->alarms_tactl) {
+ ret = -ENOMEM;
+ pr_err("%s: ioremap alarms TACTL failed\n", __func__);
+ goto fail_ioremap11;
+ }
}
penv->adc_tm_dev = qpnp_get_adc_tm(&penv->pdev->dev, "wcnss");
if (IS_ERR(penv->adc_tm_dev)) {
@@ -1986,6 +2201,12 @@ wcnss_trigger_config(struct platform_device *pdev)
fail_pil:
if (penv->riva_ccu_base)
iounmap(penv->riva_ccu_base);
+ if (penv->alarms_tactl)
+ iounmap(penv->alarms_tactl);
+fail_ioremap11:
+ if (penv->alarms_txctl)
+ iounmap(penv->alarms_txctl);
+fail_ioremap10:
if (penv->wlan_tx_status)
iounmap(penv->wlan_tx_status);
fail_ioremap9:
@@ -2183,6 +2404,7 @@ wcnss_wlan_probe(struct platform_device *pdev)
}
mutex_init(&penv->dev_lock);
+ mutex_init(&penv->ctrl_lock);
mutex_init(&penv->vbat_monitor_mutex);
init_waitqueue_head(&penv->read_wait);
@@ -2195,6 +2417,9 @@ wcnss_wlan_probe(struct platform_device *pdev)
* place
*/
pr_info(DEVICE " probed in built-in mode\n");
+
+ misc_register(&wcnss_usr_ctrl);
+
return misc_register(&wcnss_misc);
}
diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c
index c591e138dfe0..06f8fbe50018 100644
--- a/drivers/of/of_batterydata.c
+++ b/drivers/of/of_batterydata.c
@@ -222,6 +222,14 @@ static int of_batterydata_load_battery_data(struct device_node *node,
if (rc)
return rc;
+ rc = of_property_read_string(node, "qcom,battery-type",
+ &batt_data->battery_type);
+ if (rc) {
+ pr_err("Error reading qcom,battery-type property rc=%d\n", rc);
+ batt_data->battery_type = NULL;
+ return rc;
+ }
+
OF_PROP_READ(batt_data->fcc, "fcc-mah", node, rc, false);
OF_PROP_READ(batt_data->default_rbatt_mohm,
"default-rbatt-mohm", node, rc, false);
@@ -269,6 +277,7 @@ int of_batterydata_read_data(struct device_node *batterydata_container_node,
{
struct device_node *node, *best_node;
struct batt_ids batt_ids;
+ const char *battery_type = NULL;
int delta, best_delta, batt_id_kohm, rpull_up_kohm,
vadc_vdd_uv, best_id_kohm, i, rc = 0;
@@ -307,7 +316,12 @@ int of_batterydata_read_data(struct device_node *batterydata_container_node,
pr_err("No battery data found\n");
return -ENODATA;
}
- pr_info("%s loaded\n", best_node->name);
+ rc = of_property_read_string(best_node, "qcom,battery-type",
+ &battery_type);
+ if (!rc)
+ pr_info("%s loaded\n", battery_type);
+ else
+ pr_info("%s loaded\n", best_node->name);
return of_batterydata_load_battery_data(best_node,
best_id_kohm, batt_data);
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644
index 000000000000..cfa8f895ee2e
--- /dev/null
+++ b/drivers/phy/Kconfig
@@ -0,0 +1,24 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+ tristate "PHY Core"
+ help
+ Generic PHY support.
+
+ This framework is designed to provide a generic interface for PHY
+ devices present in the kernel. This layer will have the generic
+ API by which phy drivers can create PHY using the phy framework and
+ phy users can obtain reference to the PHY. All the users of this
+ framework should select this config.
+
+config PHY_MSM_SATA
+ tristate "MSM SoC SATA 6Gbps PHY driver"
+ depends on OF && ARCH_MSM
+ select GENERIC_PHY
+ help
+ Support for 6Gbps SATA PHY on MSM chipsets.
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644
index 000000000000..f3ace083f7de
--- /dev/null
+++ b/drivers/phy/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY) += phy-core.o
+obj-$(CONFIG_PHY_MSM_SATA) += phy-msm-sata.o
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644
index 000000000000..03cf8fb81554
--- /dev/null
+++ b/drivers/phy/phy-core.c
@@ -0,0 +1,698 @@
+/*
+ * phy-core.c -- Generic Phy framework.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+
+static struct class *phy_class;
+static DEFINE_MUTEX(phy_provider_mutex);
+static LIST_HEAD(phy_provider_list);
+static DEFINE_IDA(phy_ida);
+
+static void devm_phy_release(struct device *dev, void *res)
+{
+ struct phy *phy = *(struct phy **)res;
+
+ phy_put(phy);
+}
+
+static void devm_phy_provider_release(struct device *dev, void *res)
+{
+ struct phy_provider *phy_provider = *(struct phy_provider **)res;
+
+ of_phy_provider_unregister(phy_provider);
+}
+
+static void devm_phy_consume(struct device *dev, void *res)
+{
+ struct phy *phy = *(struct phy **)res;
+
+ phy_destroy(phy);
+}
+
+static int devm_phy_match(struct device *dev, void *res, void *match_data)
+{
+ return res == match_data;
+}
+
+static struct phy *phy_lookup(struct device *device, const char *port)
+{
+ unsigned int count;
+ struct phy *phy;
+ struct device *dev;
+ struct phy_consumer *consumers;
+ struct class_dev_iter iter;
+
+ class_dev_iter_init(&iter, phy_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ phy = to_phy(dev);
+ count = phy->init_data->num_consumers;
+ consumers = phy->init_data->consumers;
+ while (count--) {
+ if (!strcmp(consumers->dev_name, dev_name(device)) &&
+ !strcmp(consumers->port, port)) {
+ class_dev_iter_exit(&iter);
+ return phy;
+ }
+ consumers++;
+ }
+ }
+
+ class_dev_iter_exit(&iter);
+ return ERR_PTR(-ENODEV);
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+ struct phy_provider *phy_provider;
+
+ list_for_each_entry(phy_provider, &phy_provider_list, list) {
+ if (phy_provider->dev->of_node == node)
+ return phy_provider;
+ }
+
+ return ERR_PTR(-EPROBE_DEFER);
+}
+
+int phy_pm_runtime_get(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_get(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
+
+int phy_pm_runtime_get_sync(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_get_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
+
+int phy_pm_runtime_put(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_put(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
+
+int phy_pm_runtime_put_sync(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return -ENOTSUPP;
+
+ return pm_runtime_put_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
+
+void phy_pm_runtime_allow(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return;
+
+ pm_runtime_allow(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
+
+void phy_pm_runtime_forbid(struct phy *phy)
+{
+ if (!pm_runtime_enabled(&phy->dev))
+ return;
+
+ pm_runtime_forbid(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+
+int phy_init(struct phy *phy)
+{
+ int ret;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (phy->init_count++ == 0 && phy->ops->init) {
+ ret = phy->ops->init(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_init);
+
+int phy_exit(struct phy *phy)
+{
+ int ret;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (--phy->init_count == 0 && phy->ops->exit) {
+ ret = phy->ops->exit(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_exit);
+
+int phy_power_on(struct phy *phy)
+{
+ int ret = -ENOTSUPP;
+
+ ret = phy_pm_runtime_get_sync(phy);
+ if (ret < 0 && ret != -ENOTSUPP)
+ return ret;
+
+ mutex_lock(&phy->mutex);
+ if (phy->power_count++ == 0 && phy->ops->power_on) {
+ ret = phy->ops->power_on(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_on);
+
+int phy_power_off(struct phy *phy)
+{
+ int ret = -ENOTSUPP;
+
+ mutex_lock(&phy->mutex);
+ if (--phy->power_count == 0 && phy->ops->power_off) {
+ ret = phy->ops->power_off(phy);
+ if (ret < 0) {
+ dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ mutex_unlock(&phy->mutex);
+ phy_pm_runtime_put(phy);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_off);
+
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @dev: device that requests this phy
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. This function uses of_xlate call back function provided
+ * while registering the phy_provider to find the phy instance.
+ */
+static struct phy *of_phy_get(struct device *dev, int index)
+{
+ int ret;
+ struct phy_provider *phy_provider;
+ struct phy *phy = NULL;
+ struct of_phandle_args args;
+
+ ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
+ index, &args);
+ if (ret) {
+ dev_dbg(dev, "failed to get phy in %s node\n",
+ dev->of_node->full_name);
+ return ERR_PTR(-ENODEV);
+ }
+
+ mutex_lock(&phy_provider_mutex);
+ phy_provider = of_phy_provider_lookup(args.np);
+ if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
+ phy = ERR_PTR(-EPROBE_DEFER);
+ goto err0;
+ }
+
+ phy = phy_provider->of_xlate(phy_provider->dev, &args);
+ module_put(phy_provider->owner);
+
+err0:
+ mutex_unlock(&phy_provider_mutex);
+ of_node_put(args.np);
+
+ return phy;
+}
+
+/**
+ * phy_put() - release the PHY
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct phy *phy)
+{
+ if (IS_ERR(phy))
+ return;
+
+ module_put(phy->ops->owner);
+ put_device(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_put);
+
+/**
+ * devm_phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_put
+ * to release the phy.
+ */
+void devm_phy_put(struct device *dev, struct phy *phy)
+{
+ int r;
+
+ r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_put);
+
+/**
+ * of_phy_simple_xlate() - returns the phy instance from phy provider
+ * @dev: the PHY provider device
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by phy provider for the common case where #phy-cells is
+ * 0. For other cases where #phy-cells is greater than '0', the phy provider
+ * should provide a custom of_xlate function that reads the *args* and returns
+ * the appropriate phy.
+ */
+struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
+ *args)
+{
+ struct phy *phy;
+ struct class_dev_iter iter;
+ struct device_node *node = dev->of_node;
+
+ class_dev_iter_init(&iter, phy_class, NULL, NULL);
+ while ((dev = class_dev_iter_next(&iter))) {
+ phy = to_phy(dev);
+ if (node != phy->dev.of_node)
+ continue;
+
+ class_dev_iter_exit(&iter);
+ return phy;
+ }
+
+ class_dev_iter_exit(&iter);
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_get(struct device *dev, const char *string)
+{
+ int index = 0;
+ struct phy *phy = NULL;
+
+ if (string == NULL) {
+ dev_WARN(dev, "missing string\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (dev->of_node) {
+ index = of_property_match_string(dev->of_node, "phy-names",
+ string);
+ phy = of_phy_get(dev, index);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "unable to find phy\n");
+ return phy;
+ }
+ } else {
+ phy = phy_lookup(dev, string);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "unable to find phy\n");
+ return phy;
+ }
+ }
+
+ if (!try_module_get(phy->ops->owner))
+ return ERR_PTR(-EPROBE_DEFER);
+
+ get_device(&phy->dev);
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(phy_get);
+
+/**
+ * devm_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+ struct phy **ptr, *phy;
+
+ ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy = phy_get(dev, string);
+ if (!IS_ERR(phy)) {
+ *ptr = phy;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_get);
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+ struct phy_init_data *init_data)
+{
+ int ret;
+ int id;
+ struct phy *phy;
+
+ if (!dev) {
+ dev_WARN(dev, "no device provided for PHY\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+
+ phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
+ if (id < 0) {
+ dev_err(dev, "unable to get id\n");
+ ret = id;
+ goto err0;
+ }
+
+ device_initialize(&phy->dev);
+ mutex_init(&phy->mutex);
+
+ phy->dev.class = phy_class;
+ phy->dev.parent = dev;
+ phy->dev.of_node = dev->of_node;
+ phy->id = id;
+ phy->ops = ops;
+ phy->init_data = init_data;
+
+ ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
+ if (ret)
+ goto err1;
+
+ ret = device_add(&phy->dev);
+ if (ret)
+ goto err1;
+
+ if (pm_runtime_enabled(dev)) {
+ pm_runtime_enable(&phy->dev);
+ pm_runtime_no_callbacks(&phy->dev);
+ }
+
+ return phy;
+
+err1:
+ ida_remove(&phy_ida, phy->id);
+ put_device(&phy->dev);
+ kfree(phy);
+
+err0:
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(phy_create);
+
+/**
+ * devm_phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Creates a new PHY device adding it to the PHY class.
+ * While at that, it also associates the device with the phy using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+ struct phy_init_data *init_data)
+{
+ struct phy **ptr, *phy;
+
+ ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy = phy_create(dev, ops, init_data);
+ if (!IS_ERR(phy)) {
+ *ptr = phy;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_create);
+
+/**
+ * phy_destroy() - destroy the phy
+ * @phy: the phy to be destroyed
+ *
+ * Called to destroy the phy.
+ */
+void phy_destroy(struct phy *phy)
+{
+ pm_runtime_disable(&phy->dev);
+ device_unregister(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_destroy);
+
+/**
+ * devm_phy_destroy() - destroy the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_destroy
+ * to destroy the phy.
+ */
+void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+ int r;
+
+ r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_destroy);
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+ struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
+{
+ struct phy_provider *phy_provider;
+
+ phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+ if (!phy_provider)
+ return ERR_PTR(-ENOMEM);
+
+ phy_provider->dev = dev;
+ phy_provider->owner = owner;
+ phy_provider->of_xlate = of_xlate;
+
+ mutex_lock(&phy_provider_mutex);
+ list_add_tail(&phy_provider->list, &phy_provider_list);
+ mutex_unlock(&phy_provider_mutex);
+
+ return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__of_phy_provider_register);
+
+/**
+ * __devm_of_phy_provider_register() - create/register phy provider with the
+ * framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider. While at that, it also associates the device with the
+ * phy provider using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ */
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+ struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
+{
+ struct phy_provider **ptr, *phy_provider;
+
+ ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+ if (!IS_ERR(phy_provider)) {
+ *ptr = phy_provider;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+ if (IS_ERR(phy_provider))
+ return;
+
+ mutex_lock(&phy_provider_mutex);
+ list_del(&phy_provider->list);
+ kfree(phy_provider);
+ mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
+
+/**
+ * devm_of_phy_provider_unregister() - remove phy provider from the framework
+ * @dev: struct device of the phy provider
+ *
+ * destroys the devres associated with this phy provider and invokes
+ * of_phy_provider_unregister to unregister the phy provider.
+ */
+void devm_of_phy_provider_unregister(struct device *dev,
+ struct phy_provider *phy_provider) {
+ int r;
+
+ r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
+ phy_provider);
+ dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
+
+/**
+ * phy_release() - release the phy
+ * @dev: the dev member within phy
+ *
+ * When the last reference to the device is removed, it is called
+ * from the embedded kobject as release method.
+ */
+static void phy_release(struct device *dev)
+{
+ struct phy *phy;
+
+ phy = to_phy(dev);
+ dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+ ida_remove(&phy_ida, phy->id);
+ kfree(phy);
+}
+
+static int __init phy_core_init(void)
+{
+ phy_class = class_create(THIS_MODULE, "phy");
+ if (IS_ERR(phy_class)) {
+ pr_err("failed to create phy class --> %ld\n",
+ PTR_ERR(phy_class));
+ return PTR_ERR(phy_class);
+ }
+
+ phy_class->dev_release = phy_release;
+
+ return 0;
+}
+module_init(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+ class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
+
+MODULE_DESCRIPTION("Generic PHY Framework");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-msm-sata.c b/drivers/phy/phy-msm-sata.c
new file mode 100644
index 000000000000..02a480010796
--- /dev/null
+++ b/drivers/phy/phy-msm-sata.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
+
+/* QSERDES COMMON registers */
+#define QSERDES_COM_SYS_CLK_CTRL 0x000
+#define QSERDES_COM_PLL_IP_SETI 0x018
+#define QSERDES_COM_PLL_CP_SETI 0x024
+#define QSERDES_COM_PLL_IP_SETP 0x028
+#define QSERDES_COM_PLL_CP_SETP 0x02c
+#define QSERDES_COM_SYSCLK_EN_SEL 0x038
+#define QSERDES_COM_RESETSM_CNTRL 0x040
+#define QSERDES_COM_PLLLOCK_CMP1 0x044
+#define QSERDES_COM_PLLLOCK_CMP2 0x048
+#define QSERDES_COM_PLLLOCK_CMP3 0x04c
+#define QSERDES_COM_PLLLOCK_CMP_EN 0x050
+#define QSERDES_COM_DEC_START1 0x064
+#define QSERDES_COM_SSC_EN_CENTER 0x06c
+#define QSERDES_COM_SSC_ADJ_PER1 0x070
+#define QSERDES_COM_SSC_ADJ_PER2 0x074
+#define QSERDES_COM_SSC_PER1 0x078
+#define QSERDES_COM_SSC_PER2 0x07c
+#define QSERDES_COM_SSC_STEP_SIZE1 0x080
+#define QSERDES_COM_SSC_STEP_SIZE2 0x084
+#define QSERDES_COM_DIV_FRAC_START1 0x098
+#define QSERDES_COM_DIV_FRAC_START2 0x09c
+#define QSERDES_COM_DIV_FRAC_START3 0x0a0
+#define QSERDES_COM_DEC_START2 0x0a4
+#define QSERDES_COM_PLL_CRCTRL 0x0ac
+#define QSERDES_COM_RESET_SM 0x0bc
+
+/* QSERDES TX registers */
+#define QSERDES_TX_BIST_MODE_LANENO 0x100
+#define QSERDES_TX_TX_EMP_POST1_LVL 0x108
+#define QSERDES_TX_TX_DRV_LVL 0x10c
+
+/* QSERDES RX registers */
+#define QSERDES_RX_CDR_CONTROL 0x200
+#define QSERDES_RX_SIGDET_CNTRL 0x234
+#define QSERDES_RX_PWM_CNTRL1 0x280
+#define QSERDES_RX_PWM_CNTRL2 0x284
+#define QSERDES_RX_CDR_CONTROL_QUARTER 0x29c
+#define QSERDES_RX_RX_SIGDET_PWMDECSTATUS 0x2D8
+
+/* SATA PHY registers */
+#define SATA_PHY_SERDES_START 0x300
+#define SATA_PHY_CMN_PWR_CTRL 0x304
+#define SATA_PHY_RX_PWR_CTRL 0x308
+#define SATA_PHY_TX_PWR_CTRL 0x30c
+#define SATA_PHY_LANE_CTRL1 0x318
+#define SATA_PHY_CDR_CTRL0 0x358
+#define SATA_PHY_TX_DRV_WAKEUP 0x360
+#define SATA_PHY_CLK_BUF_SETTLING 0x364
+#define SATA_PHY_SPDNEG_CFG0 0x370
+#define SATA_PHY_SPDNEG_CFG1 0x374
+#define SATA_PHY_POW_DWN_CTRL0 0x380
+#define SATA_PHY_ALIGNP 0x3a4
+
+#define MAX_PROP_NAME 32
+#define VDDA_PHY_MIN_UV 1000000
+#define VDDA_PHY_MAX_UV 1000000
+#define VDDA_PLL_MIN_UV 1800000
+#define VDDA_PLL_MAX_UV 1800000
+
+struct msm_sata_phy_vreg {
+ const char *name;
+ struct regulator *reg;
+ int max_uA;
+ int min_uV;
+ int max_uV;
+ bool enabled;
+};
+
+struct msm_sata_phy {
+ struct device *dev;
+ void __iomem *mmio;
+ void __iomem *phy_sel;
+ struct clk *ref_clk_src;
+ struct clk *ref_clk_parent;
+ struct clk *ref_clk;
+ struct clk *rxoob_clk;
+ bool is_ref_clk_enabled;
+ bool is_rxoob_clk_enabled;
+ struct msm_sata_phy_vreg vdda_pll;
+ struct msm_sata_phy_vreg vdda_phy;
+ bool is_powered_on;
+};
+
+static int msm_sata_enable_phy_rxoob_clk(struct msm_sata_phy *phy)
+{
+ int err = 0;
+
+ if (phy->is_rxoob_clk_enabled)
+ goto out;
+
+ /* set max. 100MHz */
+ err = clk_set_rate(phy->rxoob_clk, 100000000);
+ if (err) {
+ dev_err(phy->dev, "%s: rxoob_clk set rate failed %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ err = clk_prepare_enable(phy->rxoob_clk);
+ if (err) {
+ dev_err(phy->dev, "%s: rxoob_clk enable failed %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ phy->is_rxoob_clk_enabled = true;
+out:
+ return err;
+}
+
+static void msm_sata_disable_phy_rxoob_clk(struct msm_sata_phy *phy)
+{
+ if (phy->is_rxoob_clk_enabled) {
+ clk_disable_unprepare(phy->rxoob_clk);
+ phy->is_rxoob_clk_enabled = false;
+ }
+}
+
+static int msm_sata_enable_phy_ref_clk(struct msm_sata_phy *phy)
+{
+ int err = 0;
+
+ if (phy->is_ref_clk_enabled)
+ goto out;
+
+ /*
+ * reference clock is propagated in a daisy-chained manner from
+ * source to phy, so ungate them at each stage.
+ */
+ err = clk_prepare_enable(phy->ref_clk_src);
+ if (err) {
+ dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ err = clk_prepare_enable(phy->ref_clk_parent);
+ if (err) {
+ dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
+ __func__, err);
+ goto out_disable_src;
+ }
+
+ err = clk_prepare_enable(phy->ref_clk);
+ if (err) {
+ dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
+ __func__, err);
+ goto out_disable_parent;
+ }
+
+ phy->is_ref_clk_enabled = true;
+ goto out;
+
+out_disable_parent:
+ clk_disable_unprepare(phy->ref_clk_parent);
+out_disable_src:
+ clk_disable_unprepare(phy->ref_clk_src);
+out:
+ return err;
+}
+
+static void msm_sata_disable_phy_ref_clk(struct msm_sata_phy *phy)
+{
+ if (phy->is_ref_clk_enabled) {
+ clk_disable_unprepare(phy->ref_clk);
+ clk_disable_unprepare(phy->ref_clk_parent);
+ clk_disable_unprepare(phy->ref_clk_src);
+ phy->is_ref_clk_enabled = false;
+ }
+}
+
+static int msm_sata_phy_cfg_vreg(struct device *dev,
+ struct msm_sata_phy_vreg *vreg, bool on)
+{
+ int err = 0;
+ struct regulator *reg = vreg->reg;
+ const char *name = vreg->name;
+ int min_uV, uA_load;
+
+ BUG_ON(!vreg);
+
+ if (regulator_count_voltages(reg) > 0) {
+ min_uV = on ? vreg->min_uV : 0;
+ err = regulator_set_voltage(reg, min_uV, vreg->max_uV);
+ if (err) {
+ dev_err(dev, "%s: %s set voltage failed, err=%d\n",
+ __func__, name, err);
+ goto out;
+ }
+
+ uA_load = on ? vreg->max_uA : 0;
+ err = regulator_set_optimum_mode(reg, uA_load);
+ if (err >= 0) {
+ /*
+ * regulator_set_optimum_mode() returns new regulator
+ * mode upon success.
+ */
+ err = 0;
+ } else {
+ dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+ __func__, name, uA_load, err);
+ goto out;
+ }
+ }
+out:
+ return err;
+}
+
+static int msm_sata_phy_enable_vreg(struct msm_sata_phy *phy,
+ struct msm_sata_phy_vreg *vreg)
+{
+ struct device *dev = phy->dev;
+ int err = 0;
+
+ if (!vreg || vreg->enabled)
+ goto out;
+
+ err = msm_sata_phy_cfg_vreg(dev, vreg, true);
+ if (!err)
+ err = regulator_enable(vreg->reg);
+
+ if (!err)
+ vreg->enabled = true;
+ else
+ dev_err(dev, "%s: %s enable failed, err=%d\n",
+ __func__, vreg->name, err);
+out:
+ return err;
+}
+
+static int msm_sata_phy_disable_vreg(struct msm_sata_phy *phy,
+ struct msm_sata_phy_vreg *vreg)
+{
+ struct device *dev = phy->dev;
+ int err = 0;
+
+ if (!vreg || !vreg->enabled)
+ goto out;
+
+ err = regulator_disable(vreg->reg);
+
+ if (!err) {
+ /* ignore errors on applying disable config */
+ msm_sata_phy_cfg_vreg(dev, vreg, false);
+ vreg->enabled = false;
+ } else {
+ dev_err(dev, "%s: %s disable failed, err=%d\n",
+ __func__, vreg->name, err);
+ }
+out:
+ return err;
+}
+
+static int msm_sata_phy_init_vreg(struct device *dev,
+ struct msm_sata_phy_vreg *vreg, const char *name)
+{
+ int err = 0;
+ char prop_name[MAX_PROP_NAME];
+
+ vreg->name = kstrdup(name, GFP_KERNEL);
+ if (!vreg->name) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ vreg->reg = devm_regulator_get(dev, name);
+ if (IS_ERR(vreg->reg)) {
+ err = PTR_ERR(vreg->reg);
+ dev_err(dev, "failed to get %s, %d\n", name, err);
+ goto out;
+ }
+
+ if (dev->of_node) {
+ snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
+ err = of_property_read_u32(dev->of_node,
+ prop_name, &vreg->max_uA);
+ if (err && err != -EINVAL) {
+ dev_err(dev, "%s: failed to read %s\n",
+ __func__, prop_name);
+ goto out;
+ } else if (err == -EINVAL || !vreg->max_uA) {
+ if (regulator_count_voltages(vreg->reg) > 0) {
+ dev_err(dev, "%s: %s is mandatory\n",
+ __func__, prop_name);
+ goto out;
+ }
+ err = 0;
+ }
+ }
+
+ if (!strcmp(name, "vdda-pll")) {
+ vreg->max_uV = VDDA_PLL_MAX_UV;
+ vreg->min_uV = VDDA_PLL_MIN_UV;
+ } else if (!strcmp(name, "vdda-phy")) {
+ vreg->max_uV = VDDA_PHY_MAX_UV;
+ vreg->min_uV = VDDA_PHY_MIN_UV;
+ }
+
+out:
+ if (err)
+ kfree(vreg->name);
+ return err;
+}
+
+static int msm_sata_phy_clk_get(struct device *dev,
+ const char *name, struct clk **clk_out)
+{
+ struct clk *clk;
+ int err = 0;
+
+ clk = devm_clk_get(dev, name);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ dev_err(dev, "failed to get %s err %d", name, err);
+ } else {
+ *clk_out = clk;
+ }
+
+ return err;
+}
+
+static int msm_sata_phy_power_up(struct msm_sata_phy *phy)
+{
+ int err = 0;
+ u32 reg;
+ struct device *dev = phy->dev;
+
+ if (phy->phy_sel) {
+ /* Select SATA PHY */
+ writel_relaxed(0x0, phy->phy_sel);
+
+ /*
+ * SATA PHY must be selected before configuring the PHY.
+ * The phy_sel and phy_mmio may be in different register space
+ * and *_relaxed version doesn't ensure ordering in such case.
+ */
+ mb();
+ }
+
+ /* SATA PHY powerup sequence */
+
+ /* PWM configurations */
+ writel_relaxed(0x08, phy->mmio + QSERDES_RX_PWM_CNTRL1);
+ writel_relaxed(0x40, phy->mmio + QSERDES_RX_PWM_CNTRL2);
+
+ /* Configure PHY power control to operate in mission mode */
+ writel_relaxed(0x01, phy->mmio + SATA_PHY_POW_DWN_CTRL0);
+
+ /* CDR counter selected between 30-40 */
+ writel_relaxed(0x25, phy->mmio + SATA_PHY_CDR_CTRL0);
+
+ /* Wakeup counter values are set to Maximum */
+ writel_relaxed(0x0f, phy->mmio + SATA_PHY_CLK_BUF_SETTLING);
+ writel_relaxed(0xff, phy->mmio + SATA_PHY_TX_DRV_WAKEUP);
+ writel_relaxed(0xff, phy->mmio + SATA_PHY_SPDNEG_CFG0);
+ writel_relaxed(0x25, phy->mmio + SATA_PHY_CDR_CTRL0);
+
+ /* PLL register settings */
+ writel_relaxed(0xec, phy->mmio + QSERDES_COM_PLL_CRCTRL);
+ writel_relaxed(0x01, phy->mmio + QSERDES_COM_PLL_IP_SETI);
+ writel_relaxed(0x3f, phy->mmio + QSERDES_COM_PLL_CP_SETI);
+ writel_relaxed(0x0f, phy->mmio + QSERDES_COM_PLL_IP_SETP);
+ writel_relaxed(0x13, phy->mmio + QSERDES_COM_PLL_CP_SETP);
+
+ /* PCS settings for COMMON, TX, RX paths */
+ writel_relaxed(0x5b, phy->mmio + SATA_PHY_CMN_PWR_CTRL);
+ writel_relaxed(0x32, phy->mmio + SATA_PHY_TX_PWR_CTRL);
+ writel_relaxed(0x83, phy->mmio + SATA_PHY_RX_PWR_CTRL);
+ writel_relaxed(0x7b, phy->mmio + SATA_PHY_CMN_PWR_CTRL);
+
+ /* Ref clk frequency select - 19.2Mhz selected */
+ writel_relaxed(0x08, phy->mmio + QSERDES_COM_SYSCLK_EN_SEL);
+ writel_relaxed(0x06, phy->mmio + QSERDES_COM_SYS_CLK_CTRL);
+
+ /* Decimal and Fractional dividers configuration */
+ writel_relaxed(0x9c, phy->mmio + QSERDES_COM_DEC_START1);
+ writel_relaxed(0x03, phy->mmio + QSERDES_COM_DEC_START2);
+ writel_relaxed(0xff, phy->mmio + QSERDES_COM_DIV_FRAC_START1);
+ writel_relaxed(0xff, phy->mmio + QSERDES_COM_DIV_FRAC_START2);
+ writel_relaxed(0x13, phy->mmio + QSERDES_COM_DIV_FRAC_START3);
+
+ /* PLL configurations */
+ writel_relaxed(0xff, phy->mmio + QSERDES_COM_PLLLOCK_CMP1);
+ writel_relaxed(0x7c, phy->mmio + QSERDES_COM_PLLLOCK_CMP2);
+ writel_relaxed(0x00, phy->mmio + QSERDES_COM_PLLLOCK_CMP3);
+ writel_relaxed(0x01, phy->mmio + QSERDES_COM_PLLLOCK_CMP_EN);
+
+ /* Other Resetsm configurations - FAST_VCO_TUNE */
+ writel_relaxed(0x10, phy->mmio + QSERDES_COM_RESETSM_CNTRL);
+
+ /* PI configurations- First Order threshold and Second order gain */
+ writel_relaxed(0xeb, phy->mmio + QSERDES_RX_CDR_CONTROL);
+ writel_relaxed(0x1a, phy->mmio + QSERDES_RX_CDR_CONTROL_QUARTER);
+
+ /* TX configurations */
+ writel_relaxed(0x1f, phy->mmio + QSERDES_TX_TX_DRV_LVL);
+ writel_relaxed(0x00, phy->mmio + QSERDES_TX_BIST_MODE_LANENO);
+ writel_relaxed(0x30, phy->mmio + QSERDES_TX_TX_EMP_POST1_LVL);
+
+ /* SSC Configurations and Serdes start */
+ writel_relaxed(0x00, phy->mmio + QSERDES_COM_SSC_EN_CENTER);
+ writel_relaxed(0x31, phy->mmio + QSERDES_COM_SSC_PER1);
+ writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_PER2);
+ writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_ADJ_PER1);
+ writel_relaxed(0x00, phy->mmio + QSERDES_COM_SSC_ADJ_PER2);
+ writel_relaxed(0x3f, phy->mmio + QSERDES_COM_SSC_STEP_SIZE1);
+ writel_relaxed(0x05, phy->mmio + QSERDES_COM_SSC_STEP_SIZE2);
+ writel_relaxed(0x01, phy->mmio + QSERDES_COM_SSC_EN_CENTER);
+
+ /*
+ * Flush all delayed writes before sleeping to ensure that PHY
+ * configuration is applied.
+ */
+ mb();
+
+ /* Sleep for 1ms before starting serdes */
+ usleep(1000);
+
+ /* Start serdes */
+ writel_relaxed(0x01, phy->mmio + SATA_PHY_SERDES_START);
+
+ /*
+ * Read RESETSM status until SERDES is ready,
+ * timeout after 1 sec
+ */
+ err = readl_poll_timeout(phy->mmio + QSERDES_COM_RESET_SM, reg,
+ (reg & (1 << 5)), 100, 1000000);
+ if (err) {
+ dev_err(dev, "%s: poll timeout QSERDES_COM_RESET_SM, status: 0x%x\n",
+ __func__, readl_relaxed(phy->mmio +
+ QSERDES_COM_RESET_SM));
+ goto out;
+ }
+
+ /* RX configurations */
+ writel_relaxed(0x5f, phy->mmio + SATA_PHY_LANE_CTRL1);
+ writel_relaxed(0x43, phy->mmio + SATA_PHY_ALIGNP);
+
+ dev_dbg(dev, "SATA PHY powered up in functional mode\n");
+out:
+ /* power down PHY in case of failure */
+ if (err)
+ writel_relaxed(0x0, phy->mmio + SATA_PHY_POW_DWN_CTRL0);
+
+ return err;
+}
+
+static int msm_sata_phy_power_off(struct phy *generic_phy)
+{
+ struct msm_sata_phy *phy = phy_get_drvdata(generic_phy);
+
+ writel_relaxed(0x0, phy->mmio + SATA_PHY_POW_DWN_CTRL0);
+ /*
+ * Ensure that the PHY is power down before gating power.
+ * It is possible that PHY regulators might be turned on if
+ * other PHY's share the same regulators.
+ */
+ mb();
+
+ msm_sata_disable_phy_rxoob_clk(phy);
+ msm_sata_disable_phy_ref_clk(phy);
+
+ msm_sata_phy_disable_vreg(phy, &phy->vdda_pll);
+ msm_sata_phy_disable_vreg(phy, &phy->vdda_phy);
+ phy->is_powered_on = false;
+
+ return 0;
+}
+
+static int msm_sata_phy_power_on(struct phy *generic_phy)
+{
+ int err;
+ struct msm_sata_phy *phy = phy_get_drvdata(generic_phy);
+
+ err = msm_sata_phy_enable_vreg(phy, &phy->vdda_phy);
+ if (err)
+ goto out;
+
+ /* vdda_pll also enables ref clock LDOs so enable it first */
+ err = msm_sata_phy_enable_vreg(phy, &phy->vdda_pll);
+ if (err)
+ goto out_disable_phy;
+
+ err = msm_sata_enable_phy_ref_clk(phy);
+ if (err)
+ goto out_disable_pll;
+
+ err = msm_sata_enable_phy_rxoob_clk(phy);
+ if (err)
+ goto out_disable_ref;
+
+ err = msm_sata_phy_power_up(phy);
+ if (err)
+ goto out_disable_rxoob;
+
+ phy->is_powered_on = true;
+ goto out;
+
+out_disable_rxoob:
+ msm_sata_disable_phy_rxoob_clk(phy);
+out_disable_ref:
+ msm_sata_disable_phy_ref_clk(phy);
+out_disable_pll:
+ msm_sata_phy_disable_vreg(phy, &phy->vdda_pll);
+out_disable_phy:
+ msm_sata_phy_disable_vreg(phy, &phy->vdda_phy);
+out:
+ return err;
+}
+
+static int msm_sata_phy_init(struct phy *generic_phy)
+{
+ int err;
+ struct msm_sata_phy *phy = phy_get_drvdata(generic_phy);
+ struct device *dev = phy->dev;
+
+ err = msm_sata_phy_clk_get(dev, "ref_clk_src", &phy->ref_clk_src);
+ if (err)
+ goto out;
+
+ err = msm_sata_phy_clk_get(dev, "ref_clk_parent", &phy->ref_clk_parent);
+ if (err)
+ goto out;
+
+ err = msm_sata_phy_clk_get(dev, "ref_clk", &phy->ref_clk);
+ if (err)
+ goto out;
+
+ err = msm_sata_phy_clk_get(dev, "rxoob_clk", &phy->rxoob_clk);
+ if (err)
+ goto out;
+
+ err = msm_sata_phy_init_vreg(dev, &phy->vdda_pll, "vdda-pll");
+ if (err)
+ goto out;
+
+ err = msm_sata_phy_init_vreg(dev, &phy->vdda_phy, "vdda-phy");
+ if (err)
+ goto out;
+out:
+ return err;
+}
+
+static int msm_sata_phy_exit(struct phy *generic_phy)
+{
+ struct msm_sata_phy *phy = phy_get_drvdata(generic_phy);
+
+ if (phy->is_powered_on)
+ msm_sata_phy_power_off(generic_phy);
+
+ return 0;
+}
+
+static struct phy_ops msm_sata_phy_ops = {
+ .init = msm_sata_phy_init,
+ .exit = msm_sata_phy_exit,
+ .power_on = msm_sata_phy_power_on,
+ .power_off = msm_sata_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static int msm_sata_phy_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct msm_sata_phy *phy;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct phy_provider *phy_provider;
+ struct phy *generic_phy;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ err = -ENOMEM;
+ dev_err(dev, "%s: failed to allocate phy\n", __func__);
+ goto out;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_sel");
+ phy->phy_sel = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->phy_sel)) {
+ err = PTR_ERR(phy->phy_sel);
+ /* phy_sel resource is optional */
+ dev_dbg(dev, "%s: phy select resource get failed %d\n",
+ __func__, err);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
+ phy->mmio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(phy->mmio)) {
+ err = PTR_ERR(phy->mmio);
+ dev_err(dev, "%s: phy mmio get resource failed %d\n",
+ __func__, err);
+ goto out;
+ }
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider)) {
+ err = PTR_ERR(phy_provider);
+ dev_err(dev, "%s: failed to register phy %d\n", __func__, err);
+ goto out;
+ }
+
+ generic_phy = devm_phy_create(dev, &msm_sata_phy_ops, NULL);
+ if (IS_ERR(generic_phy)) {
+ err = PTR_ERR(generic_phy);
+ dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
+ goto out;
+ }
+
+ phy->dev = dev;
+ phy_set_drvdata(generic_phy, phy);
+
+ return 0;
+out:
+ return err;
+}
+
+static const struct of_device_id msm_sata_phy_of_match[] = {
+ { .compatible = "qcom,sataphy" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, msm_sata_phy_of_match);
+
+static struct platform_driver msm_sata_phy_driver = {
+ .probe = msm_sata_phy_probe,
+ .driver = {
+ .name = "msm-sata-phy",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_sata_phy_of_match,
+ }
+};
+module_platform_driver(msm_sata_phy_driver);
+
+MODULE_DESCRIPTION("MSM 6Gbps SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 604526e65c08..8ed898c4b3a9 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -48,7 +48,7 @@ config SPS_SUPPORT_NDP_BAM
config QPNP_POWER_ON
tristate "QPNP PMIC POWER-ON Driver"
- depends on OF_SPMI && SPMI && MSM_QPNP_INT
+ depends on OF_SPMI && SPMI && MSM_QPNP_INT && INPUT
help
This driver supports the power-on functionality on Qualcomm
PNP PMIC. It currently supports reporting the change in status of
diff --git a/drivers/platform/msm/avtimer.c b/drivers/platform/msm/avtimer.c
index 00b8c238e941..354ddff601b5 100644
--- a/drivers/platform/msm/avtimer.c
+++ b/drivers/platform/msm/avtimer.c
@@ -24,7 +24,7 @@
#include <linux/of.h>
#include <linux/wait.h>
#include <linux/sched.h>
-#include <mach/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr.h>
#define DEVICE_NAME "avtimer"
#define TIMEOUT_MS 1000
diff --git a/drivers/platform/msm/ipa/ipa.c b/drivers/platform/msm/ipa/ipa.c
index 37d8004ec82f..ba4a180f0a08 100644
--- a/drivers/platform/msm/ipa/ipa.c
+++ b/drivers/platform/msm/ipa/ipa.c
@@ -54,7 +54,7 @@
static struct ipa_plat_drv_res ipa_res = {0, };
static struct of_device_id ipa_plat_drv_match[] = {
{
- .compatible = "qti,ipa",
+ .compatible = "qcom,ipa",
},
{
@@ -1247,55 +1247,55 @@ static int ipa_update_connections_info(struct device_node *node,
if (!pipe_connection || !node)
return -EINVAL;
- key = "qti,src-bam-physical-address";
+ key = "qcom,src-bam-physical-address";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->src_phy_addr = val;
- key = "qti,ipa-bam-mem-type";
+ key = "qcom,ipa-bam-mem-type";
rc = of_property_read_u32(node, key, &mem_type);
if (rc)
goto err;
pipe_connection->mem_type = mem_type;
- key = "qti,src-bam-pipe-index";
+ key = "qcom,src-bam-pipe-index";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->src_pipe_index = val;
- key = "qti,dst-bam-physical-address";
+ key = "qcom,dst-bam-physical-address";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->dst_phy_addr = val;
- key = "qti,dst-bam-pipe-index";
+ key = "qcom,dst-bam-pipe-index";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->dst_pipe_index = val;
- key = "qti,data-fifo-offset";
+ key = "qcom,data-fifo-offset";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->data_fifo_base_offset = val;
- key = "qti,data-fifo-size";
+ key = "qcom,data-fifo-size";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->data_fifo_size = val;
- key = "qti,descriptor-fifo-offset";
+ key = "qcom,descriptor-fifo-offset";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
pipe_connection->desc_fifo_base_offset = val;
- key = "qti,descriptor-fifo-size";
+ key = "qcom,descriptor-fifo-size";
rc = of_property_read_u32(node, key, &val);
if (rc)
goto err;
@@ -2211,7 +2211,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
ipa_drv_res->ipa_hw_mode = 0;
/* Get IPA HW Version */
- result = of_property_read_u32(pdev->dev.of_node, "qti,ipa-hw-ver",
+ result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-ver",
&ipa_drv_res->ipa_hw_type);
if ((result) || (ipa_drv_res->ipa_hw_type == 0)) {
IPAERR(":get resource failed for ipa-hw-ver!\n");
@@ -2220,7 +2220,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
IPADBG(": ipa_hw_type = %d", ipa_drv_res->ipa_hw_type);
/* Get IPA HW mode */
- result = of_property_read_u32(pdev->dev.of_node, "qti,ipa-hw-mode",
+ result = of_property_read_u32(pdev->dev.of_node, "qcom,ipa-hw-mode",
&ipa_drv_res->ipa_hw_mode);
if (result)
IPADBG("using default (IPA_MODE_NORMAL) for ipa-hw-mode\n");
@@ -2230,20 +2230,20 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
ipa_drv_res->use_ipa_bamdma_a2_bridge =
of_property_read_bool(pdev->dev.of_node,
- "qti,use-ipa-bamdma-a2-bridge");
+ "qcom,use-ipa-bamdma-a2-bridge");
IPADBG(": using A2-BAMDMA bridge = %s",
ipa_drv_res->use_ipa_bamdma_a2_bridge ?
"True" : "False");
ipa_drv_res->use_a2_service = of_property_read_bool(pdev->dev.of_node,
- "qti,use-a2-service");
+ "qcom,use-a2-service");
IPADBG(": using A2 service = %s",
ipa_drv_res->use_a2_service
? "True" : "False");
ipa_drv_res->use_ipa_teth_bridge =
of_property_read_bool(pdev->dev.of_node,
- "qti,use-ipa-tethering-bridge");
+ "qcom,use-ipa-tethering-bridge");
IPADBG(": using TBDr = %s",
ipa_drv_res->use_ipa_teth_bridge
? "True" : "False");
@@ -2317,7 +2317,7 @@ static int get_ipa_dts_configuration(struct platform_device *pdev,
return -ENODEV;
}
- result = of_property_read_u32(pdev->dev.of_node, "qti,ee",
+ result = of_property_read_u32(pdev->dev.of_node, "qcom,ee",
&ipa_drv_res->ee);
if (result)
ipa_drv_res->ee = 0;
diff --git a/drivers/platform/msm/ipa/ipa_bridge.c b/drivers/platform/msm/ipa/ipa_bridge.c
index ecf58e7c72be..3cb889cab1cd 100644
--- a/drivers/platform/msm/ipa/ipa_bridge.c
+++ b/drivers/platform/msm/ipa/ipa_bridge.c
@@ -12,7 +12,7 @@
#include <linux/delay.h>
#include <linux/ratelimit.h>
-#include <mach/msm_smem.h>
+#include <soc/qcom/smem.h>
#include "ipa_i.h"
/*
diff --git a/drivers/platform/msm/ipa/ipa_dp.c b/drivers/platform/msm/ipa/ipa_dp.c
index 11bc02379df0..9e4d8d064602 100644
--- a/drivers/platform/msm/ipa/ipa_dp.c
+++ b/drivers/platform/msm/ipa/ipa_dp.c
@@ -53,8 +53,10 @@ static void ipa_wq_write_done_common(struct ipa_sys_context *sys, u32 cnt)
int i;
for (i = 0; i < cnt; i++) {
- if (unlikely(list_empty(&sys->head_desc_list)))
+ if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ return;
+ }
tx_pkt_expected = list_first_entry(&sys->head_desc_list,
struct ipa_tx_pkt_wrapper,
link);
@@ -87,14 +89,17 @@ static void ipa_wq_write_done_status(int src_pipe)
return;
}
- spin_lock(&sys->spinlock);
- if (unlikely(list_empty(&sys->head_desc_list)))
+ spin_lock_bh(&sys->spinlock);
+ if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ spin_unlock_bh(&sys->spinlock);
+ return;
+ }
tx_pkt_expected = list_first_entry(&sys->head_desc_list,
struct ipa_tx_pkt_wrapper,
link);
ipa_wq_write_done_common(sys, tx_pkt_expected->cnt);
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
}
/**
@@ -120,9 +125,9 @@ static void ipa_wq_write_done(struct work_struct *work)
cnt = tx_pkt->cnt;
sys = tx_pkt->sys;
- spin_lock(&sys->spinlock);
+ spin_lock_bh(&sys->spinlock);
ipa_wq_write_done_common(sys, cnt);
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
}
static int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all,
@@ -145,9 +150,9 @@ static int ipa_handle_tx_core(struct ipa_sys_context *sys, bool process_all,
if (iov.addr == 0)
break;
- spin_lock(&sys->spinlock);
+ spin_lock_bh(&sys->spinlock);
ipa_wq_write_done_common(sys, 1);
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
cnt++;
};
@@ -314,7 +319,7 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
INIT_WORK(&tx_pkt->work, ipa_wq_write_done);
- spin_lock(&sys->spinlock);
+ spin_lock_bh(&sys->spinlock);
list_add_tail(&tx_pkt->link, &sys->head_desc_list);
result = sps_transfer_one(sys->ep->ep_hdl, dma_address, len, tx_pkt,
sps_flags);
@@ -323,13 +328,13 @@ int ipa_send_one(struct ipa_sys_context *sys, struct ipa_desc *desc,
goto fail_sps_send;
}
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
return 0;
fail_sps_send:
list_del(&tx_pkt->link);
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
if (unlikely(ipa_ctx->ipa_hw_type == IPA_HW_v1_0))
dma_pool_free(ipa_ctx->dma_pool, tx_pkt->bounce,
dma_address);
@@ -384,7 +389,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
transfer.iovec = dma_pool_alloc(ipa_ctx->dma_pool, mem_flag, &dma_addr);
transfer.iovec_phys = dma_addr;
transfer.iovec_count = num_desc;
- spin_lock(&sys->spinlock);
+ spin_lock_bh(&sys->spinlock);
if (!transfer.iovec) {
IPAERR("fail to alloc DMA mem for sps xfr buff\n");
goto failure_coherent;
@@ -491,7 +496,7 @@ int ipa_send(struct ipa_sys_context *sys, u32 num_desc, struct ipa_desc *desc,
goto failure;
}
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
return 0;
failure:
@@ -518,7 +523,7 @@ failure:
dma_pool_free(ipa_ctx->dma_pool, transfer.iovec,
transfer.iovec_phys);
failure_coherent:
- spin_unlock(&sys->spinlock);
+ spin_unlock_bh(&sys->spinlock);
return -EFAULT;
}
@@ -1913,8 +1918,10 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
struct ipa_rx_pkt_wrapper *rx_pkt_expected;
struct sk_buff *rx_skb;
- if (unlikely(list_empty(&sys->head_desc_list)))
+ if (unlikely(list_empty(&sys->head_desc_list))) {
WARN_ON(1);
+ return;
+ }
rx_pkt_expected = list_first_entry(&sys->head_desc_list,
struct ipa_rx_pkt_wrapper,
link);
@@ -1930,9 +1937,8 @@ static void ipa_wq_rx_common(struct ipa_sys_context *sys, u32 size)
rx_skb->truesize = rx_pkt_expected->len + sizeof(struct sk_buff);
sys->pyld_hdlr(rx_skb, sys);
- ipa_replenish_rx_cache(sys);
- kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt_expected);
-
+ ipa_replenish_rx_cache(sys);
+ kmem_cache_free(ipa_ctx->rx_pkt_wrapper_cache, rx_pkt_expected);
}
static void ipa_wq_rx_avail(struct work_struct *work)
diff --git a/drivers/platform/msm/ipa/ipa_nat.c b/drivers/platform/msm/ipa/ipa_nat.c
index f9ae85f7860c..98c2a0ffe87a 100644
--- a/drivers/platform/msm/ipa/ipa_nat.c
+++ b/drivers/platform/msm/ipa/ipa_nat.c
@@ -79,6 +79,12 @@ static int ipa_nat_mmap(struct file *filp, struct vm_area_struct *vma)
} else {
IPADBG("Mapping shared(local) memory\n");
IPADBG("map sz=0x%lx\n", vsize);
+
+ if ((IPA_NAT_PHYS_MEM_SIZE == 0) ||
+ (vsize > IPA_NAT_PHYS_MEM_SIZE)) {
+ result = -EINVAL;
+ goto bail;
+ }
phys_addr = ipa_ctx->ipa_wrapper_base + IPA_REG_BASE_OFST +
IPA_SRAM_DIRECT_ACCESS_N_OFST(IPA_NAT_PHYS_MEM_OFFSET);
@@ -225,6 +231,7 @@ int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
struct ipa_ip_v4_nat_init *cmd;
u16 size = sizeof(struct ipa_ip_v4_nat_init);
int result;
+ u32 offset = 0;
IPADBG("\n");
if (init->tbl_index < 0 || init->table_entries <= 0) {
@@ -245,6 +252,26 @@ int ipa_nat_init_cmd(struct ipa_ioc_v4_nat_init *init)
cmd->index_table_addr_type = IPA_NAT_SYSTEM_MEMORY;
cmd->index_table_expansion_addr_type = IPA_NAT_SYSTEM_MEMORY;
+ offset = UINT_MAX - ipa_ctx->nat_mem.dma_handle;
+
+ if ((init->ipv4_rules_offset > offset) ||
+ (init->expn_rules_offset > offset) ||
+ (init->index_offset > offset) ||
+ (init->index_expn_offset > offset)) {
+ IPAERR("Failed due to integer overflow\n");
+ IPAERR("nat.mem.dma_handle: 0x%x\n",
+ ipa_ctx->nat_mem.dma_handle);
+ IPAERR("ipv4_rules_offset: 0x%x\n",
+ init->ipv4_rules_offset);
+ IPAERR("expn_rules_offset: 0x%x\n",
+ init->expn_rules_offset);
+ IPAERR("index_offset: 0x%x\n",
+ init->index_offset);
+ IPAERR("index_expn_offset: 0x%x\n",
+ init->index_expn_offset);
+ result = -EPERM;
+ goto free_cmd;
+ }
cmd->ipv4_rules_addr =
ipa_ctx->nat_mem.dma_handle + init->ipv4_rules_offset;
IPADBG("ipv4_rules_offset:0x%x\n", init->ipv4_rules_offset);
diff --git a/drivers/platform/msm/ipa/rmnet_ipa.c b/drivers/platform/msm/ipa/rmnet_ipa.c
index d6321c115f9d..a763c8667d6b 100644
--- a/drivers/platform/msm/ipa/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/rmnet_ipa.c
@@ -387,6 +387,7 @@ static int wwan_add_ul_flt_rule_to_ipa(void)
{
u32 pyld_sz;
int i, retval = 0;
+ int num_v4_rule = 0, num_v6_rule = 0;
struct ipa_ioc_add_flt_rule *param;
struct ipa_flt_rule_add flt_rule_entry;
struct ipa_fltr_installed_notif_req_msg_v01 req;
@@ -436,9 +437,15 @@ static int wwan_add_ul_flt_rule_to_ipa(void)
req.install_status = QMI_RESULT_SUCCESS_V01;
req.filter_index_list_len = num_q6_rule;
for (i = 0; i < num_q6_rule; i++) {
+ if (q6_ul_filter_rule[i].ip == IPA_IP_v4) {
+ req.filter_index_list[i].filter_index = num_v4_rule;
+ num_v4_rule++;
+ } else {
+ req.filter_index_list[i].filter_index = num_v6_rule;
+ num_v6_rule++;
+ }
req.filter_index_list[i].filter_handle =
q6_ul_filter_rule[i].filter_hdl;
- req.filter_index_list[i].filter_index = i;
}
if (qmi_filter_notify_send(&req)) {
IPAWANDBG("add filter rule index on A7-RX failed\n");
diff --git a/drivers/platform/msm/qca1530.c b/drivers/platform/msm/qca1530.c
index 593d3d8fc85a..60e4c9a3eee3 100644
--- a/drivers/platform/msm/qca1530.c
+++ b/drivers/platform/msm/qca1530.c
@@ -42,6 +42,10 @@
#define QCA1530_OF_CLK_GPIO_NAME "qca,clk-gpio"
/* xLNA power regulator prefix for DTS */
#define QCA1530_OF_XLNA_REG_NAME "qca,xlna"
+/* xLNA voltage property name in DTS */
+#define QCA1530_OF_XLNA_POWER_VOLTAGE "qca,xlna-voltage-level"
+/* xLNA current property name in DTS */
+#define QCA1530_OF_XLNA_POWER_CURRENT "qca,xlna-current-level"
/* xLNA pin name for DTS */
#define QCA1530_OF_XLNA_GPIO_NAME "qca,xlna-gpio"
@@ -51,7 +55,6 @@
* @reset_gpio: Number of GPIO pin for reset control, or -1
* @reset_reg: Handle to power regulator for reset control, or NULL
* @rtc_clk: RTC clock handle (32KHz)
- * @rtc_clk_state: RTC clock state
* @rtc_clk_gpio: RTC clock gppio, or -1
* @tcxo_clk: TCXO clock handle
* @pwr_reg: Main SoC power regulator handle, or NULL
@@ -66,7 +69,6 @@ struct qca1530_static {
int reset_gpio;
struct regulator *reset_reg;
struct clk *rtc_clk;
- int rtc_clk_state;
int rtc_clk_gpio;
struct clk *tcxo_clk;
struct regulator *pwr_reg;
@@ -80,7 +82,6 @@ struct qca1530_static {
*/
static struct qca1530_static qca1530_data = {
.reset_gpio = -1,
- .rtc_clk_state = -1,
.rtc_clk_gpio = -1,
.pwr_gpio = -1,
.xlna_gpio = -1,
@@ -88,6 +89,7 @@ static struct qca1530_static qca1530_data = {
/**
* qca1530_deinit_gpio() - release GPIO resource
+ * @pdev: platform device data
* @pgpio: pointer to GPIO handle
*
* Function releases GPIO handle allocated by the driver. By default the
@@ -96,16 +98,15 @@ static struct qca1530_static qca1530_data = {
*
* GPIO handle is set to -1.
*/
-static void qca1530_deinit_gpio(int *pgpio)
+static void qca1530_deinit_gpio(struct platform_device *pdev, int *pgpio)
{
int gpio = *pgpio;
if (gpio >= 0) {
pr_debug("Releasing GPIO: %d", gpio);
-
gpio_unexport(gpio);
gpio_direction_input(gpio);
- gpio_free(gpio);
+ devm_gpio_free(&pdev->dev, gpio);
*pgpio = -1;
}
}
@@ -119,21 +120,21 @@ static void qca1530_deinit_gpio(int *pgpio)
static void qca1530_deinit_regulator(struct regulator **ppwr)
{
if (*ppwr) {
- regulator_put(*ppwr);
+ devm_regulator_put(*ppwr);
*ppwr = NULL;
}
}
/**
- * qca1530_clk_set_rtc() - enable or disable clock
+ * qca1530_clk_prepare() - prepare or unprepare clock
* @clk: clock handle
- * @mode: 0 to disable, 1 to enable
+ * @mode: 0 to unprepare, 1 to prepare
*
- * Function turns clock on or off and logs the result.
+ * Function prepares or unprepares clock and logs the result.
*
* Return: 0 on success, error code on failure
*/
-static int qca1530_clk_set_rtc(struct clk *clk, int mode)
+static int qca1530_clk_prepare(struct clk *clk, int mode)
{
int ret = 0;
@@ -161,12 +162,12 @@ static void qca1530_clk_set_gpio(int mode)
}
/**
- * qca1530_clk_set() - start/stop RTC clocks
+ * qca1530_clk_set() - start/stop initialized clocks
* @mode: 1 to start clocks, 0 to stop
*
* Function turns clocks on or off. Clock configuration is optional, however
- * when configured, the following order is used: GPIO pin, RTC clock, TCXO
- * clock. When switching off, the order is reveresed.
+ * when configured, the following order is used: RTC GPIO pin, RTC clock,
+ * TCXO clock. When switching off, the order is reveresed.
*
* Return: 0 on success, error code on failure.
*/
@@ -181,18 +182,16 @@ static int qca1530_clk_set(int mode)
if (qca1530_data.rtc_clk_gpio >= 0)
qca1530_clk_set_gpio(1);
if (qca1530_data.rtc_clk)
- ret = qca1530_clk_set_rtc(qca1530_data.rtc_clk, 1);
+ ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 1);
if (!ret && qca1530_data.tcxo_clk)
- ret = qca1530_clk_set_rtc(qca1530_data.tcxo_clk, 1);
- qca1530_data.rtc_clk_state = ret ? 0 : 1;
+ ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 1);
} else {
if (qca1530_data.tcxo_clk)
- ret = qca1530_clk_set_rtc(qca1530_data.tcxo_clk, 0);
+ ret = qca1530_clk_prepare(qca1530_data.tcxo_clk, 0);
if (!ret && qca1530_data.rtc_clk)
- ret = qca1530_clk_set_rtc(qca1530_data.rtc_clk, 0);
+ ret = qca1530_clk_prepare(qca1530_data.rtc_clk, 0);
if (!ret && qca1530_data.rtc_clk_gpio >= 0)
qca1530_clk_set_gpio(0);
- qca1530_data.rtc_clk_state = 0;
}
pr_debug("Configured clk: mode=%d ret=%d", mode, ret);
@@ -201,21 +200,24 @@ static int qca1530_clk_set(int mode)
}
/**
- * qca1530_clk_deinit_rtc() - release clocks
+ * qca1530_clk_release_clocks() - release clocks
* @pdev: platform device data
*
- * Function sets clock handle references to NULL.
+ * Function releases initialized clocks and sets clock handle
+ * references to NULL.
*/
-static void qca1530_clk_deinit_rtc(struct platform_device *pdev)
+static void qca1530_clk_release_clocks(struct platform_device *pdev)
{
if (qca1530_data.tcxo_clk) {
pr_debug("Unregistering CLK: device=%s name=%s",
dev_name(&pdev->dev), QCA1530_TCXO_CLK_ID);
+ devm_clk_put(&pdev->dev, qca1530_data.tcxo_clk);
qca1530_data.tcxo_clk = NULL;
}
if (qca1530_data.rtc_clk) {
pr_debug("Unregistering CLK: device=%s name=%s",
dev_name(&pdev->dev), QCA1530_RTC_CLK_ID);
+ devm_clk_put(&pdev->dev, qca1530_data.rtc_clk);
qca1530_data.rtc_clk = NULL;
}
}
@@ -306,12 +308,10 @@ static int qca1530_clk_init(struct platform_device *pdev)
return 0;
err_2:
- qca1530_deinit_gpio(&qca1530_data.rtc_clk_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.rtc_clk_gpio);
err_1:
- qca1530_clk_deinit_rtc(pdev);
+ qca1530_clk_release_clocks(pdev);
err_0:
- qca1530_data.rtc_clk_state = -1;
-
pr_err("init error: ret=%d", ret);
return ret;
@@ -326,9 +326,8 @@ err_0:
static void qca1530_clk_deinit(struct platform_device *pdev)
{
qca1530_clk_set(0);
- qca1530_deinit_gpio(&qca1530_data.rtc_clk_gpio);
- qca1530_clk_deinit_rtc(pdev);
- qca1530_data.rtc_clk_state = -1;
+ qca1530_deinit_gpio(pdev, &qca1530_data.rtc_clk_gpio);
+ qca1530_clk_release_clocks(pdev);
}
/**
@@ -360,33 +359,26 @@ static int qca1530_pwr_set_regulator(struct regulator *reg, int mode)
pr_debug("Setting regulator: mode=%d regulator=%p", mode, reg);
if (mode) {
- if (regulator_is_enabled(reg)) {
- ret = 0;
- goto done;
- }
-
- ret = regulator_set_mode(reg, REGULATOR_MODE_NORMAL);
+ ret = regulator_enable(reg);
if (ret)
- pr_warn("failed to set regulator mode, ret=%d", ret);
+ pr_err("Failed to enable regulator, rc=%d", ret);
+ else
+ pr_debug("Regulator %p was enabled (%d)", reg, ret);
- ret = regulator_enable(reg);
- if (ret) {
- pr_err("failed to enable regulator, rc=%d", ret);
- goto done;
- }
} else {
if (!regulator_is_enabled(reg)) {
ret = 0;
- goto done;
- }
-
- ret = regulator_disable(reg);
- if (ret) {
- pr_err("failed to disable regulator, rc=%d", ret);
- goto done;
+ } else {
+ ret = regulator_disable(reg);
+ if (ret)
+ pr_err("Failed to disable regulator, rc=%d",
+ ret);
+ else
+ pr_debug("Regulator %p was disabled (%d)", reg,
+ ret);
}
}
-done:
+
pr_debug("Regulator result: regulator=%p mode=%d ret=%d", reg, mode,
ret);
@@ -403,7 +395,8 @@ static int qca1530_pwr_init_gpio(struct platform_device *pdev)
{
int ret;
- ret = of_get_named_gpio(pdev->dev.of_node, QCA1530_OF_PWR_GPIO_NAME, 0);
+ ret = of_get_named_gpio(pdev->dev.of_node,
+ QCA1530_OF_PWR_GPIO_NAME, 0);
if (ret == -ENOENT) {
qca1530_data.pwr_gpio = ret;
ret = 0;
@@ -428,7 +421,8 @@ static int qca1530_pwr_init_gpio(struct platform_device *pdev)
* Return: 0 on success, error code otherwise
*/
static int qca1530_pwr_init_regulator(
- struct platform_device *pdev, const char *name, struct regulator **ppwr)
+ struct platform_device *pdev,
+ const char *name, struct regulator **ppwr)
{
int ret;
struct regulator *pwr;
@@ -517,7 +511,7 @@ static int qca1530_pwr_init(struct platform_device *pdev)
return ret;
err_2:
- qca1530_deinit_gpio(&qca1530_data.pwr_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio);
err_1:
qca1530_deinit_regulator(&qca1530_data.pwr_reg);
err_0:
@@ -526,13 +520,14 @@ err_0:
/**
* qca1530_pwr_deinit() - release main SoC power control
+ * @pdev: platform device data
*
* Function releases power regulator and power GPIO pin.
*/
-static void qca1530_pwr_deinit(void)
+static void qca1530_pwr_deinit(struct platform_device *pdev)
{
qca1530_pwr_set(0);
- qca1530_deinit_gpio(&qca1530_data.pwr_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.pwr_gpio);
qca1530_deinit_regulator(&qca1530_data.pwr_reg);
}
@@ -597,21 +592,22 @@ static int qca1530_reset_init(struct platform_device *pdev)
err_supply_configure:
qca1530_deinit_regulator(&qca1530_data.reset_reg);
err_gpio_configure:
- qca1530_deinit_gpio(&qca1530_data.reset_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.reset_gpio);
err_gpio_get:
return ret;
}
/**
* qca1530_reset_deinit() - release reset control resources
+ * @pdev: platform device data
*
* Function releases reset line GPIO and switches off and releases power
* regulator.
*/
-static void qca1530_reset_deinit(void)
+static void qca1530_reset_deinit(struct platform_device *pdev)
{
pr_debug("reset control: releasing");
- qca1530_deinit_gpio(&qca1530_data.reset_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.reset_gpio);
if (qca1530_data.reset_reg) {
qca1530_pwr_set_regulator(qca1530_data.reset_reg, 0);
qca1530_deinit_regulator(&qca1530_data.reset_reg);
@@ -661,9 +657,11 @@ static int qca1530_xlna_set(int mode)
if (!qca1530_data.xlna_reg && qca1530_data.xlna_gpio < 0)
ret = -ENOSYS;
else if (mode) {
- if (qca1530_data.xlna_reg)
+ if (qca1530_data.xlna_reg) {
ret = qca1530_pwr_set_regulator(
qca1530_data.xlna_reg, mode);
+ }
+
if (!ret && qca1530_data.xlna_gpio >= 0)
gpio_set_value(qca1530_data.xlna_gpio, 1);
} else {
@@ -677,11 +675,45 @@ static int qca1530_xlna_set(int mode)
}
/**
+ * qca1530_read_u32_arr_property() - read u32 values property
+ * @pdev: platform device data
+ * @pname: property name
+ * @num_values: array size
+ * @values: array to put results to
+ *
+ * Function reads property with given name. Filles passed array of
+ * u32 values.
+ *
+ * Return: 0 on successful read, error code on error.
+ */
+static int qca1530_read_u32_arr_property(struct platform_device *pdev,
+ const char *pname, const int num_values, u32 *values)
+{
+ int len = 0;
+ int ret = 0;
+
+ if (pdev->dev.of_node) {
+ const void *rp = of_get_property(pdev->dev.of_node,
+ pname, &len);
+ if (NULL != rp && len == sizeof(u32)*num_values)
+ ret = of_property_read_u32_array(pdev->dev.of_node,
+ pname, values, num_values);
+ else
+ ret = -ENODATA;
+ } else{
+ ret = -EINVAL;
+ }
+ if (ret)
+ pr_err("Error reading property %s, ret:%d", pname, ret);
+ return ret;
+}
+
+/**
* qca1530_xlna_init() - allocates xLNA resources
* @pdev: platform device data
*
- * Function allocates xLNA resources according to DTS configuration. When
- * configured, the following facilities are used:
+ * Function allocates xLNA resources according to DTS configuration.
+ * When configured, the following facilities are used:
* - power regulator (optionally)
* - GPIO pin (optionally)
*
@@ -692,6 +724,7 @@ static int qca1530_xlna_set(int mode)
static int qca1530_xlna_init(struct platform_device *pdev)
{
int ret = 0;
+ u32 tmp[2];
pr_debug("xLNA control: initializing");
@@ -700,6 +733,33 @@ static int qca1530_xlna_init(struct platform_device *pdev)
if (ret)
goto err_0;
+ if (qca1530_data.xlna_reg) {
+ ret = qca1530_read_u32_arr_property(pdev,
+ QCA1530_OF_XLNA_POWER_VOLTAGE, 2, tmp);
+ if (!ret) {
+ ret = regulator_set_voltage(qca1530_data.xlna_reg,
+ tmp[0], tmp[1]);
+ if (ret)
+ pr_warn("Failed to set voltage, ret=%d", ret);
+ else
+ pr_debug("Regulator %p voltage was set (%d)",
+ qca1530_data.xlna_reg, ret);
+ }
+
+ ret = qca1530_read_u32_arr_property(pdev,
+ QCA1530_OF_XLNA_POWER_CURRENT, 2, tmp);
+ if (!ret) {
+ ret = regulator_set_optimum_mode(qca1530_data.xlna_reg,
+ tmp[0]);
+ if (ret < 0)
+ pr_warn("Failed to set optimum mode, ret=%d",
+ ret);
+ else
+ pr_debug("Optimum mode for %p was set (%d)",
+ qca1530_data.xlna_reg, ret);
+ }
+ }
+
ret = qca1530_xlna_init_gpio(pdev);
if (ret)
goto err_1;
@@ -719,7 +779,7 @@ static int qca1530_xlna_init(struct platform_device *pdev)
return ret;
err_2:
- qca1530_deinit_gpio(&qca1530_data.xlna_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.xlna_gpio);
err_1:
qca1530_deinit_regulator(&qca1530_data.xlna_reg);
err_0:
@@ -728,13 +788,14 @@ err_0:
/**
* qca1530_xlna_deinit() - release all xLNA resources
+ * @pdev: platform device data
*
* Function switches off xLNA and releases all allocated resources.
*/
-static void qca1530_xlna_deinit(void)
+static void qca1530_xlna_deinit(struct platform_device *pdev)
{
qca1530_xlna_set(0);
- qca1530_deinit_gpio(&qca1530_data.xlna_gpio);
+ qca1530_deinit_gpio(pdev, &qca1530_data.xlna_gpio);
qca1530_deinit_regulator(&qca1530_data.xlna_reg);
}
@@ -778,18 +839,18 @@ static int qca1530_probe(struct platform_device *pdev)
}
qca1530_data.pdev = pdev;
-
pr_debug("Probe OK");
-
return ret;
err_pwr_init:
- qca1530_xlna_deinit();
+ qca1530_xlna_deinit(pdev);
err_xlna_init:
qca1530_clk_deinit(pdev);
err_clk_init:
- qca1530_reset_deinit();
+ qca1530_reset_deinit(pdev);
err_reset_init:
+
+ pr_debug("Probe ret=%d", ret);
return ret;
}
@@ -805,10 +866,10 @@ err_reset_init:
*/
static int qca1530_remove(struct platform_device *pdev)
{
- qca1530_pwr_deinit();
- qca1530_xlna_deinit();
+ qca1530_pwr_deinit(pdev);
+ qca1530_xlna_deinit(pdev);
qca1530_clk_deinit(pdev);
- qca1530_reset_deinit();
+ qca1530_reset_deinit(pdev);
qca1530_data.pdev = NULL;
return 0;
}
diff --git a/drivers/platform/msm/qpnp-power-on.c b/drivers/platform/msm/qpnp-power-on.c
index dfb2935c2f51..27efde1bbc62 100644
--- a/drivers/platform/msm/qpnp-power-on.c
+++ b/drivers/platform/msm/qpnp-power-on.c
@@ -51,6 +51,8 @@
#define QPNP_PON_KPDPWR_RESIN_S2_CNTL2(base) (base + 0x4B)
#define QPNP_PON_PS_HOLD_RST_CTL(base) (base + 0x5A)
#define QPNP_PON_PS_HOLD_RST_CTL2(base) (base + 0x5B)
+#define QPNP_PON_WD_RST_S2_CTL(base) (base + 0x56)
+#define QPNP_PON_WD_RST_S2_CTL2(base) (base + 0x57)
#define QPNP_PON_TRIGGER_EN(base) (base + 0x80)
#define QPNP_PON_S3_DBC_CTL(base) (base + 0x75)
@@ -74,6 +76,7 @@
#define QPNP_PON_RESIN_BARK_N_SET BIT(4)
#define QPNP_PON_KPDPWR_RESIN_BARK_N_SET BIT(5)
+#define QPNP_PON_WD_EN BIT(7)
#define QPNP_PON_RESET_EN BIT(7)
#define QPNP_PON_POWER_OFF_MASK 0xF
@@ -358,6 +361,32 @@ int qpnp_pon_is_warm_reset(void)
EXPORT_SYMBOL(qpnp_pon_is_warm_reset);
/**
+ * qpnp_pon_wd_config - Disable the wd in a warm reset.
+ * @enable: to enable or disable the PON watch dog
+ *
+ * Returns = 0 for operate successfully, < 0 for errors
+ */
+int qpnp_pon_wd_config(bool enable)
+{
+ struct qpnp_pon *pon = sys_reset_dev;
+ int rc = 0;
+
+ if (!pon)
+ return -EPROBE_DEFER;
+
+ rc = qpnp_pon_masked_write(pon, QPNP_PON_WD_RST_S2_CTL2(pon->base),
+ QPNP_PON_WD_EN, enable ? QPNP_PON_WD_EN : 0);
+ if (rc)
+ dev_err(&pon->spmi->dev,
+ "Unable to write to addr=%x, rc(%d)\n",
+ QPNP_PON_WD_RST_S2_CTL2(pon->base), rc);
+
+ return rc;
+}
+EXPORT_SYMBOL(qpnp_pon_wd_config);
+
+
+/**
* qpnp_pon_trigger_config - Configures (enable/disable) the PON trigger source
* @pon_src: PON source to be configured
* @enable: to enable or disable the PON trigger
@@ -1204,6 +1233,8 @@ static int qpnp_pon_probe(struct spmi_device *spmi)
return rc;
}
+ boot_reason = ffs(pon_sts);
+
index = ffs(pon_sts) - 1;
cold_boot = !qpnp_pon_is_warm_reset();
if (index >= ARRAY_SIZE(qpnp_pon_reason) || index < 0)
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
index ba46146cfe48..ae75c0bff1d0 100644
--- a/drivers/platform/msm/usb_bam.c
+++ b/drivers/platform/msm/usb_bam.c
@@ -105,37 +105,69 @@ static char *bam_enable_strings[MAX_BAMS] = {
[HSIC_BAM] = "hsic",
};
-static enum usb_bam ipa_rm_bams[] = {HSUSB_BAM, HSIC_BAM};
+struct ipa_rm_bam {
+ enum usb_bam bam;
+ char *str;
+ bool initialized;
+};
+
+static struct ipa_rm_bam ipa_rm_bams[] = {
+ {
+ .bam = SSUSB_BAM,
+ .initialized = false
+ },
+ {
+ .bam = HSUSB_BAM,
+ .initialized = false
+ },
+ {
+ .bam = HSIC_BAM,
+ .initialized = false
+ }
+};
+/*
+ * HSUSB_BAM & SSUSB_BAM shouldn't be used simultaneously
+ * since both share the same prod & cons rm resourses
+ */
static enum ipa_client_type ipa_rm_resource_prod[MAX_BAMS] = {
[HSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
[HSIC_BAM] = IPA_RM_RESOURCE_HSIC_PROD,
+ [SSUSB_BAM] = IPA_RM_RESOURCE_USB_PROD,
};
static enum ipa_client_type ipa_rm_resource_cons[MAX_BAMS] = {
[HSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
[HSIC_BAM] = IPA_RM_RESOURCE_HSIC_CONS,
+ [SSUSB_BAM] = IPA_RM_RESOURCE_USB_CONS,
};
static int usb_cons_request_resource(void);
static int usb_cons_release_resource(void);
+static int ss_usb_cons_request_resource(void);
+static int ss_usb_cons_release_resource(void);
static int hsic_cons_request_resource(void);
static int hsic_cons_release_resource(void);
+
static int (*request_resource_cb[MAX_BAMS])(void) = {
[HSUSB_BAM] = usb_cons_request_resource,
[HSIC_BAM] = hsic_cons_request_resource,
+ [SSUSB_BAM] = ss_usb_cons_request_resource,
};
static int (*release_resource_cb[MAX_BAMS])(void) = {
[HSUSB_BAM] = usb_cons_release_resource,
[HSIC_BAM] = hsic_cons_release_resource,
+ [SSUSB_BAM] = ss_usb_cons_release_resource,
};
struct usb_bam_ipa_handshake_info {
enum ipa_rm_event cur_prod_state;
enum ipa_rm_event cur_cons_state;
+ enum usb_bam_mode cur_bam_mode;
+ enum usb_bam bam_type;
bool lpm_wait_handshake;
int connect_complete;
bool lpm_wait_pipes;
@@ -166,7 +198,7 @@ struct usb_bam_ipa_handshake_info {
struct work_struct finish_suspend_work;
};
-struct usb_bam_hsic_host_info {
+struct usb_bam_host_info {
struct device *dev;
bool in_lpm;
};
@@ -178,7 +210,8 @@ static struct usb_bam_peer_handshake_info peer_handshake_info;
static spinlock_t usb_bam_lock; /* Protect ctx and usb_bam_connections */
static struct usb_bam_pipe_connect *usb_bam_connections;
static struct usb_bam_ctx_type ctx;
-static struct usb_bam_hsic_host_info hsic_host_info;
+static struct usb_bam_host_info host_info[MAX_BAMS];
+static struct device *usb_device;
static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user),
void *param, bool trigger_cb_per_pipe);
@@ -192,16 +225,28 @@ void msm_bam_set_hsic_host_dev(struct device *dev)
pr_debug("%s: Getting hsic device %x\n", __func__,
(int)dev);
pm_runtime_get(dev);
- } else if (hsic_host_info.dev) {
+ } else if (host_info[HSIC_BAM].dev) {
pr_debug("%s: Putting hsic device %x\n", __func__,
- (int)hsic_host_info.dev);
+ (int)host_info[HSIC_BAM].dev);
/* Just free previous device*/
info[HSIC_BAM].in_lpm = true;
- pm_runtime_put(hsic_host_info.dev);
+ pm_runtime_put(host_info[HSIC_BAM].dev);
}
- hsic_host_info.dev = dev;
- hsic_host_info.in_lpm = false;
+ host_info[HSIC_BAM].dev = dev;
+ host_info[HSIC_BAM].in_lpm = false;
+}
+
+void msm_bam_set_usb_dev(struct device *dev)
+{
+ pr_debug("%s: Updating usb device for power managment\n", __func__);
+ usb_device = dev;
+}
+
+void msm_bam_set_usb_host_dev(struct device *dev)
+{
+ host_info[HSUSB_BAM].dev = dev;
+ host_info[HSUSB_BAM].in_lpm = false;
}
static int get_bam_type_from_core_name(const char *name)
@@ -541,6 +586,7 @@ static int connect_pipe_ipa(u8 idx,
sps_connection->destination = sps_out_params.ipa_bam_hdl;
sps_connection->src_pipe_index = pipe_connect->src_pipe_index;
sps_connection->dest_pipe_index = sps_out_params.ipa_ep_idx;
+ ipa_params->ipa_cons_ep_idx = sps_out_params.ipa_ep_idx;
*(ipa_params->src_pipe) = sps_connection->src_pipe_index;
pipe_connect->dst_pipe_index = sps_out_params.ipa_ep_idx;
pr_debug("%s: BAM pipe usb[%x]->ipa[%x] connection\n",
@@ -555,6 +601,7 @@ static int connect_pipe_ipa(u8 idx,
sps_connection->source = sps_out_params.ipa_bam_hdl;
sps_connection->destination = usb_handle;
sps_connection->src_pipe_index = sps_out_params.ipa_ep_idx;
+ ipa_params->ipa_prod_ep_idx = sps_out_params.ipa_ep_idx;
sps_connection->dest_pipe_index = pipe_connect->dst_pipe_index;
*(ipa_params->dst_pipe) = sps_connection->dest_pipe_index;
pipe_connect->src_pipe_index = sps_out_params.ipa_ep_idx;
@@ -565,6 +612,9 @@ static int connect_pipe_ipa(u8 idx,
sps_connection->options = 0;
}
+ pipe_connect->data_mem_buf = sps_out_params.data;
+ pipe_connect->desc_mem_buf = sps_out_params.desc;
+
sps_connection->data = sps_out_params.data;
sps_connection->desc = sps_out_params.desc;
sps_connection->event_thresh = 16;
@@ -632,37 +682,131 @@ static int disconnect_pipe(u8 idx)
return 0;
}
-static void usb_bam_resume_core(enum usb_bam cur_bam)
+static bool _usb_bam_resume_core(void)
{
- struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ pr_debug("%s: Resuming usb peripheral/host device", __func__);
- if (cur_bam != HSUSB_BAM)
- return;
- BUG_ON(IS_ERR_OR_NULL(phy));
- pr_debug("%s: resume core", __func__);
- pm_runtime_resume(phy->dev);
+ if (usb_device)
+ pm_runtime_resume(usb_device);
+ else {
+ pr_err("%s: usb device is not initialized\n", __func__);
+ return false;
+ }
+
+ return true;
}
-static void usb_bam_start_lpm(bool disconnect)
+static bool _hsic_host_bam_resume_core(void)
{
- struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ pr_debug("%s: enter\n", __func__);
- BUG_ON(IS_ERR_OR_NULL(phy));
+ /* Exit from "full suspend" in case of hsic host */
+ if (host_info[HSIC_BAM].dev && info[HSIC_BAM].in_lpm) {
+ pr_debug("%s: Getting hsic device %x\n", __func__,
+ (int)host_info[HSIC_BAM].dev);
+ pm_runtime_get(host_info[HSIC_BAM].dev);
+ info[HSIC_BAM].in_lpm = false;
+ return true;
+ }
+ return false;
+}
+
+static bool _hsic_device_bam_resume_core(void)
+{
+ pr_debug("%s: enter\n", __func__);
+
+ /* Not supported yet */
+ return false;
+}
+
+static void _usb_bam_suspend_core(enum usb_bam bam_type, bool disconnect)
+{
+
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+
+ if (!usb_device) {
+ pr_err("%s: usb device is not initialized\n", __func__);
+ return;
+ }
spin_lock(&usb_bam_ipa_handshake_info_lock);
- info[HSUSB_BAM].lpm_wait_handshake = false;
- info[HSUSB_BAM].lpm_wait_pipes = 0;
+ info[bam_type].lpm_wait_handshake = false;
+ info[bam_type].lpm_wait_pipes = 0;
if (disconnect)
- pm_runtime_put_noidle(phy->dev);
+ pm_runtime_put_noidle(usb_device);
- if (info[HSUSB_BAM].pending_lpm) {
- info[HSUSB_BAM].pending_lpm = 0;
+ if (info[bam_type].pending_lpm) {
+ info[bam_type].pending_lpm = 0;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
pr_debug("%s: Going to LPM\n", __func__);
- pm_runtime_suspend(phy->dev);
+ pm_runtime_suspend(usb_device);
} else
spin_unlock(&usb_bam_ipa_handshake_info_lock);
+}
+
+static void _hsic_device_bam_suspend_core(void)
+{
+ /* Not supported yet */
+ pr_debug("%s: enter\n", __func__);
+}
+
+static void _hsic_host_bam_suspend_core(void)
+{
+ pr_debug("%s: enter\n", __func__);
+
+ if (host_info[HSIC_BAM].dev && !info[HSIC_BAM].in_lpm) {
+ pr_debug("%s: Putting hsic host device %x\n", __func__,
+ (int)host_info[HSIC_BAM].dev);
+ pm_runtime_put(host_info[HSIC_BAM].dev);
+ info[HSIC_BAM].in_lpm = true;
+ }
+}
+
+static void usb_bam_suspend_core(enum usb_bam bam_type,
+ enum usb_bam_mode bam_mode,
+ bool disconnect)
+{
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+
+ switch (bam_type) {
+ case HSUSB_BAM:
+ /* TODO: This needs the correct handling for SSUSB_BAM */
+ case SSUSB_BAM:
+ _usb_bam_suspend_core(bam_type, disconnect);
+ break;
+ case HSIC_BAM:
+ if (bam_mode == USB_BAM_DEVICE)
+ _hsic_device_bam_suspend_core();
+ else /* USB_BAM_HOST */
+ _hsic_host_bam_suspend_core();
+ break;
+ default:
+ pr_err("%s: Invalid BAM type %d\n", __func__, bam_type);
+ return;
+ }
+}
+
+static bool usb_bam_resume_core(enum usb_bam bam_type,
+ enum usb_bam_mode bam_mode)
+{
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+ switch (bam_type) {
+ case HSUSB_BAM:
+ /* TODO: This needs the correct handling for SSUSB_BAM */
+ case SSUSB_BAM:
+ return _usb_bam_resume_core();
+ break;
+ case HSIC_BAM:
+ if (bam_mode == USB_BAM_DEVICE)
+ return _hsic_device_bam_resume_core();
+ else /* USB_BAM_HOST */
+ return _hsic_host_bam_resume_core();
+ break;
+ default:
+ pr_err("%s: Invalid BAM type %d\n", __func__, bam_type);
+ return false;
+ }
}
int usb_bam_connect(int idx, u32 *bam_pipe_idx)
@@ -670,6 +814,8 @@ int usb_bam_connect(int idx, u32 *bam_pipe_idx)
int ret;
struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];
struct msm_usb_bam_platform_data *pdata;
+ enum usb_bam cur_bam;
+ enum usb_bam_mode cur_mode;
if (!ctx.usb_bam_pdev) {
pr_err("%s: usb_bam device not found\n", __func__);
@@ -703,6 +849,12 @@ int usb_bam_connect(int idx, u32 *bam_pipe_idx)
}
spin_unlock(&usb_bam_lock);
+ cur_bam = pipe_connect->bam_type;
+ cur_mode = pipe_connect->bam_mode;
+
+ /* Set the BAM mode (host/device) according to connected pipe */
+ info[cur_bam].cur_bam_mode = pipe_connect->bam_mode;
+
ret = connect_pipe(idx, bam_pipe_idx);
if (ret) {
pr_err("%s: pipe connection[%d] failure\n", __func__, idx);
@@ -761,13 +913,13 @@ static void stop_cons_transfers(struct usb_bam_pipe_connect *pipe_connect)
}
}
-static int ipa_suspend_pipes(u32 idx)
+static int ipa_suspend_pipes(enum usb_bam cur_bam, u32 idx)
{
struct usb_bam_pipe_connect *dst_pipe, *src_pipe;
int ret1, ret2;
- dst_pipe = &usb_bam_connections[info[HSUSB_BAM].suspend_dst_idx[idx]];
- src_pipe = &usb_bam_connections[info[HSUSB_BAM].suspend_src_idx[idx]];
+ dst_pipe = &usb_bam_connections[info[cur_bam].suspend_dst_idx[idx]];
+ src_pipe = &usb_bam_connections[info[cur_bam].suspend_src_idx[idx]];
if (dst_pipe->ipa_clnt_hdl == -1 ||
src_pipe->ipa_clnt_hdl == -1) {
@@ -785,13 +937,13 @@ static int ipa_suspend_pipes(u32 idx)
return ret1 | ret2;
}
-static int ipa_resume_pipes(u32 idx)
+static int ipa_resume_pipes(enum usb_bam cur_bam, u32 idx)
{
struct usb_bam_pipe_connect *dst_pipe, *src_pipe;
int ret1, ret2;
- src_pipe = &usb_bam_connections[info[HSUSB_BAM].resume_src_idx[idx]];
- dst_pipe = &usb_bam_connections[info[HSUSB_BAM].resume_dst_idx[idx]];
+ src_pipe = &usb_bam_connections[info[cur_bam].resume_src_idx[idx]];
+ dst_pipe = &usb_bam_connections[info[cur_bam].resume_dst_idx[idx]];
if (dst_pipe->ipa_clnt_hdl == -1 ||
src_pipe->ipa_clnt_hdl == -1) {
@@ -821,7 +973,7 @@ static void resume_suspended_pipes(enum usb_bam cur_bam)
pipe_connect = &usb_bam_connections[dst_idx];
if (pipe_connect->cons_stopped) {
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- ipa_resume_pipes(idx);
+ ipa_resume_pipes(cur_bam, idx);
spin_lock(&usb_bam_ipa_handshake_info_lock);
pr_debug("%s: Starting CONS on %d", __func__, dst_idx);
start_cons_transfers(pipe_connect);
@@ -844,13 +996,12 @@ static inline int all_pipes_suspended(enum usb_bam cur_bam)
ctx.pipes_enabled_per_bam[cur_bam]);
}
-static void usb_bam_finish_suspend(void)
+static void usb_bam_finish_suspend(enum usb_bam cur_bam)
{
int ret;
u32 cons_empty, idx, dst_idx;
struct sps_pipe *cons_pipe;
struct usb_bam_pipe_connect *pipe_connect;
- enum usb_bam cur_bam = HSUSB_BAM;
mutex_lock(&info[cur_bam].suspend_resume_mutex);
@@ -909,7 +1060,7 @@ static void usb_bam_finish_suspend(void)
IPA_RM_RESOURCE_RELEASED,
ipa_rm_resource_cons[cur_bam]);
}
- ipa_suspend_pipes(idx);
+ ipa_suspend_pipes(cur_bam, idx);
spin_lock(&usb_bam_ipa_handshake_info_lock);
info[cur_bam].resume_src_idx[idx] =
info[cur_bam].suspend_src_idx[idx];
@@ -927,7 +1078,9 @@ static void usb_bam_finish_suspend(void)
info[cur_bam].pipes_resumed = 0;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
pr_debug("%s: Starting LPM on Bus Suspend\n", __func__);
- usb_bam_start_lpm(0);
+
+ usb_bam_suspend_core(cur_bam, USB_BAM_DEVICE, 0);
+
mutex_unlock(&info[cur_bam].suspend_resume_mutex);
return;
@@ -949,7 +1102,16 @@ no_lpm:
void usb_bam_finish_suspend_(struct work_struct *w)
{
- usb_bam_finish_suspend();
+ enum usb_bam cur_bam;
+ struct usb_bam_ipa_handshake_info *info_ptr;
+
+ info_ptr = container_of(w, struct usb_bam_ipa_handshake_info,
+ finish_suspend_work);
+ cur_bam = info_ptr->cur_bam_mode;
+
+ pr_debug("%s: Finishing suspend sequence(BAM=%s)\n", __func__,
+ bam_enable_strings[cur_bam]);
+ usb_bam_finish_suspend(cur_bam);
}
static void usb_prod_notify_cb(void *user_data, enum ipa_rm_event event,
@@ -977,28 +1139,24 @@ static void usb_prod_notify_cb(void *user_data, enum ipa_rm_event event,
}
/**
- * usb_bam_resume_hsic_host: vote for hsic host core resume.
- * In addition also resume all hsic pipes that are connected to
- * the ipa peer bam.
+ * usb_bam_resume_host: vote for hsic host core resume. In
+ * addition also resume all hsic pipes that are connected to the
+ * ipa peer bam.
*
* NOTE: This function should be called in a context that hold
* usb_bam_lock.
*/
-static void usb_bam_resume_hsic_host(void)
+static void usb_bam_resume_host(enum usb_bam bam_type)
{
int i;
struct usb_bam_pipe_connect *pipe_iter;
- /* Exit from "full suspend" in case of hsic host */
- if (hsic_host_info.dev && info[HSIC_BAM].in_lpm) {
- pr_debug("%s: Getting hsic device %x\n", __func__,
- (int)hsic_host_info.dev);
- pm_runtime_get(hsic_host_info.dev);
- info[HSIC_BAM].in_lpm = false;
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+ if (usb_bam_resume_core(bam_type, USB_BAM_HOST))
for (i = 0; i < ctx.max_connections; i++) {
pipe_iter = &usb_bam_connections[i];
- if (pipe_iter->bam_type == HSIC_BAM &&
+ if (pipe_iter->bam_type == bam_type &&
pipe_iter->enabled &&
pipe_iter->suspended) {
spin_unlock(&usb_bam_lock);
@@ -1007,7 +1165,6 @@ static void usb_bam_resume_hsic_host(void)
spin_lock(&usb_bam_lock);
}
}
- }
}
static int cons_request_resource(enum usb_bam cur_bam)
@@ -1022,11 +1179,11 @@ static int cons_request_resource(enum usb_bam cur_bam)
spin_lock(&usb_bam_lock);
- switch (cur_bam) {
- case HSUSB_BAM:
- if (ctx.pipes_enabled_per_bam[HSUSB_BAM] &&
+ switch (info[cur_bam].cur_bam_mode) {
+ case USB_BAM_DEVICE:
+ if (ctx.pipes_enabled_per_bam[cur_bam] &&
info[cur_bam].connect_complete) {
- if (!all_pipes_suspended(HSUSB_BAM) &&
+ if (!all_pipes_suspended(cur_bam) &&
!info[cur_bam].bus_suspend) {
pr_debug("%s: ACK on cons_request", __func__);
ret = 0;
@@ -1040,7 +1197,7 @@ static int cons_request_resource(enum usb_bam cur_bam)
}
break;
- case HSIC_BAM:
+ case USB_BAM_HOST:
/*
* Vote for hsic resume, however the core
* resume may not be completed yet or on the other hand
@@ -1048,7 +1205,7 @@ static int cons_request_resource(enum usb_bam cur_bam)
* by other driver, in this case we will just renew our
* vote here.
*/
- usb_bam_resume_hsic_host();
+ usb_bam_resume_host(cur_bam);
/*
* Return sucess if there are pipes connected
@@ -1057,12 +1214,12 @@ static int cons_request_resource(enum usb_bam cur_bam)
* finish (see msm_bam_hsic_notify_on_resume)
*/
if (ctx.pipes_enabled_per_bam[cur_bam] &&
- !hsic_host_info.in_lpm) {
+ !host_info[cur_bam].in_lpm) {
ret = 0;
}
break;
- case SSUSB_BAM:
+
default:
break;
}
@@ -1076,6 +1233,12 @@ static int cons_request_resource(enum usb_bam cur_bam)
return ret;
}
+static int ss_usb_cons_request_resource(void)
+{
+ return cons_request_resource(SSUSB_BAM);
+}
+
+
static int usb_cons_request_resource(void)
{
return cons_request_resource(HSUSB_BAM);
@@ -1101,7 +1264,7 @@ static int cons_release_resource(enum usb_bam cur_bam)
}
spin_unlock(&usb_bam_lock);
- if (cur_bam == HSUSB_BAM) {
+ if (info[cur_bam].cur_bam_mode == USB_BAM_DEVICE) {
spin_lock(&usb_bam_ipa_handshake_info_lock);
if (info[cur_bam].bus_suspend) {
queue_work(ctx.usb_bam_wq,
@@ -1111,18 +1274,13 @@ static int cons_release_resource(enum usb_bam cur_bam)
pr_debug("%s: EINPROGRESS cons_release", __func__);
return -EINPROGRESS;
- } else if (cur_bam == HSIC_BAM) {
-
+ } else if (info[cur_bam].cur_bam_mode == USB_BAM_HOST) {
/*
* Allow to go to lpm for now. Actual state will be checked
- * in msm_bam_hsic_lpm_ok() just before going to lpm.
+ * in msm_bam_hsic_lpm_ok() / msm_bam_lpm_ok() just before
+ * going to lpm.
*/
- if (hsic_host_info.dev && !info[HSIC_BAM].in_lpm) {
- pr_debug("%s: Putting hsic device %x\n", __func__,
- (int)hsic_host_info.dev);
- pm_runtime_put(hsic_host_info.dev);
- info[HSIC_BAM].in_lpm = true;
- }
+ usb_bam_suspend_core(cur_bam, info[cur_bam].cur_bam_mode, 1);
}
return 0;
@@ -1138,6 +1296,11 @@ static int usb_cons_release_resource(void)
return cons_release_resource(HSUSB_BAM);
}
+static int ss_usb_cons_release_resource(void)
+{
+ return cons_release_resource(SSUSB_BAM);
+}
+
static void usb_bam_ipa_create_resources(void)
{
struct ipa_rm_create_params usb_prod_create_params;
@@ -1146,15 +1309,20 @@ static void usb_bam_ipa_create_resources(void)
int ret, i;
for (i = 0; i < ARRAY_SIZE(ipa_rm_bams); i++) {
+ /* Only initialized bams should be regitsterd with RM */
+ if (!ipa_rm_bams[i].initialized)
+ continue;
+
/* Create USB/HSIC_PROD entity */
- cur_bam = ipa_rm_bams[i];
+ cur_bam = ipa_rm_bams[i].bam;
memset(&usb_prod_create_params, 0,
sizeof(usb_prod_create_params));
usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam];
usb_prod_create_params.reg_params.notify_cb =
usb_prod_notify_cb;
- usb_prod_create_params.reg_params.user_data = &ipa_rm_bams[i];
+ usb_prod_create_params.reg_params.user_data =
+ &ipa_rm_bams[i].bam;
ret = ipa_rm_create_resource(&usb_prod_create_params);
if (ret) {
pr_err("%s: Failed to create USB_PROD resource\n",
@@ -1214,7 +1382,7 @@ void notify_usb_connected(enum usb_bam cur_bam)
pr_debug("%s: enter\n", __func__);
spin_lock(&usb_bam_ipa_handshake_info_lock);
- if (cur_bam == HSUSB_BAM)
+ if (info[cur_bam].cur_bam_mode == USB_BAM_DEVICE)
info[cur_bam].connect_complete = 1;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
@@ -1289,8 +1457,11 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params)
{
struct usb_bam_pipe_connect *pipe_connect;
enum usb_bam cur_bam;
+ enum usb_bam_mode bam_mode;
u8 src_idx, dst_idx;
+ pr_debug("%s: enter\n", __func__);
+
if (!ipa_params) {
pr_err("%s: Invalid ipa params\n", __func__);
return;
@@ -1306,7 +1477,8 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params)
pipe_connect = &usb_bam_connections[src_idx];
cur_bam = pipe_connect->bam_type;
- if (cur_bam != HSUSB_BAM)
+ bam_mode = pipe_connect->bam_mode;
+ if (bam_mode != USB_BAM_DEVICE)
return;
pr_debug("%s: Starting suspend sequence(BAM=%s)\n", __func__,
@@ -1336,11 +1508,17 @@ void usb_bam_suspend(struct usb_bam_connect_ipa_params *ipa_params)
static void usb_bam_start_suspend(struct work_struct *w)
{
struct usb_bam_pipe_connect *pipe_connect;
- enum usb_bam cur_bam = HSUSB_BAM;
+ struct usb_bam_ipa_handshake_info *info_ptr;
+ enum usb_bam cur_bam;
u8 src_idx, dst_idx;
int pipes_to_suspend;
- pr_debug("%s: enter", __func__);
+ info_ptr = container_of(w, struct usb_bam_ipa_handshake_info,
+ suspend_work);
+ cur_bam = info_ptr->bam_type;
+ pr_debug("%s: Starting suspend sequence(BAM=%s)\n", __func__,
+ bam_enable_strings[cur_bam]);
+
mutex_lock(&info[cur_bam].suspend_resume_mutex);
spin_lock(&usb_bam_ipa_handshake_info_lock);
@@ -1384,26 +1562,29 @@ static void usb_bam_start_suspend(struct work_struct *w)
if (info[cur_bam].pipes_to_suspend * 2 ==
ctx.pipes_enabled_per_bam[cur_bam]) {
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- wait_for_prod_release(HSUSB_BAM);
+ wait_for_prod_release(cur_bam);
} else
spin_unlock(&usb_bam_ipa_handshake_info_lock);
mutex_unlock(&info[cur_bam].suspend_resume_mutex);
if (info[cur_bam].cur_cons_state == IPA_RM_RESOURCE_RELEASED)
- usb_bam_finish_suspend();
+ usb_bam_finish_suspend(cur_bam);
else
pr_debug("Consumer not released yet\n");
}
static void usb_bam_finish_resume(struct work_struct *w)
{
- struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB2);
- enum usb_bam cur_bam = HSUSB_BAM;
+ /* TODO: Change this when HSIC device support is introduced */
+ enum usb_bam cur_bam;
+ struct usb_bam_ipa_handshake_info *info_ptr;
struct usb_bam_pipe_connect *pipe_connect;
u32 idx, dst_idx, suspended;
- BUG_ON(IS_ERR_OR_NULL(phy));
- pr_debug("%s: enter", __func__);
+ info_ptr = container_of(w, struct usb_bam_ipa_handshake_info,
+ resume_work);
+ cur_bam = info_ptr->bam_type;
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[cur_bam]);
mutex_lock(&info[cur_bam].suspend_resume_mutex);
/* Suspend happened in the meantime */
@@ -1427,7 +1608,7 @@ static void usb_bam_finish_resume(struct work_struct *w)
pipe_connect = &usb_bam_connections[dst_idx];
if (pipe_connect->cons_stopped) {
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- ipa_resume_pipes(idx);
+ ipa_resume_pipes(cur_bam, idx);
spin_lock(&usb_bam_ipa_handshake_info_lock);
pr_debug("%s: Starting CONS on %d", __func__, dst_idx);
start_cons_transfers(pipe_connect);
@@ -1437,7 +1618,7 @@ static void usb_bam_finish_resume(struct work_struct *w)
if (info[cur_bam].cur_cons_state == IPA_RM_RESOURCE_GRANTED) {
pr_debug("%s: Notify CONS_GRANTED\n", __func__);
ipa_rm_notify_completion(IPA_RM_RESOURCE_GRANTED,
- ipa_rm_resource_cons[HSUSB_BAM]);
+ ipa_rm_resource_cons[cur_bam]);
}
spin_unlock(&usb_bam_ipa_handshake_info_lock);
@@ -1495,7 +1676,9 @@ void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params)
pipe_connect = &usb_bam_connections[src_idx];
cur_bam = pipe_connect->bam_type;
- if (cur_bam != HSUSB_BAM)
+ pr_debug("%s: bam=%s mode =%d\n", __func__,
+ bam_enable_strings[cur_bam], pipe_connect->bam_mode);
+ if (pipe_connect->bam_mode != USB_BAM_DEVICE)
return;
info[cur_bam].in_lpm = false;
@@ -1505,29 +1688,38 @@ void usb_bam_resume(struct usb_bam_connect_ipa_params *ipa_params)
queue_work(ctx.usb_bam_wq, &info[cur_bam].resume_work);
}
-void msm_bam_wait_for_hsic_prod_granted(void)
+void _msm_bam_wait_for_host_prod_granted(enum usb_bam bam_type)
{
spin_lock(&usb_bam_lock);
- ctx.is_bam_inactivity[HSIC_BAM] = false;
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+ ctx.is_bam_inactivity[bam_type] = false;
/* Get back to resume state including wakeup ipa */
- usb_bam_resume_hsic_host();
+ usb_bam_resume_core(bam_type, USB_BAM_HOST);
/* Ensure getting the producer resource */
- wait_for_prod_granted(HSIC_BAM);
+ wait_for_prod_granted(bam_type);
spin_unlock(&usb_bam_lock);
+
}
-void msm_bam_hsic_notify_on_resume(void)
+void msm_bam_wait_for_hsic_host_prod_granted(void)
+{
+ pr_debug("%s: start\n", __func__);
+ _msm_bam_wait_for_host_prod_granted(HSIC_BAM);
+}
+
+void _msm_bam_host_notify_on_resume(enum usb_bam bam_type)
{
spin_lock(&usb_bam_lock);
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
- hsic_host_info.in_lpm = false;
+ host_info[bam_type].in_lpm = false;
/* HSIC resume completed. Notify CONS grant if CONS was requested */
- notify_usb_connected(HSIC_BAM);
+ notify_usb_connected(bam_type);
/*
* This function is called to notify the usb bam driver
@@ -1535,18 +1727,20 @@ void msm_bam_hsic_notify_on_resume(void)
* and clocked on. Therefore we can now set the inactivity
* timer to the hsic bam hw.
*/
- if (ctx.inactivity_timer_ms[HSIC_BAM])
- usb_bam_set_inactivity_timer(HSIC_BAM);
+ if (ctx.inactivity_timer_ms[bam_type])
+ usb_bam_set_inactivity_timer(bam_type);
spin_unlock(&usb_bam_lock);
}
-bool msm_bam_hsic_lpm_ok(void)
+bool msm_bam_host_lpm_ok(enum usb_bam bam_type)
{
int i;
struct usb_bam_pipe_connect *pipe_iter;
- if (hsic_host_info.dev) {
+ pr_debug("%s: enter bam=%s\n", __func__, bam_enable_strings[bam_type]);
+
+ if (host_info[bam_type].dev) {
pr_debug("%s: Starting hsic full suspend sequence\n",
__func__);
@@ -1560,20 +1754,20 @@ bool msm_bam_hsic_lpm_ok(void)
*/
spin_lock(&usb_bam_lock);
- if (info[HSIC_BAM].cur_cons_state ==
+ if (info[bam_type].cur_cons_state ==
IPA_RM_RESOURCE_RELEASED &&
- info[HSIC_BAM].cur_prod_state ==
+ info[bam_type].cur_prod_state ==
IPA_RM_RESOURCE_RELEASED &&
- ctx.is_bam_inactivity[HSIC_BAM] && info[HSIC_BAM].in_lpm) {
+ ctx.is_bam_inactivity[bam_type] && info[bam_type].in_lpm) {
/* HSIC host will go now to lpm */
pr_debug("%s: vote for suspend hsic %x\n",
- __func__, (int)hsic_host_info.dev);
+ __func__, (int)host_info[bam_type].dev);
for (i = 0; i < ctx.max_connections; i++) {
pipe_iter =
&usb_bam_connections[i];
- if (pipe_iter->bam_type == HSIC_BAM &&
+ if (pipe_iter->bam_type == bam_type &&
pipe_iter->enabled &&
!pipe_iter->suspended) {
spin_unlock(&usb_bam_lock);
@@ -1584,23 +1778,23 @@ bool msm_bam_hsic_lpm_ok(void)
}
}
- hsic_host_info.in_lpm = true;
+ host_info[bam_type].in_lpm = true;
spin_unlock(&usb_bam_lock);
return true;
}
/* We don't allow lpm, therefore renew our vote here */
- if (info[HSIC_BAM].in_lpm) {
+ if (info[bam_type].in_lpm) {
pr_debug("%s: Not allow lpm while ref count=0\n",
__func__);
pr_debug("%s: inactivity=%d, c_s=%d p_s=%d lpm=%d\n",
- __func__, ctx.is_bam_inactivity[HSIC_BAM],
- info[HSIC_BAM].cur_cons_state,
- info[HSIC_BAM].cur_prod_state,
- info[HSIC_BAM].in_lpm);
- pm_runtime_get(hsic_host_info.dev);
- info[HSIC_BAM].in_lpm = false;
+ __func__, ctx.is_bam_inactivity[bam_type],
+ info[bam_type].cur_cons_state,
+ info[bam_type].cur_prod_state,
+ info[bam_type].in_lpm);
+ pm_runtime_get(host_info[bam_type].dev);
+ info[bam_type].in_lpm = false;
spin_unlock(&usb_bam_lock);
} else
spin_unlock(&usb_bam_lock);
@@ -1611,15 +1805,23 @@ bool msm_bam_hsic_lpm_ok(void)
return true;
}
+void msm_bam_hsic_host_notify_on_resume(void)
+{
+ _msm_bam_host_notify_on_resume(HSIC_BAM);
+}
+
int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
{
u8 idx;
enum usb_bam cur_bam;
+ enum usb_bam_mode cur_mode;
struct usb_bam_pipe_connect *pipe_connect;
int ret;
struct msm_usb_bam_platform_data *pdata =
ctx.usb_bam_pdev->dev.platform_data;
+ pr_debug("%s: start\n", __func__);
+
if (!ipa_params) {
pr_err("%s: Invalid ipa params\n",
__func__);
@@ -1638,6 +1840,10 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
}
pipe_connect = &usb_bam_connections[idx];
cur_bam = pipe_connect->bam_type;
+ cur_mode = pipe_connect->bam_mode;
+
+ /* Set the BAM mode (host/device) according to connected pipe */
+ info[cur_bam].cur_bam_mode = pipe_connect->bam_mode;
if (pipe_connect->enabled) {
pr_err("%s: connection %d was already established\n",
@@ -1647,11 +1853,11 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
pr_debug("%s: enter", __func__);
- if (cur_bam == HSUSB_BAM) {
- mutex_lock(&info[HSUSB_BAM].suspend_resume_mutex);
+ if (cur_mode == USB_BAM_DEVICE) {
+ mutex_lock(&info[cur_bam].suspend_resume_mutex);
spin_lock(&usb_bam_lock);
- if (ctx.pipes_enabled_per_bam[HSUSB_BAM] == 0) {
+ if (ctx.pipes_enabled_per_bam[cur_bam] == 0) {
spin_unlock(&usb_bam_lock);
spin_lock(&usb_bam_ipa_handshake_info_lock);
info[cur_bam].lpm_wait_handshake = true;
@@ -1666,7 +1872,7 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
pipe_connect->cons_stopped = 0;
pipe_connect->prod_stopped = 0;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
- usb_bam_resume_core(cur_bam);
+ usb_bam_resume_core(cur_bam, USB_BAM_DEVICE);
} else
spin_unlock(&usb_bam_lock);
}
@@ -1677,21 +1883,17 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
(ctx.pipes_enabled_per_bam[cur_bam] == 0)) {
spin_unlock(&usb_bam_lock);
- if (cur_bam == HSUSB_BAM)
+ if (cur_bam != HSIC_BAM)
msm_hw_bam_disable(1);
sps_device_reset(ctx.h_bam[cur_bam]);
- if (cur_bam == HSUSB_BAM)
+ if (cur_bam != HSIC_BAM)
msm_hw_bam_disable(0);
- /* On re-connect assume out from lpm for HSIC BAM */
- if (cur_bam == HSIC_BAM && hsic_host_info.dev &&
- info[HSIC_BAM].in_lpm) {
- pr_err("%s: Getting hsic device %x\n",
- __func__, (int)hsic_host_info.dev);
- pm_runtime_get(hsic_host_info.dev);
- }
+ /* On re-connect assume out from lpm for HOST BAM */
+ if (cur_mode == USB_BAM_HOST)
+ usb_bam_resume_core(cur_bam, cur_mode);
/* On re-connect assume out from lpm for all BAMs */
info[cur_bam].in_lpm = false;
@@ -1707,8 +1909,8 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
ret = connect_pipe_ipa(idx, ipa_params);
if (ret) {
pr_err("%s: pipe connection failure\n", __func__);
- if (cur_bam == HSUSB_BAM)
- mutex_unlock(&info[HSUSB_BAM].suspend_resume_mutex);
+ if (cur_mode == USB_BAM_DEVICE)
+ mutex_unlock(&info[cur_bam].suspend_resume_mutex);
return ret;
}
pr_debug("%s: pipe connection success\n", __func__);
@@ -1730,8 +1932,8 @@ int usb_bam_connect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
notify_usb_connected(cur_bam);
spin_unlock(&usb_bam_lock);
- if (cur_bam == HSUSB_BAM)
- mutex_unlock(&info[HSUSB_BAM].suspend_resume_mutex);
+ if (cur_mode == USB_BAM_DEVICE)
+ mutex_unlock(&info[cur_bam].suspend_resume_mutex);
pr_debug("%s: done", __func__);
@@ -1764,8 +1966,7 @@ int usb_bam_client_ready(bool ready)
pr_debug("%s: enters pending_work\n",
__func__);
}
- pr_debug("%s: success\n",
- __func__);
+ pr_debug("%s: success\n", __func__);
return 0;
}
@@ -1792,9 +1993,10 @@ static void usb_bam_work(struct work_struct *w)
* wakeup hsic host class driver (done by the callback below)
*/
if (pipe_connect->peer_bam == IPA_P_BAM &&
- pipe_connect->bam_type == HSIC_BAM &&
- info[HSIC_BAM].cur_prod_state != IPA_RM_RESOURCE_GRANTED) {
- wait_for_prod_granted(HSIC_BAM);
+ pipe_connect->bam_mode == USB_BAM_HOST &&
+ info[pipe_connect->bam_type].cur_prod_state
+ != IPA_RM_RESOURCE_GRANTED) {
+ wait_for_prod_granted(pipe_connect->bam_type);
}
/*
@@ -1809,8 +2011,8 @@ static void usb_bam_work(struct work_struct *w)
* resources against the ipa resource manager.
*/
spin_lock(&usb_bam_lock);
- if (pipe_connect->bam_type == HSIC_BAM)
- usb_bam_resume_hsic_host();
+ if (pipe_connect->bam_mode == USB_BAM_HOST)
+ usb_bam_resume_host(pipe_connect->bam_type);
spin_unlock(&usb_bam_lock);
/* Notify about wakeup / activity of the bam */
@@ -1826,7 +2028,7 @@ static void usb_bam_work(struct work_struct *w)
usb_bam_set_inactivity_timer(pipe_connect->bam_type);
spin_unlock(&usb_bam_lock);
- if (pipe_connect->bam_type == HSUSB_BAM) {
+ if (pipe_connect->bam_mode == USB_BAM_DEVICE) {
/* A2 wakeup not from LPM (CONS was up) */
wait_for_prod_granted(pipe_connect->bam_type);
if (pipe_connect->start) {
@@ -1888,13 +2090,12 @@ static void usb_bam_work(struct work_struct *w)
* If consumer is up, we will wait to the release consumer
* notification.
*/
- if (hsic_host_info.dev &&
- info[HSIC_BAM].cur_cons_state ==
- IPA_RM_RESOURCE_RELEASED && !info[HSIC_BAM].in_lpm) {
- pr_debug("%s: Putting hsic device %x\n", __func__,
- (int)hsic_host_info.dev);
- pm_runtime_put(hsic_host_info.dev);
- info[HSIC_BAM].in_lpm = true;
+ if (host_info[pipe_connect->bam_type].dev &&
+ info[pipe_connect->bam_type].cur_cons_state ==
+ IPA_RM_RESOURCE_RELEASED &&
+ !info[pipe_connect->bam_type].in_lpm) {
+ usb_bam_suspend_core(pipe_connect->bam_type,
+ pipe_connect->bam_mode, 1);
}
break;
@@ -2069,8 +2270,10 @@ static int __usb_bam_register_wake_cb(int idx, int (*callback)(void *user),
int usb_bam_register_wake_cb(u8 idx, int (*callback)(void *user),
void *param)
{
- info[HSUSB_BAM].wake_cb = callback;
- info[HSUSB_BAM].wake_param = param;
+ struct usb_bam_pipe_connect *pipe_connect = &usb_bam_connections[idx];
+
+ info[pipe_connect->bam_type].wake_cb = callback;
+ info[pipe_connect->bam_type].wake_param = param;
return __usb_bam_register_wake_cb(idx, callback, param, true);
}
@@ -2156,6 +2359,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
struct usb_bam_pipe_connect *pipe_connect;
struct sps_connect *sps_connection;
enum usb_bam cur_bam;
+ enum usb_bam_mode bam_mode;
if (!ipa_params->prod_clnt_hdl && !ipa_params->cons_clnt_hdl) {
pr_err("%s: Both of the handles is missing\n", __func__);
@@ -2170,6 +2374,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
idx = ipa_params->src_idx;
pipe_connect = &usb_bam_connections[idx];
cur_bam = pipe_connect->bam_type;
+ bam_mode = pipe_connect->bam_mode;
mutex_lock(&info[cur_bam].suspend_resume_mutex);
/* Delay USB core to go into lpm before we finish our handshake */
@@ -2183,7 +2388,7 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
/* Do the release handshake with the A2 via RM */
spin_lock(&usb_bam_ipa_handshake_info_lock);
- if (cur_bam == HSUSB_BAM) {
+ if (bam_mode == USB_BAM_DEVICE) {
info[cur_bam].connect_complete = 0;
info[cur_bam].lpm_wait_pipes = 1;
info[cur_bam].disconnected = 1;
@@ -2193,7 +2398,8 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
/* Start release handshake on the last producer pipe */
if (info[cur_bam].prod_pipes_enabled_per_bam == 1)
wait_for_prod_release(cur_bam);
- usb_bam_resume_core(cur_bam);
+ if (pipe_connect->bam_mode == USB_BAM_DEVICE)
+ usb_bam_resume_core(cur_bam, pipe_connect->bam_mode);
/* close USB -> IPA pipe */
ret = ipa_disconnect(ipa_params->prod_clnt_hdl);
if (ret) {
@@ -2259,10 +2465,15 @@ int usb_bam_disconnect_ipa(struct usb_bam_connect_ipa_params *ipa_params)
IPA_RM_RESOURCE_RELEASED,
ipa_rm_resource_cons[cur_bam]);
}
- if (cur_bam == HSUSB_BAM) {
+ /* TODO: Make also for SSUSB_BAM, when correct suspend
+ * in place
+ */
+ if (cur_bam == HSUSB_BAM &&
+ pipe_connect->bam_mode == USB_BAM_DEVICE) {
pr_debug("%s Ended disconnect sequence\n",
__func__);
- usb_bam_start_lpm(1);
+ usb_bam_suspend_core(cur_bam,
+ USB_BAM_DEVICE, 1);
}
}
}
@@ -2497,6 +2708,18 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata(
}
usb_bam_connections[i].bam_type = bam;
+ rc = of_property_read_u32(node, "qcom,bam-mode",
+ &usb_bam_connections[i].bam_mode);
+ if (rc) {
+ pr_debug("%s: bam mode is missing in device tree\n",
+ __func__);
+ /*
+ * In cases where bam_mode is not set, the default
+ * will be set to device
+ */
+ usb_bam_connections[i].bam_mode = USB_BAM_DEVICE;
+ }
+
rc = of_property_read_u32(node, "qcom,peer-bam",
&usb_bam_connections[i].peer_bam);
if (rc) {
@@ -2563,9 +2786,9 @@ err:
return NULL;
}
-static int usb_bam_init(int bam_idx)
+static int usb_bam_init(int bam_type)
{
- int ret, irq;
+ int ret, irq, i;
void *usb_virt_addr;
struct msm_usb_bam_platform_data *pdata =
ctx.usb_bam_pdev->dev.platform_data;
@@ -2573,16 +2796,16 @@ static int usb_bam_init(int bam_idx)
struct sps_bam_props *props = &ctx.usb_bam_sps.usb_props;
pr_debug("%s: usb_bam_init - %s\n", __func__,
- bam_enable_strings[bam_idx]);
+ bam_enable_strings[bam_type]);
res = platform_get_resource_byname(ctx.usb_bam_pdev, IORESOURCE_MEM,
- bam_enable_strings[bam_idx]);
+ bam_enable_strings[bam_type]);
if (!res) {
dev_dbg(&ctx.usb_bam_pdev->dev, "bam not initialized\n");
return 0;
}
irq = platform_get_irq_byname(ctx.usb_bam_pdev,
- bam_enable_strings[bam_idx]);
+ bam_enable_strings[bam_type]);
if (irq < 0) {
dev_err(&ctx.usb_bam_pdev->dev, "Unable to get IRQ resource\n");
return irq;
@@ -2596,9 +2819,9 @@ static int usb_bam_init(int bam_idx)
}
/* Check if USB3 pipe memory needs to be enabled */
- if (bam_idx == SSUSB_BAM && bam_use_private_mem(bam_idx)) {
+ if (bam_type == SSUSB_BAM && bam_use_private_mem(bam_type)) {
pr_debug("%s: Enabling USB private memory for: %s\n", __func__,
- bam_enable_strings[bam_idx]);
+ bam_enable_strings[bam_type]);
ram_resource = platform_get_resource_byname(ctx.usb_bam_pdev,
IORESOURCE_MEM, "qscratch_ram1_reg");
@@ -2626,26 +2849,33 @@ static int usb_bam_init(int bam_idx)
props->event_threshold = pdata->override_threshold;
props->num_pipes = pdata->usb_bam_num_pipes;
props->callback = usb_bam_sps_events;
- props->user = bam_enable_strings[bam_idx];
+ props->user = bam_enable_strings[bam_type];
props->options = SPS_BAM_OPT_IRQ_WAKEUP;
/*
* HSUSB and HSIC Cores don't support RESET ACK signal to BAMs
* Hence, let BAM to ignore acknowledge from USB while resetting PIPE
*/
- if (pdata->ignore_core_reset_ack && bam_idx != SSUSB_BAM)
+ if (pdata->ignore_core_reset_ack && bam_type != SSUSB_BAM)
props->options = SPS_BAM_NO_EXT_P_RST;
if (pdata->disable_clk_gating)
props->options |= SPS_BAM_NO_LOCAL_CLK_GATING;
- ret = sps_register_bam_device(props, &(ctx.h_bam[bam_idx]));
+ ret = sps_register_bam_device(props, &(ctx.h_bam[bam_type]));
if (ret < 0) {
pr_err("%s: register bam error %d\n", __func__, ret);
ret = -EFAULT;
goto free_qscratch_reg;
}
+ /* Mark this bam as initilaized */
+ for (i = 0; i < ARRAY_SIZE(ipa_rm_bams); i++)
+ if (ipa_rm_bams[i].bam == bam_type) {
+ ipa_rm_bams[i].initialized = true;
+ break;
+ }
+
return 0;
free_qscratch_reg:
@@ -2829,6 +3059,7 @@ static int usb_bam_probe(struct platform_device *pdev)
info[i].pipes_to_suspend = 0;
info[i].pipes_suspended = 0;
info[i].pipes_resumed = 0;
+ info[i].bam_type = i;
INIT_WORK(&info[i].resume_work, usb_bam_finish_resume);
INIT_WORK(&info[i].suspend_work, usb_bam_start_suspend);
INIT_WORK(&info[i].finish_suspend_work,
@@ -2863,7 +3094,7 @@ static int usb_bam_probe(struct platform_device *pdev)
int usb_bam_get_qdss_idx(u8 num)
{
return usb_bam_get_connection_idx(ctx.qdss_core_name, QDSS_P_BAM,
- PEER_PERIPHERAL_TO_USB, num);
+ PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, num);
}
EXPORT_SYMBOL(usb_bam_get_qdss_idx);
@@ -2902,7 +3133,7 @@ EXPORT_SYMBOL(get_bam2bam_connection_info);
int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client,
- enum usb_bam_pipe_dir dir, u32 num)
+ enum usb_bam_pipe_dir dir, enum usb_bam_mode bam_mode, u32 num)
{
u8 i;
int bam_type;
@@ -2918,6 +3149,7 @@ int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client,
if (usb_bam_connections[i].bam_type == bam_type &&
usb_bam_connections[i].peer_bam == client &&
usb_bam_connections[i].dir == dir &&
+ usb_bam_connections[i].bam_mode == bam_mode &&
usb_bam_connections[i].pipe_num == num) {
pr_debug("%s: index %d was found\n", __func__, i);
return i;
@@ -2928,25 +3160,51 @@ int usb_bam_get_connection_idx(const char *core_name, enum peer_bam client,
}
EXPORT_SYMBOL(usb_bam_get_connection_idx);
-bool msm_bam_lpm_ok(void)
+bool msm_bam_device_lpm_ok(enum usb_bam bam_type)
{
+ pr_debug("%s: enter bam%s\n", __func__, bam_enable_strings[bam_type]);
+
spin_lock(&usb_bam_ipa_handshake_info_lock);
- if (info[HSUSB_BAM].lpm_wait_handshake ||
- info[HSUSB_BAM].lpm_wait_pipes) {
- info[HSUSB_BAM].pending_lpm = 1;
+ if (info[bam_type].lpm_wait_handshake ||
+ info[bam_type].lpm_wait_pipes) {
+ info[bam_type].pending_lpm = 1;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
pr_err("%s: Scheduling LPM for later\n", __func__);
return 0;
} else {
- info[HSUSB_BAM].pending_lpm = 0;
- info[HSUSB_BAM].in_lpm = true;
+ info[bam_type].pending_lpm = 0;
+ info[bam_type].in_lpm = true;
spin_unlock(&usb_bam_ipa_handshake_info_lock);
pr_err("%s: Going to LPM now\n", __func__);
return 1;
}
}
-EXPORT_SYMBOL(msm_bam_lpm_ok);
+bool msm_bam_usb_lpm_ok(void)
+{
+ pr_debug("%s: enter mode %d\n", __func__, info[HSUSB_BAM].cur_bam_mode);
+
+ if (info[HSUSB_BAM].cur_bam_mode == USB_BAM_DEVICE)
+ return msm_bam_device_lpm_ok(HSUSB_BAM);
+ else /* USB_BAM_HOST */ {
+ return msm_bam_host_lpm_ok(HSUSB_BAM);
+ }
+}
+EXPORT_SYMBOL(msm_bam_usb_lpm_ok);
+
+bool msm_bam_hsic_lpm_ok(void)
+{
+ pr_debug("%s: enter\n", __func__);
+
+ if (info[HSIC_BAM].cur_bam_mode == USB_BAM_DEVICE)
+ return msm_bam_device_lpm_ok(HSIC_BAM);
+ else /* USB_BAM_HOST */ {
+ return msm_bam_host_lpm_ok(HSIC_BAM);
+ }
+}
+EXPORT_SYMBOL(msm_bam_hsic_lpm_ok);
+
+/* TODO: make this for SSUSB_BAM when lpm support is in place */
void msm_bam_notify_lpm_resume()
{
/*
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index ef86091a8385..251f43575463 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -207,6 +207,7 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),
POWER_SUPPLY_ATTR(serial_number),
+ POWER_SUPPLY_ATTR(battery_type),
};
static struct attribute *
diff --git a/drivers/power/qpnp-bms.c b/drivers/power/qpnp-bms.c
index a95389f6a348..63af1ab05a41 100644
--- a/drivers/power/qpnp-bms.c
+++ b/drivers/power/qpnp-bms.c
@@ -408,6 +408,14 @@ static void disable_bms_irq(struct bms_irq *irq)
}
}
+static void disable_bms_irq_nosync(struct bms_irq *irq)
+{
+ if (!__test_and_set_bit(0, &irq->disabled)) {
+ disable_irq_nosync(irq->irq);
+ pr_debug("disabled irq %d\n", irq->irq);
+ }
+}
+
#define HOLD_OREG_DATA BIT(0)
static int lock_output_data(struct qpnp_bms_chip *chip)
{
@@ -1007,14 +1015,14 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip,
chip->base + BMS1_OCV_FOR_SOC_DATA0, 2);
if (rc) {
pr_err("Error reading ocv: rc = %d\n", rc);
- return -ENXIO;
+ goto param_err;
}
rc = read_cc_raw(chip, &raw->cc, CC);
rc = read_cc_raw(chip, &raw->shdw_cc, SHDW_CC);
if (rc) {
pr_err("Failed to read raw cc data, rc = %d\n", rc);
- return rc;
+ goto param_err;
}
unlock_output_data(chip);
@@ -1072,6 +1080,11 @@ static int read_soc_params_raw(struct qpnp_bms_chip *chip,
raw->last_good_ocv_raw, raw->last_good_ocv_uv);
pr_debug("cc_raw= 0x%llx\n", raw->cc);
return 0;
+
+param_err:
+ unlock_output_data(chip);
+ mutex_unlock(&chip->bms_output_lock);
+ return rc;
}
static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
@@ -1478,7 +1491,7 @@ static int get_prop_bms_charge_counter(struct qpnp_bms_chip *chip)
mutex_lock(&chip->bms_output_lock);
lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, false);
+ read_cc_raw(chip, &cc_raw, CC);
unlock_output_data(chip);
mutex_unlock(&chip->bms_output_lock);
@@ -1492,7 +1505,7 @@ static int get_prop_bms_charge_counter_shadow(struct qpnp_bms_chip *chip)
mutex_lock(&chip->bms_output_lock);
lock_output_data(chip);
- read_cc_raw(chip, &cc_raw, true);
+ read_cc_raw(chip, &cc_raw, SHDW_CC);
unlock_output_data(chip);
mutex_unlock(&chip->bms_output_lock);
@@ -3548,7 +3561,7 @@ static irqreturn_t bms_sw_cc_thr_irq_handler(int irq, void *_chip)
struct qpnp_bms_chip *chip = _chip;
pr_debug("sw_cc_thr irq triggered\n");
- disable_bms_irq(&chip->sw_cc_thr_irq);
+ disable_bms_irq_nosync(&chip->sw_cc_thr_irq);
bms_stay_awake(&chip->soc_wake_source);
schedule_work(&chip->recalc_work);
return IRQ_HANDLED;
diff --git a/drivers/power/smb135x-charger.c b/drivers/power/smb135x-charger.c
index d8e0de3ad135..63d4db871b20 100644
--- a/drivers/power/smb135x-charger.c
+++ b/drivers/power/smb135x-charger.c
@@ -200,6 +200,11 @@
#define IRQ_D_TIMEOUT_BIT BIT(2)
#define IRQ_E_REG 0x54
+#define IRQ_E_DC_OV_BIT BIT(6)
+#define IRQ_E_DC_UV_BIT BIT(4)
+#define IRQ_E_USB_OV_BIT BIT(2)
+#define IRQ_E_USB_UV_BIT BIT(0)
+
#define IRQ_F_REG 0x55
#define IRQ_F_POWER_OK_BIT BIT(0)
@@ -237,6 +242,7 @@ struct smb135x_chg {
bool usb_present;
bool dc_present;
+ bool dc_ov;
bool bmd_algo_disabled;
bool iterm_disabled;
@@ -375,34 +381,6 @@ static bool is_usb100_broken(struct smb135x_chg *chip)
return !!(reg & CHECK_USB100_GOOD_BIT);
}
-static bool is_dc_present(struct smb135x_chg *chip)
-{
- int rc;
- u8 reg;
-
- rc = smb135x_read(chip, STATUS_8_REG, &reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & (DCIN_9V | DCIN_UNREG | DCIN_LV));
-}
-
-static bool is_usb_present(struct smb135x_chg *chip)
-{
- int rc;
- u8 reg;
-
- rc = smb135x_read(chip, STATUS_8_REG, &reg);
- if (rc < 0) {
- dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
- return false;
- }
-
- return !!(reg & (USBIN_9V | USBIN_UNREG | USBIN_LV));
-}
-
static char *usb_type_str[] = {
"ACA_DOCK", /* bit 0 */
"ACA_C", /* bit 1 */
@@ -428,11 +406,11 @@ static enum power_supply_type usb_type_enum[] = {
POWER_SUPPLY_TYPE_USB_ACA, /* bit 1 */
POWER_SUPPLY_TYPE_USB_ACA, /* bit 2 */
POWER_SUPPLY_TYPE_USB_ACA, /* bit 3 */
- POWER_SUPPLY_TYPE_UNKNOWN, /* bit 5 */
POWER_SUPPLY_TYPE_USB, /* bit 4 */
+ POWER_SUPPLY_TYPE_UNKNOWN, /* bit 5 */
POWER_SUPPLY_TYPE_USB_DCP, /* bit 6 */
POWER_SUPPLY_TYPE_USB_CDP, /* bit 7 */
- POWER_SUPPLY_TYPE_USB, /* bit 8 error case, report SDP */
+ POWER_SUPPLY_TYPE_UNKNOWN, /* bit 8 error case, report UNKNWON */
};
/* helper to return enum power_supply_type of USB type */
@@ -505,18 +483,6 @@ static int smb135x_get_prop_batt_present(struct smb135x_chg *chip)
return chip->batt_present;
}
-static int smb135x_get_charging_status(struct smb135x_chg *chip)
-{
- int rc;
- u8 reg = 0;
-
- rc = smb135x_read(chip, STATUS_4_REG, &reg);
- if (rc < 0)
- return 0;
-
- return (reg & CHG_EN_BIT) ? 1 : 0;
-}
-
static int smb135x_get_prop_charge_type(struct smb135x_chg *chip)
{
int rc;
@@ -915,7 +881,7 @@ static int smb135x_battery_get_property(struct power_supply *psy,
val->intval = smb135x_get_prop_batt_present(chip);
break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
- val->intval = smb135x_get_charging_status(chip);
+ val->intval = chip->chg_enabled;
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = smb135x_get_prop_charge_type(chip);
@@ -937,6 +903,7 @@ static int smb135x_battery_get_property(struct power_supply *psy,
static enum power_supply_property smb135x_dc_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_HEALTH,
};
static int smb135x_dc_get_property(struct power_supply *psy,
@@ -948,8 +915,10 @@ static int smb135x_dc_get_property(struct power_supply *psy,
switch (prop) {
case POWER_SUPPLY_PROP_ONLINE:
- /* return if dc is charging the battery */
- val->intval = is_dc_present(chip);
+ val->intval = chip->dc_present;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = chip->dc_present;
break;
default:
return -EINVAL;
@@ -1222,15 +1191,42 @@ static int power_ok_handler(struct smb135x_chg *chip, u8 rt_stat)
return 0;
}
+static int handle_dc_removal(struct smb135x_chg *chip)
+{
+ if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) {
+ cancel_delayed_work_sync(&chip->wireless_insertion_work);
+ smb135x_dc_suspend(chip, true);
+ }
+
+ if (chip->dc_psy_type != -EINVAL)
+ power_supply_set_online(&chip->dc_psy, chip->dc_present);
+ return 0;
+}
+
+#define DCIN_UNSUSPEND_DELAY_MS 1000
+static int handle_dc_insertion(struct smb135x_chg *chip)
+{
+ if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS)
+ schedule_delayed_work(&chip->wireless_insertion_work,
+ msecs_to_jiffies(DCIN_UNSUSPEND_DELAY_MS));
+ if (chip->dc_psy_type != -EINVAL)
+ power_supply_set_online(&chip->dc_psy,
+ chip->dc_present);
+
+ return 0;
+}
/**
* dcin_uv_handler() - called when the dc voltage crosses the uv threshold
* @chip: pointer to smb135x_chg chip
* @rt_stat: the status bit indicating whether dc voltage is uv
*/
-#define DCIN_UNSUSPEND_DELAY_MS 1000
static int dcin_uv_handler(struct smb135x_chg *chip, u8 rt_stat)
{
- bool dc_present = is_dc_present(chip);
+ /*
+ * rt_stat indicates if dc is undervolted. If so dc_present
+ * should be marked removed
+ */
+ bool dc_present = !rt_stat;
pr_debug("chip->dc_present = %d dc_present = %d\n",
chip->dc_present, dc_present);
@@ -1238,30 +1234,45 @@ static int dcin_uv_handler(struct smb135x_chg *chip, u8 rt_stat)
if (chip->dc_present && !dc_present) {
/* dc removed */
chip->dc_present = dc_present;
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS) {
- cancel_delayed_work_sync(
- &chip->wireless_insertion_work);
- smb135x_dc_suspend(chip, true);
- }
- if (chip->dc_psy_type != -EINVAL)
- power_supply_set_online(&chip->dc_psy,
- chip->dc_present);
+ handle_dc_removal(chip);
}
if (!chip->dc_present && dc_present) {
/* dc inserted */
chip->dc_present = dc_present;
- if (chip->dc_psy_type == POWER_SUPPLY_TYPE_WIRELESS)
- schedule_delayed_work(&chip->wireless_insertion_work,
- msecs_to_jiffies(DCIN_UNSUSPEND_DELAY_MS));
- if (chip->dc_psy_type != -EINVAL)
- power_supply_set_online(&chip->dc_psy,
- chip->dc_present);
+ handle_dc_insertion(chip);
}
return 0;
}
+static int dcin_ov_handler(struct smb135x_chg *chip, u8 rt_stat)
+{
+ /*
+ * rt_stat indicates if dc is overvolted. If so dc_present
+ * should be marked removed
+ */
+ bool dc_present = !rt_stat;
+
+ pr_debug("chip->dc_present = %d dc_present = %d\n",
+ chip->dc_present, dc_present);
+
+ chip->dc_ov = !!rt_stat;
+
+ if (chip->dc_present && !dc_present) {
+ /* dc removed */
+ chip->dc_present = dc_present;
+ handle_dc_removal(chip);
+ }
+
+ if (!chip->dc_present && dc_present) {
+ /* dc inserted */
+ chip->dc_present = dc_present;
+ handle_dc_insertion(chip);
+ }
+ return 0;
+}
+
static int handle_usb_removal(struct smb135x_chg *chip)
{
if (chip->usb_psy) {
@@ -1288,15 +1299,16 @@ static int handle_usb_insertion(struct smb135x_chg *chip)
dev_err(chip->dev, "Couldn't read status 5 rc = %d\n", rc);
return rc;
}
- usb_type_name = get_usb_type_name(reg);
- usb_supply_type = get_usb_supply_type(reg);
/*
* Report the charger type as UNKNOWN if the
* apsd-fail flag is set. This nofifies the USB driver
* to initiate a s/w based charger type detection.
*/
if (chip->workaround_flags & WRKARND_APSD_FAIL)
- usb_supply_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ reg = 0;
+
+ usb_type_name = get_usb_type_name(reg);
+ usb_supply_type = get_usb_supply_type(reg);
pr_debug("inserted %s, usb psy type = %d stat_5 = 0x%02x\n",
usb_type_name, usb_supply_type, reg);
if (chip->usb_psy) {
@@ -1315,7 +1327,30 @@ static int handle_usb_insertion(struct smb135x_chg *chip)
*/
static int usbin_uv_handler(struct smb135x_chg *chip, u8 rt_stat)
{
- bool usb_present = is_usb_present(chip);
+ /*
+ * rt_stat indicates if usb is undervolted. If so usb_present
+ * should be marked removed
+ */
+ bool usb_present = !rt_stat;
+
+ pr_debug("chip->usb_present = %d usb_present = %d\n",
+ chip->usb_present, usb_present);
+ if (chip->usb_present && !usb_present) {
+ /* USB removed */
+ chip->usb_present = usb_present;
+ handle_usb_removal(chip);
+ }
+ return 0;
+}
+
+static int usbin_ov_handler(struct smb135x_chg *chip, u8 rt_stat)
+{
+ /*
+ * rt_stat indicates if usb is overvolted. If so usb_present
+ * should be marked removed
+ */
+ bool usb_present = !rt_stat;
+ int health;
pr_debug("chip->usb_present = %d usb_present = %d\n",
chip->usb_present, usb_present);
@@ -1324,18 +1359,25 @@ static int usbin_uv_handler(struct smb135x_chg *chip, u8 rt_stat)
chip->usb_present = usb_present;
handle_usb_removal(chip);
}
+
+ if (chip->usb_psy) {
+ health = rt_stat ? POWER_SUPPLY_HEALTH_OVERVOLTAGE
+ : POWER_SUPPLY_HEALTH_GOOD;
+ power_supply_set_health_state(chip->usb_psy, health);
+ }
+
return 0;
}
/**
* src_detect_handler() - this is called when USB charger type is detected, use
- * it for handling USB charger insertion
+ * it for handling USB charger insertion/removal
* @chip: pointer to smb135x_chg chip
* @rt_stat: the status bit indicating chg insertion/removal
*/
static int src_detect_handler(struct smb135x_chg *chip, u8 rt_stat)
{
- bool usb_present = is_usb_present(chip);
+ bool usb_present = !!rt_stat;
pr_debug("chip->usb_present = %d usb_present = %d\n",
chip->usb_present, usb_present);
@@ -1466,6 +1508,7 @@ static struct irq_handler_info handlers[] = {
},
{
.name = "usbin_ov",
+ .smb_irq = usbin_ov_handler,
},
{
.name = "dcin_uv",
@@ -1473,6 +1516,7 @@ static struct irq_handler_info handlers[] = {
},
{
.name = "dcin_ov",
+ .smb_irq = dcin_ov_handler,
},
},
},
@@ -1774,6 +1818,11 @@ static int force_rechg_set(void *data, u64 val)
int rc;
struct smb135x_chg *chip = data;
+ if (!chip->chg_enabled) {
+ pr_debug("Charging Disabled force recharge not allowed\n");
+ return -EINVAL;
+ }
+
rc = smb135x_masked_write(chip, CFG_14_REG, EN_CHG_INHIBIT_BIT, 0);
if (rc)
dev_err(chip->dev,
@@ -1883,8 +1932,14 @@ static int determine_initial_status(struct smb135x_chg *chip)
if (reg & IRQ_C_TERM_BIT)
chip->chg_done_batt_full = true;
- chip->usb_present = is_usb_present(chip);
- chip->dc_present = is_dc_present(chip);
+ rc = smb135x_read(chip, IRQ_E_REG, &reg);
+ if (rc < 0) {
+ dev_err(chip->dev, "Couldn't read irq E rc = %d\n", rc);
+ return rc;
+ }
+ chip->usb_present = !(reg & IRQ_E_USB_OV_BIT)
+ && !(reg & IRQ_E_USB_UV_BIT);
+ chip->dc_present = !(reg & IRQ_E_DC_OV_BIT) && !(reg & IRQ_E_DC_UV_BIT);
if (chip->usb_present)
handle_usb_insertion(chip);
@@ -2510,7 +2565,8 @@ static int smb135x_charger_probe(struct i2c_client *client,
"Couldn't create count debug file rc = %d\n",
rc);
- ent = debugfs_create_file("force_recharge", S_IFREG | S_IRUGO,
+ ent = debugfs_create_file("force_recharge",
+ S_IFREG | S_IWUSR | S_IRUGO,
chip->debug_root, chip,
&force_rechg_ops);
if (!ent)
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 25ed1fc7e226..179c87fb892d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -544,6 +544,17 @@ config REGULATOR_STUB
Clients can use the real regulator device names with proper
constraint checking while the real driver is being developed.
+config REGULATOR_RPM_SMD
+ bool "RPM SMD regulator driver"
+ depends on OF
+ depends on MSM_RPM_SMD
+ help
+ Compile in support for the RPM SMD regulator driver which is used for
+ setting voltages and other parameters of the various power rails
+ supplied by some Qualcomm PMICs. The RPM SMD regulator driver should
+ be used on systems which contain an RPM which communicates with the
+ application processor over SMD.
+
config REGULATOR_QPNP
depends on SPMI
depends on OF_SPMI
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 064ea0a6161d..5eb98aca1bcd 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -73,6 +73,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+obj-$(CONFIG_REGULATOR_RPM_SMD) += rpm-smd-regulator.o
obj-$(CONFIG_REGULATOR_QPNP) += qpnp-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
index 740732f42287..2487bd2f6df3 100644
--- a/drivers/regulator/qpnp-regulator.c
+++ b/drivers/regulator/qpnp-regulator.c
@@ -1105,7 +1105,7 @@ static irqreturn_t qpnp_regulator_vs_ocp_isr(int irq, void *data)
return IRQ_HANDLED;
}
-static const char const *qpnp_print_actions[] = {
+static const char * const qpnp_print_actions[] = {
[QPNP_REGULATOR_ACTION_INIT] = "initial ",
[QPNP_REGULATOR_ACTION_ENABLE] = "enable ",
[QPNP_REGULATOR_ACTION_DISABLE] = "disable ",
diff --git a/drivers/regulator/rpm-smd-regulator.c b/drivers/regulator/rpm-smd-regulator.c
new file mode 100644
index 000000000000..ffc4776b799e
--- /dev/null
+++ b/drivers/regulator/rpm-smd-regulator.c
@@ -0,0 +1,1722 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <mach/rpm-smd.h>
+
+/* Debug Definitions */
+
+enum {
+ RPM_VREG_DEBUG_REQUEST = BIT(0),
+ RPM_VREG_DEBUG_FULL_REQUEST = BIT(1),
+ RPM_VREG_DEBUG_DUPLICATE = BIT(2),
+};
+
+static int rpm_vreg_debug_mask;
+module_param_named(
+ debug_mask, rpm_vreg_debug_mask, int, S_IRUSR | S_IWUSR
+);
+
+#define vreg_err(req, fmt, ...) \
+ pr_err("%s: " fmt, req->rdesc.name, ##__VA_ARGS__)
+
+/* RPM regulator request types */
+enum rpm_regulator_type {
+ RPM_REGULATOR_TYPE_LDO,
+ RPM_REGULATOR_TYPE_SMPS,
+ RPM_REGULATOR_TYPE_VS,
+ RPM_REGULATOR_TYPE_NCP,
+ RPM_REGULATOR_TYPE_MAX,
+};
+
+/* RPM resource parameters */
+enum rpm_regulator_param_index {
+ RPM_REGULATOR_PARAM_ENABLE,
+ RPM_REGULATOR_PARAM_VOLTAGE,
+ RPM_REGULATOR_PARAM_CURRENT,
+ RPM_REGULATOR_PARAM_MODE_LDO,
+ RPM_REGULATOR_PARAM_MODE_SMPS,
+ RPM_REGULATOR_PARAM_PIN_CTRL_ENABLE,
+ RPM_REGULATOR_PARAM_PIN_CTRL_MODE,
+ RPM_REGULATOR_PARAM_FREQUENCY,
+ RPM_REGULATOR_PARAM_HEAD_ROOM,
+ RPM_REGULATOR_PARAM_QUIET_MODE,
+ RPM_REGULATOR_PARAM_FREQ_REASON,
+ RPM_REGULATOR_PARAM_CORNER,
+ RPM_REGULATOR_PARAM_BYPASS,
+ RPM_REGULATOR_PARAM_FLOOR_CORNER,
+ RPM_REGULATOR_PARAM_MAX,
+};
+
+enum rpm_regulator_smps_mode {
+ RPM_REGULATOR_SMPS_MODE_AUTO = 0,
+ RPM_REGULATOR_SMPS_MODE_IPEAK = 1,
+ RPM_REGULATOR_SMPS_MODE_PWM = 2,
+};
+
+enum rpm_regulator_ldo_mode {
+ RPM_REGULATOR_LDO_MODE_IPEAK = 0,
+ RPM_REGULATOR_LDO_MODE_HPM = 1,
+};
+
+#define RPM_SET_CONFIG_ACTIVE BIT(0)
+#define RPM_SET_CONFIG_SLEEP BIT(1)
+#define RPM_SET_CONFIG_BOTH (RPM_SET_CONFIG_ACTIVE \
+ | RPM_SET_CONFIG_SLEEP)
+struct rpm_regulator_param {
+ char *name;
+ char *property_name;
+ u32 key;
+ u32 min;
+ u32 max;
+ u32 supported_regulator_types;
+};
+
+#define PARAM(_idx, _support_ldo, _support_smps, _support_vs, _support_ncp, \
+ _name, _min, _max, _property_name) \
+ [RPM_REGULATOR_PARAM_##_idx] = { \
+ .name = _name, \
+ .property_name = _property_name, \
+ .min = _min, \
+ .max = _max, \
+ .supported_regulator_types = \
+ _support_ldo << RPM_REGULATOR_TYPE_LDO | \
+ _support_smps << RPM_REGULATOR_TYPE_SMPS | \
+ _support_vs << RPM_REGULATOR_TYPE_VS | \
+ _support_ncp << RPM_REGULATOR_TYPE_NCP, \
+ }
+
+static struct rpm_regulator_param params[RPM_REGULATOR_PARAM_MAX] = {
+ /* ID LDO SMPS VS NCP name min max property-name */
+ PARAM(ENABLE, 1, 1, 1, 1, "swen", 0, 1, "qcom,init-enable"),
+ PARAM(VOLTAGE, 1, 1, 0, 1, "uv", 0, 0x7FFFFFF, "qcom,init-voltage"),
+ PARAM(CURRENT, 1, 1, 0, 0, "ma", 0, 0x1FFF, "qcom,init-current"),
+ PARAM(MODE_LDO, 1, 0, 0, 0, "lsmd", 0, 1, "qcom,init-ldo-mode"),
+ PARAM(MODE_SMPS, 0, 1, 0, 0, "ssmd", 0, 2, "qcom,init-smps-mode"),
+ PARAM(PIN_CTRL_ENABLE, 1, 1, 1, 0, "pcen", 0, 0xF, "qcom,init-pin-ctrl-enable"),
+ PARAM(PIN_CTRL_MODE, 1, 1, 1, 0, "pcmd", 0, 0x1F, "qcom,init-pin-ctrl-mode"),
+ PARAM(FREQUENCY, 0, 1, 0, 1, "freq", 0, 31, "qcom,init-frequency"),
+ PARAM(HEAD_ROOM, 1, 0, 0, 1, "hr", 0, 0x7FFFFFFF, "qcom,init-head-room"),
+ PARAM(QUIET_MODE, 0, 1, 0, 0, "qm", 0, 2, "qcom,init-quiet-mode"),
+ PARAM(FREQ_REASON, 0, 1, 0, 1, "resn", 0, 8, "qcom,init-freq-reason"),
+ PARAM(CORNER, 1, 1, 0, 0, "corn", 0, 6, "qcom,init-voltage-corner"),
+ PARAM(BYPASS, 1, 0, 0, 0, "bypa", 0, 1, "qcom,init-disallow-bypass"),
+ PARAM(FLOOR_CORNER, 1, 1, 0, 0, "vfc", 0, 6, "qcom,init-voltage-floor-corner"),
+};
+
+struct rpm_regulator_mode_map {
+ int ldo_mode;
+ int smps_mode;
+};
+
+static struct rpm_regulator_mode_map mode_mapping[] = {
+ [RPM_REGULATOR_MODE_AUTO]
+ = {-1, RPM_REGULATOR_SMPS_MODE_AUTO},
+ [RPM_REGULATOR_MODE_IPEAK]
+ = {RPM_REGULATOR_LDO_MODE_IPEAK, RPM_REGULATOR_SMPS_MODE_IPEAK},
+ [RPM_REGULATOR_MODE_HPM]
+ = {RPM_REGULATOR_LDO_MODE_HPM, RPM_REGULATOR_SMPS_MODE_PWM},
+};
+
+struct rpm_vreg_request {
+ u32 param[RPM_REGULATOR_PARAM_MAX];
+ u32 valid;
+ u32 modified;
+};
+
+struct rpm_vreg {
+ struct rpm_vreg_request aggr_req_active;
+ struct rpm_vreg_request aggr_req_sleep;
+ struct list_head reg_list;
+ const char *resource_name;
+ u32 resource_id;
+ bool allow_atomic;
+ int regulator_type;
+ int hpm_min_load;
+ int enable_time;
+ spinlock_t slock;
+ struct mutex mlock;
+ unsigned long flags;
+ bool sleep_request_sent;
+ bool apps_only;
+ struct msm_rpm_request *handle_active;
+ struct msm_rpm_request *handle_sleep;
+};
+
+struct rpm_regulator {
+ struct regulator_desc rdesc;
+ struct regulator_dev *rdev;
+ struct rpm_vreg *rpm_vreg;
+ struct list_head list;
+ bool set_active;
+ bool set_sleep;
+ bool always_send_voltage;
+ bool always_send_current;
+ struct rpm_vreg_request req;
+ int system_load;
+ int min_uV;
+ int max_uV;
+};
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level. It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/*
+ * Regulator requests sent in the active set take effect immediately. Requests
+ * sent in the sleep set take effect when the Apps processor transitions into
+ * RPM assisted power collapse. For any given regulator, if an active set
+ * request is present, but not a sleep set request, then the active set request
+ * is used at all times, even when the Apps processor is power collapsed.
+ *
+ * The rpm-regulator-smd takes advantage of this default usage of the active set
+ * request by only sending a sleep set request if it differs from the
+ * corresponding active set request.
+ */
+#define RPM_SET_ACTIVE MSM_RPM_CTX_ACTIVE_SET
+#define RPM_SET_SLEEP MSM_RPM_CTX_SLEEP_SET
+
+static u32 rpm_vreg_string_to_int(const u8 *str)
+{
+ int i, len;
+ u32 output = 0;
+
+ len = strnlen(str, sizeof(u32));
+ for (i = 0; i < len; i++)
+ output |= str[i] << (i * 8);
+
+ return output;
+}
+
+static inline void rpm_vreg_lock(struct rpm_vreg *rpm_vreg)
+{
+ if (rpm_vreg->allow_atomic)
+ spin_lock_irqsave(&rpm_vreg->slock, rpm_vreg->flags);
+ else
+ mutex_lock(&rpm_vreg->mlock);
+}
+
+static inline void rpm_vreg_unlock(struct rpm_vreg *rpm_vreg)
+{
+ if (rpm_vreg->allow_atomic)
+ spin_unlock_irqrestore(&rpm_vreg->slock, rpm_vreg->flags);
+ else
+ mutex_unlock(&rpm_vreg->mlock);
+}
+
+static inline bool rpm_vreg_active_or_sleep_enabled(struct rpm_vreg *rpm_vreg)
+{
+ return (rpm_vreg->aggr_req_active.param[RPM_REGULATOR_PARAM_ENABLE]
+ && (rpm_vreg->aggr_req_active.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE)))
+ || ((rpm_vreg->aggr_req_sleep.param[RPM_REGULATOR_PARAM_ENABLE])
+ && (rpm_vreg->aggr_req_sleep.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE)));
+}
+
+static inline bool rpm_vreg_shared_active_or_sleep_enabled_valid
+ (struct rpm_vreg *rpm_vreg)
+{
+ return !rpm_vreg->apps_only &&
+ ((rpm_vreg->aggr_req_active.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE))
+ || (rpm_vreg->aggr_req_sleep.valid
+ & BIT(RPM_REGULATOR_PARAM_ENABLE)));
+}
+
+/*
+ * This is used when voting for LPM or HPM by subtracting or adding to the
+ * hpm_min_load of a regulator. It has units of uA.
+ */
+#define LOAD_THRESHOLD_STEP 1000
+
+static inline int rpm_vreg_hpm_min_uA(struct rpm_vreg *rpm_vreg)
+{
+ return rpm_vreg->hpm_min_load;
+}
+
+static inline int rpm_vreg_lpm_max_uA(struct rpm_vreg *rpm_vreg)
+{
+ return rpm_vreg->hpm_min_load - LOAD_THRESHOLD_STEP;
+}
+
+#define MICRO_TO_MILLI(uV) ((uV) / 1000)
+#define MILLI_TO_MICRO(uV) ((uV) * 1000)
+
+#define DEBUG_PRINT_BUFFER_SIZE 512
+#define REQ_SENT 0
+#define REQ_PREV 1
+#define REQ_CACHED 2
+#define REQ_TYPES 3
+
+static void rpm_regulator_req(struct rpm_regulator *regulator, int set,
+ bool sent)
+{
+ char buf[DEBUG_PRINT_BUFFER_SIZE];
+ size_t buflen = DEBUG_PRINT_BUFFER_SIZE;
+ struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
+ struct rpm_vreg_request *aggr;
+ bool first;
+ u32 mask[REQ_TYPES] = {0, 0, 0};
+ const char *req_names[REQ_TYPES] = {"sent", "prev", "cached"};
+ int pos = 0;
+ int i, j;
+
+ aggr = (set == RPM_SET_ACTIVE)
+ ? &rpm_vreg->aggr_req_active : &rpm_vreg->aggr_req_sleep;
+
+ if (rpm_vreg_debug_mask & RPM_VREG_DEBUG_DUPLICATE) {
+ mask[REQ_SENT] = aggr->modified;
+ mask[REQ_PREV] = aggr->valid & ~aggr->modified;
+ } else if (sent
+ && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_FULL_REQUEST)) {
+ mask[REQ_SENT] = aggr->modified;
+ mask[REQ_PREV] = aggr->valid & ~aggr->modified;
+ } else if (sent && (rpm_vreg_debug_mask & RPM_VREG_DEBUG_REQUEST)) {
+ mask[REQ_SENT] = aggr->modified;
+ }
+
+ if (!(mask[REQ_SENT] | mask[REQ_PREV]))
+ return;
+
+ if (set == RPM_SET_SLEEP && !rpm_vreg->sleep_request_sent) {
+ mask[REQ_CACHED] = mask[REQ_SENT] | mask[REQ_PREV];
+ mask[REQ_SENT] = 0;
+ mask[REQ_PREV] = 0;
+ }
+
+ pos += scnprintf(buf + pos, buflen - pos, "%s%s: ",
+ KERN_INFO, __func__);
+
+ pos += scnprintf(buf + pos, buflen - pos, "%s %u (%s): s=%s",
+ rpm_vreg->resource_name, rpm_vreg->resource_id,
+ regulator->rdesc.name,
+ (set == RPM_SET_ACTIVE ? "act" : "slp"));
+
+ for (i = 0; i < REQ_TYPES; i++) {
+ if (mask[i])
+ pos += scnprintf(buf + pos, buflen - pos, "; %s: ",
+ req_names[i]);
+
+ first = true;
+ for (j = 0; j < RPM_REGULATOR_PARAM_MAX; j++) {
+ if (mask[i] & BIT(j)) {
+ pos += scnprintf(buf + pos, buflen - pos,
+ "%s%s=%u", (first ? "" : ", "),
+ params[j].name, aggr->param[j]);
+ first = false;
+ }
+ }
+ }
+
+ pos += scnprintf(buf + pos, buflen - pos, "\n");
+ printk(buf);
+}
+
+#define RPM_VREG_SET_PARAM(_regulator, _param, _val) \
+{ \
+ (_regulator)->req.param[RPM_REGULATOR_PARAM_##_param] = _val; \
+ (_regulator)->req.modified |= BIT(RPM_REGULATOR_PARAM_##_param); \
+} \
+
+static int rpm_vreg_add_kvp_to_request(struct rpm_vreg *rpm_vreg,
+ const u32 *param, int idx, u32 set)
+{
+ struct msm_rpm_request *handle;
+
+ handle = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active
+ : rpm_vreg->handle_sleep);
+
+ if (rpm_vreg->allow_atomic)
+ return msm_rpm_add_kvp_data_noirq(handle, params[idx].key,
+ (u8 *)&param[idx], 4);
+ else
+ return msm_rpm_add_kvp_data(handle, params[idx].key,
+ (u8 *)&param[idx], 4);
+}
+
+static void rpm_vreg_check_modified_requests(const u32 *prev_param,
+ const u32 *param, u32 prev_valid, u32 *modified)
+{
+ u32 value_changed = 0;
+ int i;
+
+ for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
+ if (param[i] != prev_param[i])
+ value_changed |= BIT(i);
+ }
+
+ /*
+ * Only keep bits that are for changed parameters or previously
+ * invalid parameters.
+ */
+ *modified &= value_changed | ~prev_valid;
+}
+
+static int rpm_vreg_add_modified_requests(struct rpm_regulator *regulator,
+ u32 set, const u32 *param, u32 modified)
+{
+ struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
+ int rc = 0;
+ int i;
+
+ for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
+ /* Only send requests for modified parameters. */
+ if (modified & BIT(i)) {
+ rc = rpm_vreg_add_kvp_to_request(rpm_vreg, param, i,
+ set);
+ if (rc) {
+ vreg_err(regulator,
+ "add KVP failed: %s %u; %s, rc=%d\n",
+ rpm_vreg->resource_name,
+ rpm_vreg->resource_id, params[i].name,
+ rc);
+ return rc;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int rpm_vreg_send_request(struct rpm_regulator *regulator, u32 set)
+{
+ struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
+ struct msm_rpm_request *handle
+ = (set == RPM_SET_ACTIVE ? rpm_vreg->handle_active
+ : rpm_vreg->handle_sleep);
+ int rc;
+
+ if (rpm_vreg->allow_atomic)
+ rc = msm_rpm_wait_for_ack_noirq(msm_rpm_send_request_noirq(
+ handle));
+ else
+ rc = msm_rpm_wait_for_ack(msm_rpm_send_request(handle));
+
+ if (rc)
+ vreg_err(regulator,
+ "msm rpm send failed: %s %u; set=%s, rc=%d\n",
+ rpm_vreg->resource_name,
+ rpm_vreg->resource_id,
+ (set == RPM_SET_ACTIVE ? "act" : "slp"), rc);
+
+ return rc;
+}
+
+#define RPM_VREG_AGGR_MIN(_idx, _param_aggr, _param_reg) \
+{ \
+ _param_aggr[RPM_REGULATOR_PARAM_##_idx] \
+ = min(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \
+ _param_reg[RPM_REGULATOR_PARAM_##_idx]); \
+}
+
+#define RPM_VREG_AGGR_MAX(_idx, _param_aggr, _param_reg) \
+{ \
+ _param_aggr[RPM_REGULATOR_PARAM_##_idx] \
+ = max(_param_aggr[RPM_REGULATOR_PARAM_##_idx], \
+ _param_reg[RPM_REGULATOR_PARAM_##_idx]); \
+}
+
+#define RPM_VREG_AGGR_SUM(_idx, _param_aggr, _param_reg) \
+{ \
+ _param_aggr[RPM_REGULATOR_PARAM_##_idx] \
+ += _param_reg[RPM_REGULATOR_PARAM_##_idx]; \
+}
+
+#define RPM_VREG_AGGR_OR(_idx, _param_aggr, _param_reg) \
+{ \
+ _param_aggr[RPM_REGULATOR_PARAM_##_idx] \
+ |= _param_reg[RPM_REGULATOR_PARAM_##_idx]; \
+}
+
+/*
+ * Aggregation is performed on each parameter based on the way that the RPM
+ * aggregates that type internally between RPM masters.
+ */
+static void rpm_vreg_aggregate_params(u32 *param_aggr, const u32 *param_reg)
+{
+ RPM_VREG_AGGR_MAX(ENABLE, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(VOLTAGE, param_aggr, param_reg);
+ RPM_VREG_AGGR_SUM(CURRENT, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(MODE_LDO, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(MODE_SMPS, param_aggr, param_reg);
+ RPM_VREG_AGGR_OR(PIN_CTRL_ENABLE, param_aggr, param_reg);
+ RPM_VREG_AGGR_OR(PIN_CTRL_MODE, param_aggr, param_reg);
+ RPM_VREG_AGGR_MIN(FREQUENCY, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(HEAD_ROOM, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(QUIET_MODE, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(FREQ_REASON, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(CORNER, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(BYPASS, param_aggr, param_reg);
+ RPM_VREG_AGGR_MAX(FLOOR_CORNER, param_aggr, param_reg);
+}
+
+static int rpm_vreg_aggregate_requests(struct rpm_regulator *regulator)
+{
+ struct rpm_vreg *rpm_vreg = regulator->rpm_vreg;
+ u32 param_active[RPM_REGULATOR_PARAM_MAX];
+ u32 param_sleep[RPM_REGULATOR_PARAM_MAX];
+ u32 modified_active, modified_sleep;
+ struct rpm_regulator *reg;
+ bool sleep_set_differs = false;
+ bool send_active = false;
+ bool send_sleep = false;
+ int rc = 0;
+ int i;
+
+ memset(param_active, 0, sizeof(param_active));
+ memset(param_sleep, 0, sizeof(param_sleep));
+ modified_active = rpm_vreg->aggr_req_active.modified;
+ modified_sleep = rpm_vreg->aggr_req_sleep.modified;
+
+ /*
+ * Aggregate all of the requests for this regulator in both active
+ * and sleep sets.
+ */
+ list_for_each_entry(reg, &rpm_vreg->reg_list, list) {
+ if (reg->set_active) {
+ rpm_vreg_aggregate_params(param_active, reg->req.param);
+ modified_active |= reg->req.modified;
+ }
+ if (reg->set_sleep) {
+ rpm_vreg_aggregate_params(param_sleep, reg->req.param);
+ modified_sleep |= reg->req.modified;
+ }
+ }
+
+ /*
+ * Check if the aggregated sleep set parameter values differ from the
+ * aggregated active set parameter values.
+ */
+ if (!rpm_vreg->sleep_request_sent) {
+ for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
+ if ((param_active[i] != param_sleep[i])
+ && (modified_sleep & BIT(i))) {
+ sleep_set_differs = true;
+ break;
+ }
+ }
+ }
+
+ /* Add KVPs to the active set RPM request if they have new values. */
+ rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_active.param,
+ param_active, rpm_vreg->aggr_req_active.valid,
+ &modified_active);
+ rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_ACTIVE,
+ param_active, modified_active);
+ if (rc)
+ return rc;
+ send_active = modified_active;
+
+ /*
+ * Sleep set configurations are only sent if they differ from the
+ * active set values. This is because the active set values will take
+ * effect during rpm assisted power collapse in the absence of sleep set
+ * values.
+ *
+ * However, once a sleep set request is sent for a given regulator,
+ * additional sleep set requests must be sent in the future even if they
+ * match the corresponding active set requests.
+ */
+ if (rpm_vreg->sleep_request_sent || sleep_set_differs) {
+ /* Add KVPs to the sleep set RPM request if they are new. */
+ rpm_vreg_check_modified_requests(rpm_vreg->aggr_req_sleep.param,
+ param_sleep, rpm_vreg->aggr_req_sleep.valid,
+ &modified_sleep);
+ rc = rpm_vreg_add_modified_requests(regulator, RPM_SET_SLEEP,
+ param_sleep, modified_sleep);
+ if (rc)
+ return rc;
+ send_sleep = modified_sleep;
+ }
+
+ /* Send active set request to the RPM if it contains new KVPs. */
+ if (send_active) {
+ rc = rpm_vreg_send_request(regulator, RPM_SET_ACTIVE);
+ if (rc)
+ return rc;
+ rpm_vreg->aggr_req_active.valid |= modified_active;
+ }
+ /* Store the results of the aggregation. */
+ rpm_vreg->aggr_req_active.modified = modified_active;
+ memcpy(rpm_vreg->aggr_req_active.param, param_active,
+ sizeof(param_active));
+
+ /* Handle debug printing of the active set request. */
+ rpm_regulator_req(regulator, RPM_SET_ACTIVE, send_active);
+ if (send_active)
+ rpm_vreg->aggr_req_active.modified = 0;
+
+ /* Send sleep set request to the RPM if it contains new KVPs. */
+ if (send_sleep) {
+ rc = rpm_vreg_send_request(regulator, RPM_SET_SLEEP);
+ if (rc)
+ return rc;
+ else
+ rpm_vreg->sleep_request_sent = true;
+ rpm_vreg->aggr_req_sleep.valid |= modified_sleep;
+ }
+ /* Store the results of the aggregation. */
+ rpm_vreg->aggr_req_sleep.modified = modified_sleep;
+ memcpy(rpm_vreg->aggr_req_sleep.param, param_sleep,
+ sizeof(param_sleep));
+
+ /* Handle debug printing of the sleep set request. */
+ rpm_regulator_req(regulator, RPM_SET_SLEEP, send_sleep);
+ if (send_sleep)
+ rpm_vreg->aggr_req_sleep.modified = 0;
+
+ /*
+ * Loop over all requests for this regulator to update the valid and
+ * modified values for use in future aggregation.
+ */
+ list_for_each_entry(reg, &rpm_vreg->reg_list, list) {
+ reg->req.valid |= reg->req.modified;
+ reg->req.modified = 0;
+ }
+
+ return rc;
+}
+
+static int rpm_vreg_is_enabled(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+
+ return reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
+}
+
+static int rpm_vreg_enable(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc;
+ u32 prev_enable;
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
+ RPM_VREG_SET_PARAM(reg, ENABLE, 1);
+ rc = rpm_vreg_aggregate_requests(reg);
+ if (rc) {
+ vreg_err(reg, "enable failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static int rpm_vreg_disable(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc;
+ u32 prev_enable;
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_enable = reg->req.param[RPM_REGULATOR_PARAM_ENABLE];
+ RPM_VREG_SET_PARAM(reg, ENABLE, 0);
+ rc = rpm_vreg_aggregate_requests(reg);
+ if (rc) {
+ vreg_err(reg, "enable failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, ENABLE, prev_enable);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static int rpm_vreg_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ u32 prev_voltage;
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_voltage = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE];
+ RPM_VREG_SET_PARAM(reg, VOLTAGE, min_uV);
+
+ /*
+ * Only send a new voltage if the regulator is currently enabled or
+ * if the regulator has been configured to always send voltage updates.
+ */
+ if (reg->always_send_voltage
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
+ rc = rpm_vreg_aggregate_requests(reg);
+
+ if (rc) {
+ vreg_err(reg, "set voltage failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, VOLTAGE, prev_voltage);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static int rpm_vreg_get_voltage(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int uV;
+
+ uV = reg->req.param[RPM_REGULATOR_PARAM_VOLTAGE];
+ if (uV == 0)
+ uV = VOLTAGE_UNKNOWN;
+
+ return uV;
+}
+
+static int rpm_vreg_set_voltage_corner(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ int corner;
+ u32 prev_corner;
+
+ /*
+ * Translate from values which work as inputs in the
+ * regulator_set_voltage function to the actual corner values
+ * sent to the RPM.
+ */
+ corner = min_uV - RPM_REGULATOR_CORNER_NONE;
+
+ if (corner < params[RPM_REGULATOR_PARAM_CORNER].min
+ || corner > params[RPM_REGULATOR_PARAM_CORNER].max) {
+ vreg_err(reg, "corner=%d is not within allowed range: [%u, %u]\n",
+ corner, params[RPM_REGULATOR_PARAM_CORNER].min,
+ params[RPM_REGULATOR_PARAM_CORNER].max);
+ return -EINVAL;
+ }
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_corner = reg->req.param[RPM_REGULATOR_PARAM_CORNER];
+ RPM_VREG_SET_PARAM(reg, CORNER, corner);
+
+ /*
+ * Only send a new voltage corner if the regulator is currently enabled
+ * or if the regulator has been configured to always send voltage
+ * updates.
+ */
+ if (reg->always_send_voltage
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
+ rc = rpm_vreg_aggregate_requests(reg);
+
+ if (rc) {
+ vreg_err(reg, "set voltage corner failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, CORNER, prev_corner);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static int rpm_vreg_get_voltage_corner(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+
+ return reg->req.param[RPM_REGULATOR_PARAM_CORNER]
+ + RPM_REGULATOR_CORNER_NONE;
+}
+
+static int rpm_vreg_set_voltage_floor_corner(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ int corner;
+ u32 prev_corner;
+
+ /*
+ * Translate from values which work as inputs in the
+ * regulator_set_voltage function to the actual corner values
+ * sent to the RPM.
+ */
+ corner = min_uV - RPM_REGULATOR_CORNER_NONE;
+
+ if (corner < params[RPM_REGULATOR_PARAM_FLOOR_CORNER].min
+ || corner > params[RPM_REGULATOR_PARAM_FLOOR_CORNER].max) {
+ vreg_err(reg, "corner=%d is not within allowed range: [%u, %u]\n",
+ corner, params[RPM_REGULATOR_PARAM_FLOOR_CORNER].min,
+ params[RPM_REGULATOR_PARAM_FLOOR_CORNER].max);
+ return -EINVAL;
+ }
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_corner = reg->req.param[RPM_REGULATOR_PARAM_FLOOR_CORNER];
+ RPM_VREG_SET_PARAM(reg, FLOOR_CORNER, corner);
+
+ /*
+ * Only send a new voltage floor corner if the regulator is currently
+ * enabled or if the regulator has been configured to always send
+ * voltage updates.
+ */
+ if (reg->always_send_voltage
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
+ rc = rpm_vreg_aggregate_requests(reg);
+
+ if (rc) {
+ vreg_err(reg, "set voltage corner failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, FLOOR_CORNER, prev_corner);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static int rpm_vreg_get_voltage_floor_corner(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+
+ return reg->req.param[RPM_REGULATOR_PARAM_FLOOR_CORNER]
+ + RPM_REGULATOR_CORNER_NONE;
+}
+
+static int rpm_vreg_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ u32 prev_current;
+ int prev_uA;
+
+ rpm_vreg_lock(reg->rpm_vreg);
+
+ prev_current = reg->req.param[RPM_REGULATOR_PARAM_CURRENT];
+ prev_uA = MILLI_TO_MICRO(prev_current);
+
+ if (mode == REGULATOR_MODE_NORMAL) {
+ /* Make sure that request current is in HPM range. */
+ if (prev_uA < rpm_vreg_hpm_min_uA(reg->rpm_vreg))
+ RPM_VREG_SET_PARAM(reg, CURRENT,
+ MICRO_TO_MILLI(rpm_vreg_hpm_min_uA(reg->rpm_vreg)));
+ } else if (REGULATOR_MODE_IDLE) {
+ /* Make sure that request current is in LPM range. */
+ if (prev_uA > rpm_vreg_lpm_max_uA(reg->rpm_vreg))
+ RPM_VREG_SET_PARAM(reg, CURRENT,
+ MICRO_TO_MILLI(rpm_vreg_lpm_max_uA(reg->rpm_vreg)));
+ } else {
+ vreg_err(reg, "invalid mode: %u\n", mode);
+ rpm_vreg_unlock(reg->rpm_vreg);
+ return -EINVAL;
+ }
+
+ /*
+ * Only send a new load current value if the regulator is currently
+ * enabled or if the regulator has been configured to always send
+ * current updates.
+ */
+ if (reg->always_send_current
+ || rpm_vreg_active_or_sleep_enabled(reg->rpm_vreg)
+ || rpm_vreg_shared_active_or_sleep_enabled_valid(reg->rpm_vreg))
+ rc = rpm_vreg_aggregate_requests(reg);
+
+ if (rc) {
+ vreg_err(reg, "set mode failed, rc=%d", rc);
+ RPM_VREG_SET_PARAM(reg, CURRENT, prev_current);
+ }
+
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return rc;
+}
+
+static unsigned int rpm_vreg_get_mode(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+
+ return (reg->req.param[RPM_REGULATOR_PARAM_CURRENT]
+ >= MICRO_TO_MILLI(reg->rpm_vreg->hpm_min_load))
+ ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static unsigned int rpm_vreg_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+ u32 load_mA;
+
+ load_uA += reg->system_load;
+
+ load_mA = MICRO_TO_MILLI(load_uA);
+ if (load_mA > params[RPM_REGULATOR_PARAM_CURRENT].max)
+ load_mA = params[RPM_REGULATOR_PARAM_CURRENT].max;
+
+ rpm_vreg_lock(reg->rpm_vreg);
+ RPM_VREG_SET_PARAM(reg, CURRENT, load_mA);
+ rpm_vreg_unlock(reg->rpm_vreg);
+
+ return (load_uA >= reg->rpm_vreg->hpm_min_load)
+ ? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int rpm_vreg_enable_time(struct regulator_dev *rdev)
+{
+ struct rpm_regulator *reg = rdev_get_drvdata(rdev);
+
+ return reg->rpm_vreg->enable_time;
+}
+
+/**
+ * rpm_regulator_get() - lookup and obtain a handle to an RPM regulator
+ * @dev: device for regulator consumer
+ * @supply: supply name
+ *
+ * Returns a struct rpm_regulator corresponding to the regulator producer,
+ * or ERR_PTR() containing errno.
+ *
+ * This function may only be called from nonatomic context.
+ */
+struct rpm_regulator *rpm_regulator_get(struct device *dev, const char *supply)
+{
+ struct rpm_regulator *framework_reg;
+ struct rpm_regulator *priv_reg = NULL;
+ struct regulator *regulator;
+ struct rpm_vreg *rpm_vreg;
+
+ regulator = regulator_get(dev, supply);
+ if (IS_ERR(regulator)) {
+ pr_err("could not find regulator for: dev=%s, supply=%s, rc=%ld\n",
+ (dev ? dev_name(dev) : ""), (supply ? supply : ""),
+ PTR_ERR(regulator));
+ return ERR_CAST(regulator);
+ }
+
+ framework_reg = regulator_get_drvdata(regulator);
+ if (framework_reg == NULL) {
+ pr_err("regulator structure not found.\n");
+ regulator_put(regulator);
+ return ERR_PTR(-ENODEV);
+ }
+ regulator_put(regulator);
+
+ rpm_vreg = framework_reg->rpm_vreg;
+
+ priv_reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL);
+ if (priv_reg == NULL) {
+ vreg_err(framework_reg,
+ "could not allocate memory for regulator\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * Allocate a regulator_dev struct so that framework callback functions
+ * can be called from the private API functions.
+ */
+ priv_reg->rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
+ if (priv_reg->rdev == NULL) {
+ vreg_err(framework_reg,
+ "could not allocate memory for regulator_dev\n");
+ kfree(priv_reg);
+ return ERR_PTR(-ENOMEM);
+ }
+ priv_reg->rdev->reg_data = priv_reg;
+ priv_reg->rpm_vreg = rpm_vreg;
+ priv_reg->rdesc.name = framework_reg->rdesc.name;
+ priv_reg->rdesc.ops = framework_reg->rdesc.ops;
+ priv_reg->set_active = framework_reg->set_active;
+ priv_reg->set_sleep = framework_reg->set_sleep;
+ priv_reg->min_uV = framework_reg->min_uV;
+ priv_reg->max_uV = framework_reg->max_uV;
+ priv_reg->system_load = framework_reg->system_load;
+
+ might_sleep_if(!rpm_vreg->allow_atomic);
+ rpm_vreg_lock(rpm_vreg);
+ list_add(&priv_reg->list, &rpm_vreg->reg_list);
+ rpm_vreg_unlock(rpm_vreg);
+
+ return priv_reg;
+}
+EXPORT_SYMBOL(rpm_regulator_get);
+
+static int rpm_regulator_check_input(struct rpm_regulator *regulator)
+{
+ if (IS_ERR_OR_NULL(regulator) || regulator->rpm_vreg == NULL) {
+ pr_err("invalid rpm_regulator pointer\n");
+ return -EINVAL;
+ }
+
+ might_sleep_if(!regulator->rpm_vreg->allow_atomic);
+
+ return 0;
+}
+
+/**
+ * rpm_regulator_put() - free the RPM regulator handle
+ * @regulator: RPM regulator handle
+ *
+ * Parameter reaggregation does not take place when rpm_regulator_put is called.
+ * Therefore, regulator enable state and voltage must be configured
+ * appropriately before calling rpm_regulator_put.
+ *
+ * This function may be called from either atomic or nonatomic context. If this
+ * function is called from atomic context, then the regulator being operated on
+ * must be configured via device tree with qcom,allow-atomic == 1.
+ */
+void rpm_regulator_put(struct rpm_regulator *regulator)
+{
+ struct rpm_vreg *rpm_vreg;
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return;
+
+ rpm_vreg = regulator->rpm_vreg;
+
+ might_sleep_if(!rpm_vreg->allow_atomic);
+ rpm_vreg_lock(rpm_vreg);
+ list_del(&regulator->list);
+ rpm_vreg_unlock(rpm_vreg);
+
+ kfree(regulator->rdev);
+ kfree(regulator);
+}
+EXPORT_SYMBOL(rpm_regulator_put);
+
+/**
+ * rpm_regulator_enable() - enable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * This function may be called from either atomic or nonatomic context. If this
+ * function is called from atomic context, then the regulator being operated on
+ * must be configured via device tree with qcom,allow-atomic == 1.
+ */
+int rpm_regulator_enable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ return rpm_vreg_enable(regulator->rdev);
+}
+EXPORT_SYMBOL(rpm_regulator_enable);
+
+/**
+ * rpm_regulator_disable() - disable regulator output
+ * @regulator: RPM regulator handle
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The enable state of the regulator is determined by aggregating the requests
+ * of all consumers. Therefore, it is possible that the regulator will remain
+ * enabled even after rpm_regulator_disable is called.
+ *
+ * This function may be called from either atomic or nonatomic context. If this
+ * function is called from atomic context, then the regulator being operated on
+ * must be configured via device tree with qcom,allow-atomic == 1.
+ */
+int rpm_regulator_disable(struct rpm_regulator *regulator)
+{
+ int rc = rpm_regulator_check_input(regulator);
+
+ if (rc)
+ return rc;
+
+ return rpm_vreg_disable(regulator->rdev);
+}
+EXPORT_SYMBOL(rpm_regulator_disable);
+
+/**
+ * rpm_regulator_set_voltage() - set regulator output voltage
+ * @regulator: RPM regulator handle
+ * @min_uV: minimum required voltage in uV
+ * @max_uV: maximum acceptable voltage in uV
+ *
+ * Sets a voltage regulator to the desired output voltage. This can be set
+ * while the regulator is disabled or enabled. If the regulator is enabled then
+ * the voltage will change to the new value immediately; otherwise, if the
+ * regulator is disabled, then the regulator will output at the new voltage when
+ * enabled.
+ *
+ * The min_uV to max_uV voltage range requested must intersect with the
+ * voltage constraint range configured for the regulator.
+ *
+ * Returns 0 on success or errno on failure.
+ *
+ * The final voltage value that is sent to the RPM is aggregated based upon the
+ * values requested by all consumers of the regulator. This corresponds to the
+ * maximum min_uV value.
+ *
+ * This function may be called from either atomic or nonatomic context. If this
+ * function is called from atomic context, then the regulator being operated on
+ * must be configured via device tree with qcom,allow-atomic == 1.
+ */
+int rpm_regulator_set_voltage(struct rpm_regulator *regulator, int min_uV,
+ int max_uV)
+{
+ int rc = rpm_regulator_check_input(regulator);
+ int uV = min_uV;
+
+ if (rc)
+ return rc;
+
+ if (regulator->rpm_vreg->regulator_type == RPM_REGULATOR_TYPE_VS) {
+ vreg_err(regulator, "unsupported regulator type: %d\n",
+ regulator->rpm_vreg->regulator_type);
+ return -EINVAL;
+ }
+
+ if (min_uV > max_uV) {
+ vreg_err(regulator, "min_uV=%d must be less than max_uV=%d\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ if (uV < regulator->min_uV && max_uV >= regulator->min_uV)
+ uV = regulator->min_uV;
+
+ if (uV < regulator->min_uV || uV > regulator->max_uV) {
+ vreg_err(regulator,
+ "request v=[%d, %d] is outside allowed v=[%d, %d]\n",
+ min_uV, max_uV, regulator->min_uV, regulator->max_uV);
+ return -EINVAL;
+ }
+
+ return regulator->rdesc.ops->set_voltage(regulator->rdev, uV, uV, NULL);
+}
+EXPORT_SYMBOL(rpm_regulator_set_voltage);
+
+/**
+ * rpm_regulator_set_mode() - set regulator operating mode
+ * @regulator: RPM regulator handle
+ * @mode: operating mode requested for the regulator
+ *
+ * Requests that the mode of the regulator be set to the mode specified. This
+ * parameter is aggregated using a max function such that AUTO < IPEAK < HPM.
+ *
+ * Returns 0 on success or errno on failure.
+ */
+int rpm_regulator_set_mode(struct rpm_regulator *regulator,
+ enum rpm_regulator_mode mode)
+{
+ int index = 0;
+ u32 new_mode, prev_mode;
+ int rc;
+
+ rc = rpm_regulator_check_input(regulator);
+ if (rc)
+ return rc;
+
+ if (mode < 0 || mode >= ARRAY_SIZE(mode_mapping)) {
+ vreg_err(regulator, "invalid mode requested: %d\n", mode);
+ return -EINVAL;
+ }
+
+ switch (regulator->rpm_vreg->regulator_type) {
+ case RPM_REGULATOR_TYPE_SMPS:
+ index = RPM_REGULATOR_PARAM_MODE_SMPS;
+ new_mode = mode_mapping[mode].smps_mode;
+ break;
+ case RPM_REGULATOR_TYPE_LDO:
+ index = RPM_REGULATOR_PARAM_MODE_LDO;
+ new_mode = mode_mapping[mode].ldo_mode;
+ break;
+ default:
+ vreg_err(regulator, "unsupported regulator type: %d\n",
+ regulator->rpm_vreg->regulator_type);
+ return -EINVAL;
+ };
+
+ if (new_mode < params[index].min || new_mode > params[index].max) {
+ vreg_err(regulator, "invalid mode requested: %d for type: %d\n",
+ mode, regulator->rpm_vreg->regulator_type);
+ return -EINVAL;
+ }
+
+ rpm_vreg_lock(regulator->rpm_vreg);
+
+ prev_mode = regulator->req.param[index];
+ regulator->req.param[index] = new_mode;
+ regulator->req.modified |= BIT(index);
+
+ rc = rpm_vreg_aggregate_requests(regulator);
+ if (rc) {
+ vreg_err(regulator, "set mode failed, rc=%d", rc);
+ regulator->req.param[index] = prev_mode;
+ }
+
+ rpm_vreg_unlock(regulator->rpm_vreg);
+
+ return rc;
+}
+EXPORT_SYMBOL(rpm_regulator_set_mode);
+
+static struct regulator_ops ldo_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage,
+ .get_voltage = rpm_vreg_get_voltage,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops ldo_corner_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage_corner,
+ .get_voltage = rpm_vreg_get_voltage_corner,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops ldo_floor_corner_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage_floor_corner,
+ .get_voltage = rpm_vreg_get_voltage_floor_corner,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops smps_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage,
+ .get_voltage = rpm_vreg_get_voltage,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops smps_corner_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage_corner,
+ .get_voltage = rpm_vreg_get_voltage_corner,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops smps_floor_corner_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage_floor_corner,
+ .get_voltage = rpm_vreg_get_voltage_floor_corner,
+ .set_mode = rpm_vreg_set_mode,
+ .get_mode = rpm_vreg_get_mode,
+ .get_optimum_mode = rpm_vreg_get_optimum_mode,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops switch_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops ncp_ops = {
+ .enable = rpm_vreg_enable,
+ .disable = rpm_vreg_disable,
+ .is_enabled = rpm_vreg_is_enabled,
+ .set_voltage = rpm_vreg_set_voltage,
+ .get_voltage = rpm_vreg_get_voltage,
+ .enable_time = rpm_vreg_enable_time,
+};
+
+static struct regulator_ops *vreg_ops[] = {
+ [RPM_REGULATOR_TYPE_LDO] = &ldo_ops,
+ [RPM_REGULATOR_TYPE_SMPS] = &smps_ops,
+ [RPM_REGULATOR_TYPE_VS] = &switch_ops,
+ [RPM_REGULATOR_TYPE_NCP] = &ncp_ops,
+};
+
+static int rpm_vreg_device_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpm_regulator *reg;
+ struct rpm_vreg *rpm_vreg;
+
+ reg = platform_get_drvdata(pdev);
+ if (reg) {
+ rpm_vreg = reg->rpm_vreg;
+ rpm_vreg_lock(rpm_vreg);
+ regulator_unregister(reg->rdev);
+ list_del(&reg->list);
+ kfree(reg);
+ rpm_vreg_unlock(rpm_vreg);
+ } else {
+ dev_err(dev, "%s: drvdata missing\n", __func__);
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static int rpm_vreg_resource_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpm_regulator *reg, *reg_temp;
+ struct rpm_vreg *rpm_vreg;
+
+ rpm_vreg = platform_get_drvdata(pdev);
+ if (rpm_vreg) {
+ rpm_vreg_lock(rpm_vreg);
+ list_for_each_entry_safe(reg, reg_temp, &rpm_vreg->reg_list,
+ list) {
+ /* Only touch data for private consumers. */
+ if (reg->rdev->desc == NULL) {
+ list_del(&reg->list);
+ kfree(reg->rdev);
+ kfree(reg);
+ } else {
+ dev_err(dev, "%s: not all child devices have been removed\n",
+ __func__);
+ }
+ }
+ rpm_vreg_unlock(rpm_vreg);
+
+ msm_rpm_free_request(rpm_vreg->handle_active);
+ msm_rpm_free_request(rpm_vreg->handle_sleep);
+
+ kfree(rpm_vreg);
+ } else {
+ dev_err(dev, "%s: drvdata missing\n", __func__);
+ return -EINVAL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/*
+ * This probe is called for child rpm-regulator devices which have
+ * properties which are required to configure individual regulator
+ * framework regulators for a given RPM regulator resource.
+ */
+static int rpm_vreg_device_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct regulator_init_data *init_data;
+ struct rpm_vreg *rpm_vreg;
+ struct rpm_regulator *reg;
+ struct regulator_config reg_config = {};
+ int rc = 0;
+ int i, regulator_type;
+ u32 val;
+
+ if (!dev->of_node) {
+ dev_err(dev, "%s: device tree information missing\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdev->dev.parent == NULL) {
+ dev_err(dev, "%s: parent device missing\n", __func__);
+ return -ENODEV;
+ }
+
+ rpm_vreg = dev_get_drvdata(pdev->dev.parent);
+ if (rpm_vreg == NULL) {
+ dev_err(dev, "%s: rpm_vreg not found in parent device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ reg = kzalloc(sizeof(struct rpm_regulator), GFP_KERNEL);
+ if (reg == NULL) {
+ dev_err(dev, "%s: could not allocate memory for reg\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ regulator_type = rpm_vreg->regulator_type;
+ reg->rpm_vreg = rpm_vreg;
+ reg->rdesc.ops = vreg_ops[regulator_type];
+ reg->rdesc.owner = THIS_MODULE;
+ reg->rdesc.type = REGULATOR_VOLTAGE;
+
+ /*
+ * Switch to voltage corner regulator ops if qcom,use-voltage-corner
+ * is specified in the device node (SMPS and LDO only).
+ */
+ if (of_property_read_bool(node, "qcom,use-voltage-corner")) {
+ if (of_property_read_bool(node,
+ "qcom,use-voltage-floor-corner")) {
+ dev_err(dev, "%s: invalid properties: both qcom,use-voltage-corner and qcom,use-voltage-floor-corner specified\n",
+ __func__);
+ goto fail_free_reg;
+ }
+
+ if (regulator_type == RPM_REGULATOR_TYPE_SMPS)
+ reg->rdesc.ops = &smps_corner_ops;
+ else if (regulator_type == RPM_REGULATOR_TYPE_LDO)
+ reg->rdesc.ops = &ldo_corner_ops;
+ } else if (of_property_read_bool(node,
+ "qcom,use-voltage-floor-corner")) {
+ if (regulator_type == RPM_REGULATOR_TYPE_SMPS)
+ reg->rdesc.ops = &smps_floor_corner_ops;
+ else if (regulator_type == RPM_REGULATOR_TYPE_LDO)
+ reg->rdesc.ops = &ldo_floor_corner_ops;
+ }
+
+ reg->always_send_voltage
+ = of_property_read_bool(node, "qcom,always-send-voltage");
+ reg->always_send_current
+ = of_property_read_bool(node, "qcom,always-send-current");
+
+ if (regulator_type == RPM_REGULATOR_TYPE_VS)
+ reg->rdesc.n_voltages = 0;
+ else
+ reg->rdesc.n_voltages = 2;
+
+ rc = of_property_read_u32(node, "qcom,set", &val);
+ if (rc) {
+ dev_err(dev, "%s: sleep set and/or active set must be configured via qcom,set property, rc=%d\n",
+ __func__, rc);
+ goto fail_free_reg;
+ } else if (!(val & RPM_SET_CONFIG_BOTH)) {
+ dev_err(dev, "%s: qcom,set=%u property is invalid\n", __func__,
+ val);
+ rc = -EINVAL;
+ goto fail_free_reg;
+ }
+
+ reg->set_active = !!(val & RPM_SET_CONFIG_ACTIVE);
+ reg->set_sleep = !!(val & RPM_SET_CONFIG_SLEEP);
+
+ init_data = of_get_regulator_init_data(dev, node);
+ if (init_data == NULL) {
+ dev_err(dev, "%s: unable to allocate memory\n", __func__);
+ rc = -ENOMEM;
+ goto fail_free_reg;
+ }
+ if (init_data->constraints.name == NULL) {
+ dev_err(dev, "%s: regulator name not specified\n", __func__);
+ rc = -EINVAL;
+ goto fail_free_reg;
+ }
+
+ init_data->constraints.input_uV = init_data->constraints.max_uV;
+
+ if (of_get_property(node, "parent-supply", NULL))
+ init_data->supply_regulator = "parent";
+
+ /*
+ * Fill in ops and mode masks based on callbacks specified for
+ * this type of regulator.
+ */
+ if (reg->rdesc.ops->enable)
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_STATUS;
+ if (reg->rdesc.ops->get_voltage)
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_VOLTAGE;
+ if (reg->rdesc.ops->get_mode) {
+ init_data->constraints.valid_ops_mask
+ |= REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS;
+ init_data->constraints.valid_modes_mask
+ |= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+ }
+
+ reg->rdesc.name = init_data->constraints.name;
+ reg->min_uV = init_data->constraints.min_uV;
+ reg->max_uV = init_data->constraints.max_uV;
+
+ /* Initialize the param array based on optional properties. */
+ for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++) {
+ rc = of_property_read_u32(node, params[i].property_name, &val);
+ if (rc == 0) {
+ if (params[i].supported_regulator_types
+ & BIT(regulator_type)) {
+ if (val < params[i].min
+ || val > params[i].max) {
+ pr_warn("%s: device tree property: %s=%u is outsided allowed range [%u, %u]\n",
+ reg->rdesc.name,
+ params[i].property_name, val,
+ params[i].min, params[i].max);
+ continue;
+ }
+ reg->req.param[i] = val;
+ reg->req.modified |= BIT(i);
+ } else {
+ pr_warn("%s: regulator type=%d does not support device tree property: %s\n",
+ reg->rdesc.name, regulator_type,
+ params[i].property_name);
+ }
+ }
+ }
+
+ of_property_read_u32(node, "qcom,system-load", &reg->system_load);
+
+ rpm_vreg_lock(rpm_vreg);
+ list_add(&reg->list, &rpm_vreg->reg_list);
+ rpm_vreg_unlock(rpm_vreg);
+
+ reg_config.dev = dev;
+ reg_config.init_data = init_data;
+ reg_config.of_node = node;
+ reg_config.driver_data = reg;
+ reg->rdev = regulator_register(&reg->rdesc, &reg_config);
+ if (IS_ERR(reg->rdev)) {
+ rc = PTR_ERR(reg->rdev);
+ reg->rdev = NULL;
+ pr_err("regulator_register failed: %s, rc=%d\n",
+ reg->rdesc.name, rc);
+ goto fail_remove_from_list;
+ }
+
+ platform_set_drvdata(pdev, reg);
+
+ pr_debug("successfully probed: %s\n", reg->rdesc.name);
+
+ return 0;
+
+fail_remove_from_list:
+ rpm_vreg_lock(rpm_vreg);
+ list_del(&reg->list);
+ rpm_vreg_unlock(rpm_vreg);
+
+fail_free_reg:
+ kfree(reg);
+ return rc;
+}
+
+/*
+ * This probe is called for parent rpm-regulator devices which have
+ * properties which are required to identify a given RPM resource.
+ */
+static int rpm_vreg_resource_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = dev->of_node;
+ struct rpm_vreg *rpm_vreg;
+ int val = 0;
+ u32 resource_type;
+ int rc;
+
+ if (!dev->of_node) {
+ dev_err(dev, "%s: device tree information missing\n", __func__);
+ return -ENODEV;
+ }
+
+ /* Create new rpm_vreg entry. */
+ rpm_vreg = kzalloc(sizeof(struct rpm_vreg), GFP_KERNEL);
+ if (rpm_vreg == NULL) {
+ dev_err(dev, "%s: could not allocate memory for vreg\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* Required device tree properties: */
+ rc = of_property_read_string(node, "qcom,resource-name",
+ &rpm_vreg->resource_name);
+ if (rc) {
+ dev_err(dev, "%s: qcom,resource-name missing in DT node\n",
+ __func__);
+ goto fail_free_vreg;
+ }
+ resource_type = rpm_vreg_string_to_int(rpm_vreg->resource_name);
+
+ rc = of_property_read_u32(node, "qcom,resource-id",
+ &rpm_vreg->resource_id);
+ if (rc) {
+ dev_err(dev, "%s: qcom,resource-id missing in DT node\n",
+ __func__);
+ goto fail_free_vreg;
+ }
+
+ rc = of_property_read_u32(node, "qcom,regulator-type",
+ &rpm_vreg->regulator_type);
+ if (rc) {
+ dev_err(dev, "%s: qcom,regulator-type missing in DT node\n",
+ __func__);
+ goto fail_free_vreg;
+ }
+
+ if ((rpm_vreg->regulator_type < 0)
+ || (rpm_vreg->regulator_type >= RPM_REGULATOR_TYPE_MAX)) {
+ dev_err(dev, "%s: invalid regulator type: %d\n", __func__,
+ rpm_vreg->regulator_type);
+ rc = -EINVAL;
+ goto fail_free_vreg;
+ }
+
+ /* Optional device tree properties: */
+ of_property_read_u32(node, "qcom,allow-atomic", &val);
+ rpm_vreg->allow_atomic = !!val;
+ of_property_read_u32(node, "qcom,enable-time", &rpm_vreg->enable_time);
+ of_property_read_u32(node, "qcom,hpm-min-load",
+ &rpm_vreg->hpm_min_load);
+ rpm_vreg->apps_only = of_property_read_bool(node, "qcom,apps-only");
+
+ rpm_vreg->handle_active = msm_rpm_create_request(RPM_SET_ACTIVE,
+ resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX);
+ if (rpm_vreg->handle_active == NULL
+ || IS_ERR(rpm_vreg->handle_active)) {
+ rc = PTR_ERR(rpm_vreg->handle_active);
+ dev_err(dev, "%s: failed to create active RPM handle, rc=%d\n",
+ __func__, rc);
+ goto fail_free_vreg;
+ }
+
+ rpm_vreg->handle_sleep = msm_rpm_create_request(RPM_SET_SLEEP,
+ resource_type, rpm_vreg->resource_id, RPM_REGULATOR_PARAM_MAX);
+ if (rpm_vreg->handle_sleep == NULL || IS_ERR(rpm_vreg->handle_sleep)) {
+ rc = PTR_ERR(rpm_vreg->handle_sleep);
+ dev_err(dev, "%s: failed to create sleep RPM handle, rc=%d\n",
+ __func__, rc);
+ goto fail_free_handle_active;
+ }
+
+ INIT_LIST_HEAD(&rpm_vreg->reg_list);
+
+ if (rpm_vreg->allow_atomic)
+ spin_lock_init(&rpm_vreg->slock);
+ else
+ mutex_init(&rpm_vreg->mlock);
+
+ platform_set_drvdata(pdev, rpm_vreg);
+
+ rc = of_platform_populate(node, NULL, NULL, dev);
+ if (rc) {
+ dev_err(dev, "%s: failed to add child nodes, rc=%d\n", __func__,
+ rc);
+ goto fail_unset_drvdata;
+ }
+
+ pr_debug("successfully probed: %s (%08X) %u\n", rpm_vreg->resource_name,
+ resource_type, rpm_vreg->resource_id);
+
+ return rc;
+
+fail_unset_drvdata:
+ platform_set_drvdata(pdev, NULL);
+ msm_rpm_free_request(rpm_vreg->handle_sleep);
+
+fail_free_handle_active:
+ msm_rpm_free_request(rpm_vreg->handle_active);
+
+fail_free_vreg:
+ kfree(rpm_vreg);
+
+ return rc;
+}
+
+static struct of_device_id rpm_vreg_match_table_device[] = {
+ { .compatible = "qcom,rpm-smd-regulator", },
+ {}
+};
+
+static struct of_device_id rpm_vreg_match_table_resource[] = {
+ { .compatible = "qcom,rpm-smd-regulator-resource", },
+ {}
+};
+
+static struct platform_driver rpm_vreg_device_driver = {
+ .probe = rpm_vreg_device_probe,
+ .remove = rpm_vreg_device_remove,
+ .driver = {
+ .name = "qcom,rpm-smd-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = rpm_vreg_match_table_device,
+ },
+};
+
+static struct platform_driver rpm_vreg_resource_driver = {
+ .probe = rpm_vreg_resource_probe,
+ .remove = rpm_vreg_resource_remove,
+ .driver = {
+ .name = "qcom,rpm-smd-regulator-resource",
+ .owner = THIS_MODULE,
+ .of_match_table = rpm_vreg_match_table_resource,
+ },
+};
+
+/**
+ * rpm_smd_regulator_driver_init() - initialize the RPM SMD regulator drivers
+ *
+ * This function registers the RPM SMD regulator platform drivers.
+ *
+ * Returns 0 on success or errno on failure.
+ */
+int __init rpm_smd_regulator_driver_init(void)
+{
+ static bool initialized;
+ int i, rc;
+
+ if (initialized)
+ return 0;
+ else
+ initialized = true;
+
+ /* Store parameter string names as integers */
+ for (i = 0; i < RPM_REGULATOR_PARAM_MAX; i++)
+ params[i].key = rpm_vreg_string_to_int(params[i].name);
+
+ rc = platform_driver_register(&rpm_vreg_device_driver);
+ if (rc)
+ return rc;
+
+ return platform_driver_register(&rpm_vreg_resource_driver);
+}
+EXPORT_SYMBOL(rpm_smd_regulator_driver_init);
+
+static void __exit rpm_vreg_exit(void)
+{
+ platform_driver_unregister(&rpm_vreg_device_driver);
+ platform_driver_unregister(&rpm_vreg_resource_driver);
+}
+
+module_init(rpm_smd_regulator_driver_init);
+module_exit(rpm_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM RPM SMD regulator driver");
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
index 876482de73aa..11a593cb9161 100644
--- a/drivers/rtc/qpnp-rtc.c
+++ b/drivers/rtc/qpnp-rtc.c
@@ -355,6 +355,7 @@ qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
unsigned long irq_flags;
struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
u8 ctrl_reg;
+ u8 value[4] = {0};
spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
ctrl_reg = rtc_dd->alarm_ctrl_reg1;
@@ -370,6 +371,15 @@ qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+ /* Clear Alarm register */
+ if (!enabled) {
+ rc = qpnp_write_wrapper(rtc_dd, value,
+ rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+ NUM_8_BIT_RTC_REGS);
+ if (rc)
+ dev_err(dev, "Clear ALARM value reg failed\n");
+ }
+
rtc_rw_fail:
spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
return rc;
diff --git a/drivers/scsi/ufs/debugfs.c b/drivers/scsi/ufs/debugfs.c
index 9664120533f6..57110039eaff 100644
--- a/drivers/scsi/ufs/debugfs.c
+++ b/drivers/scsi/ufs/debugfs.c
@@ -302,8 +302,10 @@ static int ufsdbg_host_regs_show(struct seq_file *file, void *data)
{
struct ufs_hba *hba = (struct ufs_hba *)file->private;
+ ufshcd_hold(hba, false);
ufsdbg_pr_buf_to_std(file, hba->mmio_base, UFSHCI_REG_SPACE_SIZE,
"host regs");
+ ufshcd_release(hba);
return 0;
}
@@ -408,6 +410,8 @@ static int ufsdbg_show_hba_show(struct seq_file *file, void *data)
hba->auto_bkops_enabled);
seq_printf(file, "hba->ufshcd_state = 0x%x\n", hba->ufshcd_state);
+ seq_printf(file, "hba->clk_gating.state = 0x%x\n",
+ hba->clk_gating.state);
seq_printf(file, "hba->eh_flags = 0x%x\n", hba->eh_flags);
seq_printf(file, "hba->intr_mask = 0x%x\n", hba->intr_mask);
seq_printf(file, "hba->ee_ctrl_mask = 0x%x\n", hba->ee_ctrl_mask);
diff --git a/drivers/scsi/ufs/ufs-msm.c b/drivers/scsi/ufs/ufs-msm.c
index 2827ec543773..82520b1e865b 100644
--- a/drivers/scsi/ufs/ufs-msm.c
+++ b/drivers/scsi/ufs/ufs-msm.c
@@ -32,7 +32,7 @@
#define FAST 2
#define UFS_MSM_LIMIT_NUM_LANES_RX 2
-#define UFS_MSM_LIMIT_NUM_LANES_TX 1
+#define UFS_MSM_LIMIT_NUM_LANES_TX 2
#define UFS_MSM_LIMIT_HSGEAR_RX UFS_HS_G2
#define UFS_MSM_LIMIT_HSGEAR_TX UFS_HS_G2
#define UFS_MSM_LIMIT_PWMGEAR_RX UFS_PWM_G4
@@ -1116,14 +1116,8 @@ static int msm_ufs_phy_power_on(struct msm_ufs_phy *phy)
if (err)
goto out_disable_pll;
- err = msm_ufs_enable_phy_iface_clk(phy);
- if (err)
- goto out_disable_ref;
-
goto out;
-out_disable_ref:
- msm_ufs_disable_phy_ref_clk(phy);
out_disable_pll:
msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll);
out_disable_phy:
@@ -1137,7 +1131,6 @@ static int msm_ufs_phy_power_off(struct msm_ufs_phy *phy)
writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
mb();
- msm_ufs_disable_phy_iface_clk(phy);
msm_ufs_disable_phy_ref_clk(phy);
msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll);
@@ -1212,6 +1205,108 @@ static int msm_ufs_hce_enable_notify(struct ufs_hba *hba, bool status)
return err;
}
+/**
+ * Returns non-zero for success (which rate of core_clk) and 0
+ * in case of a failure
+ */
+static unsigned long
+msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs, u32 rate)
+{
+ struct ufs_clk_info *clki;
+ u32 core_clk_period_in_ns;
+ u32 tx_clk_cycles_per_us = 0;
+ unsigned long core_clk_rate = 0;
+
+ static u32 pwm_fr_table[][2] = {
+ {UFS_PWM_G1, 0x1},
+ {UFS_PWM_G2, 0x1},
+ {UFS_PWM_G3, 0x1},
+ {UFS_PWM_G4, 0x1},
+ };
+
+ static u32 hs_fr_table_rA[][2] = {
+ {UFS_HS_G1, 0x1F},
+ {UFS_HS_G2, 0x3e},
+ };
+
+ static u32 hs_fr_table_rB[][2] = {
+ {UFS_HS_G1, 0x24},
+ {UFS_HS_G2, 0x49},
+ };
+
+ if (gear == 0) {
+ dev_err(hba->dev, "%s: invalid gear = %d\n", __func__, gear);
+ goto out_error;
+ }
+
+ list_for_each_entry(clki, &hba->clk_list_head, list) {
+ if (!strcmp(clki->name, "core_clk"))
+ core_clk_rate = clk_get_rate(clki->clk);
+ }
+
+ /* If frequency is smaller than 1MHz, set to 1MHz */
+ if (core_clk_rate < DEFAULT_CLK_RATE_HZ)
+ core_clk_rate = DEFAULT_CLK_RATE_HZ;
+
+ core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate;
+ core_clk_period_in_ns <<= OFFSET_CLK_NS_REG;
+ core_clk_period_in_ns &= MASK_CLK_NS_REG;
+
+ switch (hs) {
+ case FASTAUTO_MODE:
+ case FAST_MODE:
+ if (rate == PA_HS_MODE_A) {
+ if (gear >= ARRAY_SIZE(hs_fr_table_rA)) {
+ dev_err(hba->dev,
+ "%s: index %d exceeds table size %d\n",
+ __func__, gear,
+ ARRAY_SIZE(hs_fr_table_rA));
+ goto out_error;
+ }
+ tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1];
+ } else if (rate == PA_HS_MODE_B) {
+ if (gear >= ARRAY_SIZE(hs_fr_table_rB)) {
+ dev_err(hba->dev,
+ "%s: index %d exceeds table size %d\n",
+ __func__, gear,
+ ARRAY_SIZE(hs_fr_table_rB));
+ goto out_error;
+ }
+ tx_clk_cycles_per_us = hs_fr_table_rB[gear-1][1];
+ } else {
+ dev_err(hba->dev, "%s: invalid rate = %d\n",
+ __func__, rate);
+ goto out_error;
+ }
+ break;
+ case SLOWAUTO_MODE:
+ case SLOW_MODE:
+ if (gear >= ARRAY_SIZE(pwm_fr_table)) {
+ dev_err(hba->dev,
+ "%s: index %d exceeds table size %d\n",
+ __func__, gear,
+ ARRAY_SIZE(pwm_fr_table));
+ goto out_error;
+ }
+ tx_clk_cycles_per_us = pwm_fr_table[gear-1][1];
+ break;
+ case UNCHANGED:
+ default:
+ dev_err(hba->dev, "%s: invalid mode = %d\n", __func__, hs);
+ goto out_error;
+ }
+
+ /* this register 2 fields shall be written at once */
+ ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us,
+ REG_UFS_TX_SYMBOL_CLK_NS_US);
+ goto out;
+
+out_error:
+ core_clk_rate = 0;
+out:
+ return core_clk_rate;
+}
+
static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status)
{
unsigned long core_clk_rate = 0;
@@ -1219,7 +1314,13 @@ static int msm_ufs_link_startup_notify(struct ufs_hba *hba, bool status)
switch (status) {
case PRE_CHANGE:
- core_clk_rate = msm_ufs_cfg_timers(hba, 0, 0);
+ core_clk_rate = msm_ufs_cfg_timers(hba, UFS_PWM_G1,
+ SLOWAUTO_MODE, 0);
+ if (!core_clk_rate) {
+ dev_err(hba->dev, "%s: msm_ufs_cfg_timers() failed\n",
+ __func__);
+ return -EINVAL;
+ }
core_clk_cycles_per_100ms =
(core_clk_rate / MSEC_PER_SEC) * 100;
ufshcd_writel(hba, core_clk_cycles_per_100ms,
@@ -1255,20 +1356,14 @@ static int msm_ufs_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
goto out;
}
- /* M-PHY RMMI interface clocks can be turned off */
- msm_ufs_disable_phy_iface_clk(phy);
-
/*
- * If UniPro link is not active, PHY ref_clk and main PHY analog power
- * can be switched off.
+ * If UniPro link is not active, PHY ref_clk, main PHY analog power
+ * rail and low noise analog power rail for PLL can be switched off.
*/
if (!ufshcd_is_link_active(hba)) {
msm_ufs_disable_phy_ref_clk(phy);
- ret = msm_ufs_phy_disable_vreg(phy, &phy->vdda_phy);
- /*
- * TODO: Check if "vdda_pll" can voted off when link is hibern8
- * or power off state?
- */
+ msm_ufs_phy_disable_vreg(phy, &phy->vdda_phy);
+ msm_ufs_phy_disable_vreg(phy, &phy->vdda_pll);
}
out:
@@ -1301,24 +1396,6 @@ struct ufs_msm_dev_params {
u32 desired_working_mode;
};
-enum ufs_pwm_gear_tag {
- UFS_PWM_DONT_CHANGE, /* Don't change Gear */
- UFS_PWM_G1, /* PWM Gear 1 (default for reset) */
- UFS_PWM_G2, /* PWM Gear 2 */
- UFS_PWM_G3, /* PWM Gear 3 */
- UFS_PWM_G4, /* PWM Gear 4 */
- UFS_PWM_G5, /* PWM Gear 5 */
- UFS_PWM_G6, /* PWM Gear 6 */
- UFS_PWM_G7, /* PWM Gear 7 */
-};
-
-enum ufs_hs_gear_tag {
- UFS_HS_DONT_CHANGE, /* Don't change Gear */
- UFS_HS_G1, /* HS Gear 1 (default for reset) */
- UFS_HS_G2, /* HS Gear 2 */
- UFS_HS_G3, /* HS Gear 3 */
-};
-
/**
* as every power mode, according to the UFS spec, have a defined
* number that are not corresponed to their order or power
@@ -1473,65 +1550,6 @@ static int get_pwr_dev_param(struct ufs_msm_dev_params *msm_param,
return 0;
}
-static unsigned long
-msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs)
-{
- struct ufs_clk_info *clki;
- u32 core_clk_period_in_ns;
- u32 tx_clk_cycles_per_us = 0;
- unsigned long core_clk_rate = 0;
-
- static u32 pwm_fr_table[][2] = {
- {UFS_PWM_G1, 0x1},
- {UFS_PWM_G2, 0x1},
- {UFS_PWM_G3, 0x1},
- {UFS_PWM_G4, 0x1},
- };
-
- static u32 hs_fr_table_rA[][2] = {
- {UFS_HS_G1, 0x1F},
- {UFS_HS_G2, 0x3e},
- };
-
- if (gear == 0 && hs == 0) {
- gear = UFS_PWM_G1;
- hs = SLOWAUTO_MODE;
- }
-
- list_for_each_entry(clki, &hba->clk_list_head, list) {
- if (!strcmp(clki->name, "core_clk"))
- core_clk_rate = clk_get_rate(clki->clk);
- }
-
- /* If frequency is smaller than 1MHz, set to 1MHz */
- if (core_clk_rate < DEFAULT_CLK_RATE_HZ)
- core_clk_rate = DEFAULT_CLK_RATE_HZ;
-
- core_clk_period_in_ns = NSEC_PER_SEC / core_clk_rate;
- core_clk_period_in_ns <<= OFFSET_CLK_NS_REG;
- core_clk_period_in_ns &= MASK_CLK_NS_REG;
-
- switch (hs) {
- case FASTAUTO_MODE:
- case FAST_MODE:
- tx_clk_cycles_per_us = hs_fr_table_rA[gear-1][1];
- break;
- case SLOWAUTO_MODE:
- case SLOW_MODE:
- tx_clk_cycles_per_us = pwm_fr_table[gear-1][1];
- break;
- case UNCHANGED:
- default:
- pr_err("%s: power parameter not valid\n", __func__);
- return core_clk_rate;
- }
-
- /* this register 2 fields shall be written at once */
- ufshcd_writel(hba, core_clk_period_in_ns | tx_clk_cycles_per_us,
- REG_UFS_TX_SYMBOL_CLK_NS_US);
- return core_clk_rate;
-}
-
static int msm_ufs_pwr_change_notify(struct ufs_hba *hba,
bool status,
struct ufs_pa_layer_attr *dev_max_params,
@@ -1551,8 +1569,12 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba,
switch (status) {
case PRE_CHANGE:
+ if (hba->quirks & UFSHCD_QUIRK_BROKEN_2_TX_LANES)
+ ufs_msm_cap.tx_lanes = 1;
+ else
+ ufs_msm_cap.tx_lanes = UFS_MSM_LIMIT_NUM_LANES_TX;
+
ufs_msm_cap.rx_lanes = UFS_MSM_LIMIT_NUM_LANES_RX;
- ufs_msm_cap.tx_lanes = UFS_MSM_LIMIT_NUM_LANES_TX;
ufs_msm_cap.hs_rx_gear = UFS_MSM_LIMIT_HSGEAR_RX;
ufs_msm_cap.hs_tx_gear = UFS_MSM_LIMIT_HSGEAR_TX;
ufs_msm_cap.pwm_rx_gear = UFS_MSM_LIMIT_PWMGEAR_RX;
@@ -1575,8 +1597,18 @@ static int msm_ufs_pwr_change_notify(struct ufs_hba *hba,
break;
case POST_CHANGE:
- msm_ufs_cfg_timers(hba, dev_req_params->gear_rx,
- dev_req_params->pwr_rx);
+ if (!msm_ufs_cfg_timers(hba, dev_req_params->gear_rx,
+ dev_req_params->pwr_rx,
+ dev_req_params->hs_rate)) {
+ dev_err(hba->dev, "%s: msm_ufs_cfg_timers() failed\n",
+ __func__);
+ /*
+ * we return error code at the end of the routine,
+ * but continue to configure UFS_PHY_TX_LANE_ENABLE
+ * and bus voting as usual
+ */
+ ret = -EINVAL;
+ }
val = ~(MAX_U32 << dev_req_params->lane_tx);
writel_relaxed(val, phy->mmio + UFS_PHY_TX_LANE_ENABLE);
@@ -1631,6 +1663,7 @@ static void msm_ufs_advertise_quirks(struct ufs_hba *hba)
| UFSHCD_QUIRK_BROKEN_VER_REG_1_1
| UFSHCD_QUIRK_BROKEN_CAP_64_BIT_0
| UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS
+ | UFSHCD_QUIRK_BROKEN_2_TX_LANES
| UFSHCD_QUIRK_BROKEN_SUSPEND);
}
@@ -1745,11 +1778,26 @@ static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on)
int err;
int vote;
+ /*
+ * In case msm_ufs_init() is not yet done, simply ignore.
+ * This msm_ufs_setup_clocks() shall be called from
+ * msm_ufs_init() after init is done.
+ */
+ if (!host)
+ return 0;
+
if (on) {
+ err = msm_ufs_enable_phy_iface_clk(host->phy);
+ if (err)
+ goto out;
+
vote = host->bus_vote.saved_vote;
if (vote == host->bus_vote.min_bw_vote)
msm_ufs_update_bus_bw_vote(host);
} else {
+ /* M-PHY RMMI interface clocks can be turned off */
+ msm_ufs_disable_phy_iface_clk(host->phy);
+
vote = host->bus_vote.min_bw_vote;
}
@@ -1758,6 +1806,7 @@ static int msm_ufs_setup_clocks(struct ufs_hba *hba, bool on)
dev_err(hba->dev, "%s: set bus vote failed %d\n",
__func__, err);
+out:
return err;
}
@@ -1903,6 +1952,9 @@ static int msm_ufs_init(struct ufs_hba *hba)
hba->spm_lvl = UFS_PM_LVL_3;
}
+ hba->caps |= UFSHCD_CAP_CLK_GATING |
+ UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
+ msm_ufs_setup_clocks(hba, true);
goto out;
out_disable_phy:
diff --git a/drivers/scsi/ufs/ufs-msm.h b/drivers/scsi/ufs/ufs-msm.h
index 37ec02971d1f..c1ecff82ba1b 100644
--- a/drivers/scsi/ufs/ufs-msm.h
+++ b/drivers/scsi/ufs/ufs-msm.h
@@ -100,8 +100,6 @@ struct msm_ufs_host {
bool is_lane_clks_enabled;
};
-static unsigned long
-msm_ufs_cfg_timers(struct ufs_hba *hba, u32 gear, u32 hs);
static int msm_ufs_update_bus_bw_vote(struct msm_ufs_host *host);
/* MSM UFS PHY control registers */
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
index 4a84dcd3a84b..ab42aa553bd3 100644
--- a/drivers/scsi/ufs/ufs.h
+++ b/drivers/scsi/ufs/ufs.h
@@ -177,6 +177,18 @@ enum unit_desc_param {
UNIT_DESC_PARAM_LARGE_UNIT_SIZE_M1 = 0x22,
};
+/*
+ * Logical Unit Write Protect
+ * 00h: LU not write protected
+ * 01h: LU write protected when fPowerOnWPEn =1
+ * 02h: LU permanently write protected when fPermanentWPEn =1
+ */
+enum ufs_lu_wp_type {
+ UFS_LU_NO_WP = 0x00,
+ UFS_LU_POWER_ON_WP = 0x01,
+ UFS_LU_PERM_WP = 0x02,
+};
+
/* bActiveICCLevel parameter current units */
enum {
UFSHCD_NANO_AMP = 0,
@@ -415,6 +427,12 @@ struct ufs_query_res {
#define UFS_VREG_VCCQ2_MIN_UV 1650000 /* uV */
#define UFS_VREG_VCCQ2_MAX_UV 1950000 /* uV */
+/*
+ * VCCQ & VCCQ2 current requirement when UFS device is in sleep state
+ * and link is in Hibern8 state.
+ */
+#define UFS_VREG_LPM_LOAD_UA 1000 /* uA */
+
struct ufs_vreg {
struct regulator *reg;
const char *name;
@@ -431,4 +449,10 @@ struct ufs_vreg_info {
struct ufs_vreg *vccq2;
};
+struct ufs_dev_info {
+ bool f_power_on_wp_en;
+ /* Keeps information if any of the LU is power on write protected */
+ bool is_lu_power_on_wp;
+};
+
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufs_test.c b/drivers/scsi/ufs/ufs_test.c
index 3fd52b7848ac..35f841c183ee 100644
--- a/drivers/scsi/ufs/ufs_test.c
+++ b/drivers/scsi/ufs/ufs_test.c
@@ -934,9 +934,6 @@ static int run_long_seq_test(struct test_data *td)
/* NUM_OF_BLOCK * (BLOCK_SIZE / SECTOR_SIZE) */
sector += TEST_MAX_BIOS_PER_REQ * (PAGE_SIZE /
td->req_q->limits.logical_block_size);
- td->test_info.test_byte_count +=
- (TEST_MAX_BIOS_PER_REQ * sizeof(unsigned int) *
- BIO_U32_SIZE);
} while (inserted_requests < LONG_SEQ_TEST_NUM_REQS);
diff --git a/drivers/scsi/ufs/ufshcd-pci.c b/drivers/scsi/ufs/ufshcd-pci.c
index 8f3d6e5cff7a..c92fa656f1f8 100644
--- a/drivers/scsi/ufs/ufshcd-pci.c
+++ b/drivers/scsi/ufs/ufshcd-pci.c
@@ -92,7 +92,7 @@ static int ufshcd_pci_runtime_idle(struct device *dev)
*/
static void ufshcd_pci_shutdown(struct pci_dev *pdev)
{
- ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev), true);
+ ufshcd_shutdown((struct ufs_hba *)pci_get_drvdata(pdev));
}
/**
diff --git a/drivers/scsi/ufs/ufshcd-pltfrm.c b/drivers/scsi/ufs/ufshcd-pltfrm.c
index 5cba57c48759..9e0f018f7394 100644
--- a/drivers/scsi/ufs/ufshcd-pltfrm.c
+++ b/drivers/scsi/ufs/ufshcd-pltfrm.c
@@ -258,6 +258,11 @@ static int ufshcd_pltfrm_runtime_idle(struct device *dev)
#define ufshcd_pltfrm_runtime_idle NULL
#endif /* CONFIG_PM_RUNTIME */
+static void ufshcd_pltfrm_shutdown(struct platform_device *pdev)
+{
+ ufshcd_shutdown((struct ufs_hba *)platform_get_drvdata(pdev));
+}
+
/**
* ufshcd_pltfrm_probe - probe routine of the driver
* @pdev: pointer to Platform device handle
@@ -362,6 +367,7 @@ static const struct dev_pm_ops ufshcd_dev_pm_ops = {
static struct platform_driver ufshcd_pltfrm_driver = {
.probe = ufshcd_pltfrm_probe,
.remove = ufshcd_pltfrm_remove,
+ .shutdown = ufshcd_pltfrm_shutdown,
.driver = {
.name = "ufshcd",
.owner = THIS_MODULE,
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6e130282f492..fd1708595b0f 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -3,6 +3,7 @@
*
* This code is based on drivers/scsi/ufs/ufshcd.c
* Copyright (C) 2011-2013 Samsung India Software Operations
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* Authors:
* Santosh Yaraganavi <santosh.sy@samsung.com>
@@ -31,6 +32,9 @@
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program.
+ *
+ * The Linux Foundation chooses to take subject only to the GPLv2
+ * license terms, and distributes only under these terms.
*/
#include <linux/async.h>
@@ -96,6 +100,9 @@
/* default value of auto suspend is 3 seconds */
#define UFSHCD_AUTO_SUSPEND_DELAY_MS 3000 /* millisecs */
+/* IOCTL opcode for command - ufs set device read only */
+#define UFS_IOCTL_BLKROSET BLKROSET
+
#define ufshcd_toggle_vreg(_dev, _vreg, _on) \
({ \
int _ret; \
@@ -209,6 +216,11 @@ static inline int ufshcd_read_unit_desc_param(struct ufs_hba *hba,
enum unit_desc_param param_offset,
u8 *param_read_buf,
u32 param_size);
+static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
+ bool skip_ref_clk);
+static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on);
+static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
+static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_enable_irq(struct ufs_hba *hba)
{
@@ -618,6 +630,254 @@ static inline int ufshcd_is_hba_active(struct ufs_hba *hba)
return (ufshcd_readl(hba, REG_CONTROLLER_ENABLE) & 0x1) ? 0 : 1;
}
+static void ufshcd_ungate_work(struct work_struct *work)
+{
+ int ret;
+ unsigned long flags;
+ struct ufs_hba *hba = container_of(work, struct ufs_hba,
+ clk_gating.ungate_work);
+
+ cancel_delayed_work_sync(&hba->clk_gating.gate_work);
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->clk_gating.state == CLKS_ON) {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ goto unblock_reqs;
+ }
+
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ ufshcd_setup_clocks(hba, true);
+
+ /* Exit from hibern8 */
+ if (ufshcd_can_hibern8_during_gating(hba)) {
+ /* Prevent gating in this path */
+ hba->clk_gating.is_suspended = true;
+ if (ufshcd_is_link_hibern8(hba)) {
+ ret = ufshcd_uic_hibern8_exit(hba);
+ if (ret)
+ dev_err(hba->dev, "%s: hibern8 exit failed %d\n",
+ __func__, ret);
+ else
+ ufshcd_set_link_active(hba);
+ }
+ hba->clk_gating.is_suspended = false;
+ }
+unblock_reqs:
+ scsi_unblock_requests(hba->host);
+}
+
+static const char *to_string(enum clk_gating_state state)
+{
+ switch (state) {
+ case CLKS_OFF: return "CLKS_OFF";
+ case CLKS_ON: return "CLKS_ON";
+ case REQ_CLKS_OFF: return "REQ_CLKS_OFF";
+ case REQ_CLKS_ON: return "REQ_CLKS_ON";
+ default: return "UNKNOWN_STATE";
+ }
+}
+
+/**
+ * ufshcd_hold - Enable clocks that were gated earlier due to ufshcd_release.
+ * Also, exit from hibern8 mode and set the link as active.
+ * @hba: per adapter instance
+ * @async: This indicates whether caller should ungate clocks asynchronously.
+ */
+int ufshcd_hold(struct ufs_hba *hba, bool async)
+{
+ int rc = 0;
+ unsigned long flags;
+
+ if (!ufshcd_is_clkgating_allowed(hba))
+ goto out;
+start:
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.active_reqs++;
+
+ switch (hba->clk_gating.state) {
+ case CLKS_ON:
+ break;
+ case REQ_CLKS_OFF:
+ if (cancel_delayed_work(&hba->clk_gating.gate_work)) {
+ hba->clk_gating.state = CLKS_ON;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ break;
+ }
+ /*
+ * If we here, it means gating work is either done or
+ * currently running. Hence, fall through to cancel gating
+ * work and to enable clocks.
+ */
+ case CLKS_OFF:
+ scsi_block_requests(hba->host);
+ hba->clk_gating.state = REQ_CLKS_ON;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ schedule_work(&hba->clk_gating.ungate_work);
+ /*
+ * fall through to check if we should wait for this
+ * work to be done or not.
+ */
+ case REQ_CLKS_ON:
+ if (async) {
+ rc = -EAGAIN;
+ hba->clk_gating.active_reqs--;
+ break;
+ } else {
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ flush_work(&hba->clk_gating.ungate_work);
+ /* Make sure state is CLKS_ON before returning */
+ goto start;
+ }
+ default:
+ dev_err(hba->dev, "%s: clk gating is in invalid state %d\n",
+ __func__, hba->clk_gating.state);
+ break;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+out:
+ return rc;
+}
+
+static void ufshcd_gate_work(struct work_struct *work)
+{
+ struct ufs_hba *hba = container_of(work, struct ufs_hba,
+ clk_gating.gate_work.work);
+ unsigned long flags;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->clk_gating.is_suspended) {
+ hba->clk_gating.state = CLKS_ON;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ goto rel_lock;
+ }
+
+ if (hba->clk_gating.active_reqs
+ || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL
+ || hba->lrb_in_use || hba->outstanding_tasks
+ || hba->active_uic_cmd || hba->uic_async_done)
+ goto rel_lock;
+
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ /* put the link into hibern8 mode before turning off clocks */
+ if (ufshcd_can_hibern8_during_gating(hba)) {
+ if (ufshcd_uic_hibern8_enter(hba)) {
+ hba->clk_gating.state = CLKS_ON;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ goto out;
+ }
+ ufshcd_set_link_hibern8(hba);
+ }
+
+ if (!ufshcd_is_link_active(hba))
+ ufshcd_setup_clocks(hba, false);
+ else
+ /* If link is active, device ref_clk can't be switched off */
+ __ufshcd_setup_clocks(hba, false, true);
+
+ /*
+ * In case you are here to cancel this work the gating state
+ * would be marked as REQ_CLKS_ON. In this case keep the state
+ * as REQ_CLKS_ON which would anyway imply that clocks are off
+ * and a request to turn them on is pending. By doing this way,
+ * we keep the state machine in tact and this would ultimately
+ * prevent from doing cancel work multiple times when there are
+ * new requests arriving before the current cancel work is done.
+ */
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ if (hba->clk_gating.state == REQ_CLKS_OFF) {
+ hba->clk_gating.state = CLKS_OFF;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ }
+rel_lock:
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+out:
+ return;
+}
+
+/* host lock must be held before calling this variant */
+static void __ufshcd_release(struct ufs_hba *hba)
+{
+ if (!ufshcd_is_clkgating_allowed(hba))
+ return;
+
+ hba->clk_gating.active_reqs--;
+
+ if (hba->clk_gating.active_reqs || hba->clk_gating.is_suspended
+ || hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL
+ || hba->lrb_in_use || hba->outstanding_tasks
+ || hba->active_uic_cmd || hba->uic_async_done)
+ return;
+
+ hba->clk_gating.state = REQ_CLKS_OFF;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ schedule_delayed_work(&hba->clk_gating.gate_work,
+ msecs_to_jiffies(hba->clk_gating.delay_ms));
+}
+
+void ufshcd_release(struct ufs_hba *hba)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ __ufshcd_release(hba);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+}
+
+static ssize_t ufshcd_clkgate_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%lu\n", hba->clk_gating.delay_ms);
+}
+
+static ssize_t ufshcd_clkgate_delay_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct ufs_hba *hba = dev_get_drvdata(dev);
+ unsigned long flags, value;
+
+ if (kstrtoul(buf, 0, &value))
+ return -EINVAL;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.delay_ms = value;
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return count;
+}
+
+static void ufshcd_init_clk_gating(struct ufs_hba *hba)
+{
+ if (!ufshcd_is_clkgating_allowed(hba))
+ return;
+
+ hba->clk_gating.delay_ms = 150;
+ INIT_DELAYED_WORK(&hba->clk_gating.gate_work, ufshcd_gate_work);
+ INIT_WORK(&hba->clk_gating.ungate_work, ufshcd_ungate_work);
+
+ hba->clk_gating.delay_attr.show = ufshcd_clkgate_delay_show;
+ hba->clk_gating.delay_attr.store = ufshcd_clkgate_delay_store;
+ sysfs_attr_init(&hba->clk_gating.delay_attr.attr);
+ hba->clk_gating.delay_attr.attr.name = "clkgate_delay_ms";
+ hba->clk_gating.delay_attr.attr.mode = S_IRUGO | S_IWUSR;
+ if (device_create_file(hba->dev, &hba->clk_gating.delay_attr))
+ dev_err(hba->dev, "Failed to create sysfs for clkgate_delay\n");
+}
+
+static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
+{
+ if (!ufshcd_is_clkgating_allowed(hba))
+ return;
+ device_remove_file(hba->dev, &hba->clk_gating.delay_attr);
+}
+
/**
* ufshcd_send_command - Send SCSI or device management commands
* @hba: per adapter instance
@@ -823,10 +1083,12 @@ ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd)
{
int ret;
+ ufshcd_hold(hba, false);
mutex_lock(&hba->uic_cmd_mutex);
ret = __ufshcd_send_uic_cmd(hba, uic_cmd);
mutex_unlock(&hba->uic_cmd_mutex);
+ ufshcd_release(hba);
return ret;
}
@@ -1142,6 +1404,14 @@ static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
goto out;
}
+ err = ufshcd_hold(hba, true);
+ if (err) {
+ err = SCSI_MLQUEUE_HOST_BUSY;
+ clear_bit_unlock(tag, &hba->lrb_in_use);
+ goto out;
+ }
+ WARN_ON(hba->clk_gating.state != CLKS_ON);
+
lrbp = &hba->lrb[tag];
WARN_ON(lrbp->cmd);
@@ -1417,6 +1687,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
BUG_ON(!hba);
+ ufshcd_hold(hba, false);
mutex_lock(&hba->dev_cmd.lock);
ufshcd_init_query(hba, &request, &response, opcode, idn, index,
selector);
@@ -1460,6 +1731,7 @@ int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
out_unlock:
mutex_unlock(&hba->dev_cmd.lock);
+ ufshcd_release(hba);
return err;
}
@@ -1483,6 +1755,7 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
BUG_ON(!hba);
+ ufshcd_hold(hba, false);
if (!attr_val) {
dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n",
__func__, opcode);
@@ -1522,6 +1795,7 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
out_unlock:
mutex_unlock(&hba->dev_cmd.lock);
out:
+ ufshcd_release(hba);
return err;
}
@@ -1586,6 +1860,7 @@ int ufshcd_query_descriptor(struct ufs_hba *hba,
BUG_ON(!hba);
+ ufshcd_hold(hba, false);
if (!desc_buf) {
dev_err(hba->dev, "%s: descriptor buffer required for opcode 0x%x\n",
__func__, opcode);
@@ -1635,6 +1910,7 @@ int ufshcd_query_descriptor(struct ufs_hba *hba,
out_unlock:
mutex_unlock(&hba->dev_cmd.lock);
out:
+ ufshcd_release(hba);
return err;
}
@@ -2071,6 +2347,7 @@ int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd)
u8 status;
int ret;
+ ufshcd_hold(hba, false);
mutex_lock(&hba->uic_cmd_mutex);
init_completion(&uic_async_done);
@@ -2107,6 +2384,8 @@ out:
hba->uic_async_done = NULL;
spin_unlock_irqrestore(hba->host->host_lock, flags);
mutex_unlock(&hba->uic_cmd_mutex);
+
+ ufshcd_release(hba);
return ret;
}
@@ -2147,6 +2426,48 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba)
return ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
}
+ /**
+ * ufshcd_print_pwr_info - print power params as saved in hba
+ * power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_print_pwr_info(struct ufs_hba *hba)
+{
+ char *names[] = {
+ "INVALID MODE",
+ "FAST MODE",
+ "SLOW_MODE",
+ "INVALID MODE",
+ "FASTAUTO_MODE",
+ "SLOWAUTO_MODE",
+ "INVALID MODE",
+ };
+
+ dev_info(hba->dev, "%s:[RX, TX]: gear=[%d, %d], lane[%d, %d], pwr[%s, %s], rate = %d\n",
+ __func__,
+ hba->pwr_info.gear_rx, hba->pwr_info.gear_tx,
+ hba->pwr_info.lane_rx, hba->pwr_info.lane_tx,
+ names[hba->pwr_info.pwr_rx],
+ names[hba->pwr_info.pwr_tx],
+ hba->pwr_info.hs_rate);
+}
+
+ /**
+ * ufshcd_init_pwr_info - setting the POR (power on reset)
+ * values in hba power info
+ * @hba: per-adapter instance
+ */
+static void ufshcd_init_pwr_info(struct ufs_hba *hba)
+{
+ hba->pwr_info.gear_rx = UFS_PWM_G1;
+ hba->pwr_info.gear_tx = UFS_PWM_G1;
+ hba->pwr_info.lane_rx = 1;
+ hba->pwr_info.lane_tx = 1;
+ hba->pwr_info.pwr_rx = SLOWAUTO_MODE;
+ hba->pwr_info.pwr_tx = SLOWAUTO_MODE;
+ hba->pwr_info.hs_rate = 0;
+}
+
/**
* ufshcd_config_max_pwr_mode - Set & Change power mode with
* maximum capability attribute information.
@@ -2169,6 +2490,12 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES), &lanes[RX]);
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), &lanes[TX]);
+ if (!lanes[RX] || !lanes[TX]) {
+ dev_err(hba->dev, "%s: invalid connected lanes value. rx=%d, tx=%d\n",
+ __func__, lanes[RX], lanes[TX]);
+ return -EINVAL;
+ }
+
/*
* First, get the maximum gears of HS speed.
* If a zero value, it means there is no HSGEAR capability.
@@ -2177,6 +2504,11 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXHSGEAR), &gear[RX]);
if (!gear[RX]) {
ufshcd_dme_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR), &gear[RX]);
+ if (!gear[RX]) {
+ dev_err(hba->dev, "%s: invalid rx gear read = %d\n",
+ __func__, gear[RX]);
+ return -EINVAL;
+ }
pwr[RX] = SLOWAUTO_MODE;
}
@@ -2184,6 +2516,11 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
if (!gear[TX]) {
ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_MAXRXPWMGEAR),
&gear[TX]);
+ if (!gear[TX]) {
+ dev_err(hba->dev, "%s: invalid tx gear read = %d\n",
+ __func__, gear[TX]);
+ return -EINVAL;
+ }
pwr[TX] = SLOWAUTO_MODE;
}
@@ -2244,8 +2581,18 @@ static int ufshcd_config_max_pwr_mode(struct ufs_hba *hba)
if (hba->vops->pwr_change_notify)
hba->vops->pwr_change_notify(hba,
POST_CHANGE, NULL, &dev_required_params);
+
+ hba->pwr_info.gear_rx = gear[RX];
+ hba->pwr_info.gear_tx = gear[TX];
+ hba->pwr_info.lane_rx = lanes[RX];
+ hba->pwr_info.lane_tx = lanes[TX];
+ hba->pwr_info.pwr_rx = pwr[RX];
+ hba->pwr_info.pwr_tx = pwr[TX];
+ hba->pwr_info.hs_rate = hs_rate;
}
+ ufshcd_print_pwr_info(hba);
+
if (hba->quirks & UFSHCD_QUIRK_BROKEN_PWR_MODE_CHANGE)
msleep(1000);
@@ -2501,6 +2848,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
int err = 0;
int retries;
+ ufshcd_hold(hba, false);
mutex_lock(&hba->dev_cmd.lock);
for (retries = NOP_OUT_RETRIES; retries > 0; retries--) {
err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_NOP,
@@ -2512,6 +2860,7 @@ static int ufshcd_verify_dev_init(struct ufs_hba *hba)
dev_dbg(hba->dev, "%s: error %d retrying\n", __func__, err);
}
mutex_unlock(&hba->dev_cmd.lock);
+ ufshcd_release(hba);
if (err)
dev_err(hba->dev, "%s: NOP OUT failed %d\n", __func__, err);
@@ -2564,6 +2913,62 @@ static void ufshcd_set_queue_depth(struct scsi_device *sdev)
scsi_activate_tcq(sdev, lun_qdepth);
}
+/*
+ * ufshcd_get_lu_wp - returns the "b_lu_write_protect" from UNIT DESCRIPTOR
+ * @hba: per-adapter instance
+ * @lun: UFS device lun id
+ * @b_lu_write_protect: pointer to buffer to hold the LU's write protect info
+ *
+ * Returns 0 in case of success and b_lu_write_protect status would be returned
+ * @b_lu_write_protect parameter.
+ * Returns -ENOTSUPP if reading b_lu_write_protect is not supported.
+ * Returns -EINVAL in case of invalid parameters passed to this function.
+ */
+static int ufshcd_get_lu_wp(struct ufs_hba *hba,
+ u8 lun,
+ u8 *b_lu_write_protect)
+{
+ int ret;
+
+ if (!b_lu_write_protect)
+ ret = -EINVAL;
+ /*
+ * According to UFS device spec, RPMB LU can't be write
+ * protected so skip reading bLUWriteProtect parameter for
+ * it. For other W-LUs, UNIT DESCRIPTOR is not available.
+ */
+ else if (lun >= UFS_UPIU_MAX_GENERAL_LUN)
+ ret = -ENOTSUPP;
+ else
+ ret = ufshcd_read_unit_desc_param(hba,
+ lun,
+ UNIT_DESC_PARAM_LU_WR_PROTECT,
+ b_lu_write_protect,
+ sizeof(*b_lu_write_protect));
+ return ret;
+}
+
+/**
+ * ufshcd_get_lu_power_on_wp_status - get LU's power on write protect
+ * status
+ * @hba: per-adapter instance
+ * @sdev: pointer to SCSI device
+ *
+ */
+static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba,
+ struct scsi_device *sdev)
+{
+ if (hba->dev_info.f_power_on_wp_en &&
+ !hba->dev_info.is_lu_power_on_wp) {
+ u8 b_lu_write_protect;
+
+ if (!ufshcd_get_lu_wp(hba, ufshcd_scsi_to_upiu_lun(sdev->lun),
+ &b_lu_write_protect) &&
+ (b_lu_write_protect == UFS_LU_POWER_ON_WP))
+ hba->dev_info.is_lu_power_on_wp = true;
+ }
+}
+
/**
* ufshcd_slave_alloc - handle initial SCSI device configurations
* @sdev: pointer to SCSI device
@@ -2595,6 +3000,8 @@ static int ufshcd_slave_alloc(struct scsi_device *sdev)
ufshcd_set_queue_depth(sdev);
+ ufshcd_get_lu_power_on_wp_status(hba, sdev);
+
/*
* For selecting the UFS device power mode (Active / UFS_Sleep /
* UFS_PowerDown), SCSI power management command (START STOP UNIT)
@@ -2886,6 +3293,7 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
clear_bit_unlock(index, &hba->lrb_in_use);
/* Do not touch lrbp after scsi done */
cmd->scsi_done(cmd);
+ __ufshcd_release(hba);
} else if (lrbp->command_type == UTP_CMD_TYPE_DEV_MANAGE) {
if (hba->dev_cmd.complete)
complete(hba->dev_cmd.complete);
@@ -3170,6 +3578,7 @@ static void ufshcd_err_handler(struct work_struct *work)
hba = container_of(work, struct ufs_hba, eh_work);
pm_runtime_get_sync(hba->dev);
+ ufshcd_hold(hba, false);
spin_lock_irqsave(hba->host->host_lock, flags);
if (hba->ufshcd_state == UFSHCD_STATE_RESET) {
@@ -3223,6 +3632,7 @@ static void ufshcd_err_handler(struct work_struct *work)
out:
scsi_unblock_requests(hba->host);
+ ufshcd_release(hba);
pm_runtime_put_sync(hba->dev);
}
@@ -3303,6 +3713,7 @@ static void ufshcd_check_errors(struct ufs_hba *hba)
SYSTEM_BUS_FATAL_ERROR);
ufshcd_print_host_regs(hba);
+ ufshcd_print_pwr_info(hba);
ufshcd_print_tmrs(hba, hba->outstanding_tasks);
ufshcd_print_trs(hba, hba->outstanding_reqs,
pr_prdt);
@@ -3430,6 +3841,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
* the maximum wait time is bounded by %TM_CMD_TIMEOUT.
*/
wait_event(hba->tm_tag_wq, ufshcd_get_tm_free_slot(hba, &free_slot));
+ ufshcd_hold(hba, false);
spin_lock_irqsave(host->host_lock, flags);
task_req_descp = hba->utmrdl_base_addr;
@@ -3481,6 +3893,7 @@ static int ufshcd_issue_tm_cmd(struct ufs_hba *hba, int lun_id, int task_id,
ufshcd_put_tm_slot(hba, free_slot);
wake_up(&hba->tm_tag_wq);
+ ufshcd_release(hba);
return err;
}
@@ -3563,6 +3976,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
hba = shost_priv(host);
tag = cmd->request->tag;
+ ufshcd_hold(hba, false);
/* If command is already aborted/completed, return SUCCESS */
if (!(test_bit(tag, &hba->outstanding_reqs)))
goto out;
@@ -3578,6 +3992,7 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
dev_err(hba->dev, "%s: Device abort task at tag %d", __func__, tag);
scsi_print_command(cmd);
ufshcd_print_host_regs(hba);
+ ufshcd_print_pwr_info(hba);
ufshcd_print_trs(hba, 1 << tag, true);
lrbp = &hba->lrb[tag];
@@ -3633,6 +4048,11 @@ static int ufshcd_abort(struct scsi_cmnd *cmd)
clear_bit_unlock(tag, &hba->lrb_in_use);
wake_up(&hba->dev_cmd.tag_wq);
+ /*
+ * This ufshcd_release() corresponds to the original scsi cmd that got
+ * aborted here (as we won't get any IRQ for it).
+ */
+ ufshcd_release(hba);
out:
if (!err) {
err = SUCCESS;
@@ -3640,7 +4060,7 @@ out:
dev_err(hba->dev, "%s: failed with err %d\n", __func__, err);
err = FAILED;
}
-
+ ufshcd_release(hba);
return err;
}
@@ -3725,6 +4145,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd)
hba = shost_priv(cmd->device->host);
+ ufshcd_hold(hba, false);
/*
* Check if there is any race with fatal error handling.
* If so, wait for it to complete. Even though fatal error
@@ -3758,6 +4179,7 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd)
ufshcd_clear_eh_in_progress(hba);
spin_unlock_irqrestore(hba->host->host_lock, flags);
+ ufshcd_release(hba);
return err;
}
@@ -3919,6 +4341,9 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
if (ret)
goto out;
+ ufshcd_init_pwr_info(hba);
+ ufshcd_print_pwr_info(hba);
+
/* UniPro link is active now */
ufshcd_set_link_active(hba);
@@ -3943,13 +4368,24 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
ufshcd_force_reset_auto_bkops(hba);
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
- ufshcd_config_max_pwr_mode(hba);
+ if (ufshcd_config_max_pwr_mode(hba))
+ dev_err(hba->dev,
+ "%s: Failed configuring max supported power mode\n",
+ __func__);
/*
* If we are in error handling context or in power management callbacks
* context, no need to scan the host
*/
if (!ufshcd_eh_in_progress(hba) && !hba->pm_op_in_progress) {
+ bool flag;
+
+ /* clear any previous UFS device information */
+ memset(&hba->dev_info, 0, sizeof(hba->dev_info));
+ if (!ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG,
+ QUERY_FLAG_IDN_PWR_ON_WPE, &flag))
+ hba->dev_info.f_power_on_wp_en = flag;
+
ufshcd_init_icc_levels(hba);
scsi_scan_host(hba->host);
pm_runtime_put_sync(hba->dev);
@@ -4186,6 +4622,9 @@ static int ufshcd_ioctl(struct scsi_device *dev, int cmd, void __user *buffer)
buffer);
pm_runtime_put_sync(hba->dev);
break;
+ case UFS_IOCTL_BLKROSET:
+ err = -ENOIOCTLCMD;
+ break;
default:
err = -EINVAL;
dev_err(hba->dev, "%s: Illegal ufs-IOCTL cmd %d\n", __func__,
@@ -4213,8 +4652,45 @@ static struct scsi_host_template ufshcd_driver_template = {
.sg_tablesize = SG_ALL,
.cmd_per_lun = UFSHCD_CMD_PER_LUN,
.can_queue = UFSHCD_CAN_QUEUE,
+ .max_host_blocked = 1,
};
+static int ufshcd_config_vreg_load(struct device *dev, struct ufs_vreg *vreg,
+ int ua)
+{
+ int ret = 0;
+ struct regulator *reg = vreg->reg;
+ const char *name = vreg->name;
+
+ BUG_ON(!vreg);
+
+ ret = regulator_set_optimum_mode(reg, ua);
+ if (ret >= 0) {
+ /*
+ * regulator_set_optimum_mode() returns new regulator
+ * mode upon success.
+ */
+ ret = 0;
+ } else {
+ dev_err(dev, "%s: %s set optimum mode(ua=%d) failed, err=%d\n",
+ __func__, name, ua, ret);
+ }
+
+ return ret;
+}
+
+static inline int ufshcd_config_vreg_lpm(struct ufs_hba *hba,
+ struct ufs_vreg *vreg)
+{
+ return ufshcd_config_vreg_load(hba->dev, vreg, UFS_VREG_LPM_LOAD_UA);
+}
+
+static inline int ufshcd_config_vreg_hpm(struct ufs_hba *hba,
+ struct ufs_vreg *vreg)
+{
+ return ufshcd_config_vreg_load(hba->dev, vreg, vreg->max_uA);
+}
+
static int ufshcd_config_vreg(struct device *dev,
struct ufs_vreg *vreg, bool on)
{
@@ -4235,18 +4711,9 @@ static int ufshcd_config_vreg(struct device *dev,
}
uA_load = on ? vreg->max_uA : 0;
- ret = regulator_set_optimum_mode(reg, uA_load);
- if (ret >= 0) {
- /*
- * regulator_set_optimum_mode() returns new regulator
- * mode upon success.
- */
- ret = 0;
- } else {
- dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
- __func__, name, uA_load, ret);
+ ret = ufshcd_config_vreg_load(dev, vreg, uA_load);
+ if (ret)
goto out;
- }
}
out:
return ret;
@@ -4368,6 +4835,7 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
int ret = 0;
struct ufs_clk_info *clki;
struct list_head *head = &hba->clk_list_head;
+ unsigned long flags;
if (!head || list_empty(head))
goto out;
@@ -4392,12 +4860,21 @@ static int __ufshcd_setup_clocks(struct ufs_hba *hba, bool on,
clki->name, on ? "en" : "dis");
}
}
+
+ if (hba->vops && hba->vops->setup_clocks)
+ ret = hba->vops->setup_clocks(hba, on);
out:
if (ret) {
list_for_each_entry(clki, head, list) {
if (!IS_ERR_OR_NULL(clki->clk) && clki->enabled)
clk_disable_unprepare(clki->clk);
}
+ } else if (!ret && on) {
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->clk_gating.state = CLKS_ON;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
}
return ret;
}
@@ -4458,23 +4935,14 @@ static int ufshcd_variant_hba_init(struct ufs_hba *hba)
goto out;
}
- if (hba->vops->setup_clocks) {
- err = hba->vops->setup_clocks(hba, true);
- if (err)
- goto out_exit;
- }
-
if (hba->vops->setup_regulators) {
err = hba->vops->setup_regulators(hba, true);
if (err)
- goto out_clks;
+ goto out_exit;
}
goto out;
-out_clks:
- if (hba->vops->setup_clocks)
- hba->vops->setup_clocks(hba, false);
out_exit:
if (hba->vops->exit)
hba->vops->exit(hba);
@@ -4672,18 +5140,72 @@ out:
return ret;
}
+static void ufshcd_vreg_set_lpm(struct ufs_hba *hba)
+{
+ /*
+ * If UFS device is either in UFS_Sleep turn off VCC rail to save some
+ * power.
+ *
+ * If UFS device and link is in OFF state, all power supplies (VCC,
+ * VCCQ, VCCQ2) can be turned off if power on write protect is not
+ * required. If UFS link is inactive (Hibern8 or OFF state) and device
+ * is in sleep state, put VCCQ & VCCQ2 rails in LPM mode.
+ *
+ * Ignore the error returned by ufshcd_toggle_vreg() as device is anyway
+ * in low power state which would save some power.
+ */
+ if (ufshcd_is_ufs_dev_poweroff(hba) &&
+ !hba->dev_info.is_lu_power_on_wp) {
+ ufshcd_setup_vreg(hba, false);
+ } else if (!ufshcd_is_ufs_dev_active(hba)) {
+ ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+ if (!ufshcd_is_link_active(hba)) {
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq2);
+ }
+ }
+}
+
+static int ufshcd_vreg_set_hpm(struct ufs_hba *hba)
+{
+ int ret = 0;
+
+ if (ufshcd_is_ufs_dev_poweroff(hba) &&
+ !hba->dev_info.is_lu_power_on_wp) {
+ ret = ufshcd_setup_vreg(hba, true);
+ } else if (!ufshcd_is_ufs_dev_active(hba)) {
+ ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
+ if (!ret && !ufshcd_is_link_active(hba)) {
+ ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq);
+ if (ret)
+ goto vcc_disable;
+ ret = ufshcd_config_vreg_hpm(hba, hba->vreg_info.vccq2);
+ if (ret)
+ goto vccq_lpm;
+ }
+ }
+ goto out;
+
+vccq_lpm:
+ ufshcd_config_vreg_lpm(hba, hba->vreg_info.vccq);
+vcc_disable:
+ ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+out:
+ return ret;
+}
+
/**
* ufshcd_suspend - helper function for suspend operations
* @hba: per adapter instance
- * @pm_op: runtime PM or system PM
- *
- * This is common function called by both ufshcd_system_suspend() and
- * ufshcd_runtime_suspend().
+ * @pm_op: desired low power operation type
*
* This function will try to put the UFS device and link into low power
* mode based on the "rpm_lvl" (Runtime PM level) or "spm_lvl"
* (System PM level).
*
+ * If this function is called during shutdown, it will make sure that
+ * both UFS device and UFS link is powered off.
+ *
* NOTE: UFS device & link must be active before we enter in this function.
*
* Returns 0 for success and non-zero for failure
@@ -4696,14 +5218,23 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
enum uic_link_state req_link_state;
hba->pm_op_in_progress = 1;
- pm_lvl = ufshcd_is_runtime_pm(pm_op) ? hba->rpm_lvl : hba->spm_lvl;
- req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl);
- req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl);
+ if (!ufshcd_is_shutdown_pm(pm_op)) {
+ pm_lvl = ufshcd_is_runtime_pm(pm_op) ?
+ hba->rpm_lvl : hba->spm_lvl;
+ req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl);
+ req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl);
+ } else {
+ req_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE;
+ req_link_state = UIC_LINK_OFF_STATE;
+ }
/*
* If we can't transition into any of the low power modes
* just gate the clocks.
*/
+ ufshcd_hold(hba, false);
+ hba->clk_gating.is_suspended = true;
+
if (req_dev_pwr_mode == UFS_ACTIVE_PWR_MODE &&
req_link_state == UIC_LINK_ACTIVE_STATE) {
goto disable_clks;
@@ -4726,35 +5257,24 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
*/
ret = ufshcd_bkops_ctrl(hba, BKOPS_STATUS_NON_CRITICAL);
if (ret)
- goto out;
+ goto enable_gating;
}
if ((req_dev_pwr_mode != hba->curr_dev_pwr_mode) &&
((ufshcd_is_runtime_pm(pm_op) && !hba->auto_bkops_enabled) ||
- ufshcd_is_system_pm(pm_op))) {
+ !ufshcd_is_runtime_pm(pm_op))) {
/* ensure that bkops is disabled */
ufshcd_disable_auto_bkops(hba);
ret = ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);
if (ret)
- goto out;
+ goto enable_gating;
}
ret = ufshcd_link_state_transition(hba, req_link_state, 1);
if (ret)
goto set_dev_active;
- /*
- * If UFS device is either in UFS_Sleep turn off VCC rail to
- * save some power.
- * If UFS device is in UFS_Poweroff state, all power supplies
- * (VCC, VCCQ, VCCQ2) can be turned off.
- * Ignore the error returned by ufshcd_toggle_vreg() as device
- * is anyway in low power state which would save some power.
- */
- if (ufshcd_is_ufs_dev_poweroff(hba))
- ufshcd_setup_vreg(hba, false);
- else if (ufshcd_is_ufs_dev_sleep(hba))
- ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+ ufshcd_vreg_set_lpm(hba);
disable_clks:
/*
@@ -4780,6 +5300,9 @@ disable_clks:
/* If link is active, device ref_clk can't be switched off */
__ufshcd_setup_clocks(hba, false, true);
+ hba->clk_gating.state = CLKS_OFF;
+ trace_ufshcd_clk_gating(dev_name(hba->dev),
+ to_string(hba->clk_gating.state));
/*
* Disable the host irq as host controller as there won't be any
* host controller trasanction expected till resume.
@@ -4791,10 +5314,7 @@ vops_resume:
if (hba->vops && hba->vops->resume)
hba->vops->resume(hba, pm_op);
set_link_active:
- if (ufshcd_is_ufs_dev_poweroff(hba))
- ufshcd_setup_vreg(hba, true);
- else if (ufshcd_is_ufs_dev_sleep(hba))
- ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
+ ufshcd_vreg_set_hpm(hba);
if (ufshcd_is_link_hibern8(hba) && !ufshcd_uic_hibern8_exit(hba))
ufshcd_set_link_active(hba);
else if (ufshcd_is_link_off(hba))
@@ -4802,6 +5322,9 @@ set_link_active:
set_dev_active:
if (!ufshcd_set_dev_pwr_mode(hba, UFS_ACTIVE_PWR_MODE))
ufshcd_disable_auto_bkops(hba);
+enable_gating:
+ hba->clk_gating.is_suspended = false;
+ ufshcd_release(hba);
out:
hba->pm_op_in_progress = 0;
return ret;
@@ -4829,21 +5352,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
if (ret)
goto out;
- if (hba->vops && hba->vops->setup_clocks) {
- ret = hba->vops->setup_clocks(hba, true);
- if (ret)
- goto disable_clks;
- }
-
/* enable the host irq as host controller would be active soon */
ufshcd_enable_irq(hba);
- /* Bring regulators back online if its turned off during suspend. */
- if (ufshcd_is_ufs_dev_poweroff(hba))
- ret = ufshcd_setup_vreg(hba, true);
- else if (ufshcd_is_ufs_dev_sleep(hba))
- ret = ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, true);
-
+ ret = ufshcd_vreg_set_hpm(hba);
if (ret)
goto disable_irq_and_vops_clks;
@@ -4881,6 +5393,10 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
}
ufshcd_disable_auto_bkops(hba);
+ hba->clk_gating.is_suspended = false;
+
+ /* Schedule clock gating in case of no access to UFS device yet */
+ ufshcd_release(hba);
goto out;
set_old_link_state:
@@ -4889,15 +5405,9 @@ vendor_suspend:
if (hba->vops && hba->vops->suspend)
hba->vops->suspend(hba, pm_op);
disable_vreg:
- if (ufshcd_is_ufs_dev_poweroff(hba))
- ufshcd_setup_vreg(hba, false);
- else if (ufshcd_is_ufs_dev_sleep(hba))
- ufshcd_toggle_vreg(hba->dev, hba->vreg_info.vcc, false);
+ ufshcd_vreg_set_lpm(hba);
disable_irq_and_vops_clks:
ufshcd_disable_irq(hba);
- if (hba->vops && hba->vops->setup_clocks)
- ret = hba->vops->setup_clocks(hba, false);
-disable_clks:
ufshcd_setup_clocks(hba, false);
out:
hba->pm_op_in_progress = 0;
@@ -5047,6 +5557,36 @@ int ufshcd_runtime_idle(struct ufs_hba *hba)
EXPORT_SYMBOL(ufshcd_runtime_idle);
/**
+ * ufshcd_shutdown - shutdown routine
+ * @hba: per adapter instance
+ *
+ * This function would power off both UFS device and UFS link.
+ *
+ * Returns 0 always to allow force shutdown even in case of errors.
+ */
+int ufshcd_shutdown(struct ufs_hba *hba)
+{
+ int ret = 0;
+
+ if (ufshcd_is_ufs_dev_poweroff(hba) && ufshcd_is_link_off(hba))
+ goto out;
+
+ if (pm_runtime_suspended(hba->dev)) {
+ ret = ufshcd_runtime_resume(hba);
+ if (ret)
+ goto out;
+ }
+
+ ret = ufshcd_suspend(hba, UFS_SHUTDOWN_PM);
+out:
+ if (ret)
+ dev_err(hba->dev, "%s failed, err %d\n", __func__, ret);
+ /* allow force shutdown even in case of errors */
+ return 0;
+}
+EXPORT_SYMBOL(ufshcd_shutdown);
+
+/**
* ufshcd_remove - de-allocate SCSI host and host memory space
* data structure memory
* @hba - per adapter instance
@@ -5060,6 +5600,7 @@ void ufshcd_remove(struct ufs_hba *hba)
scsi_host_put(hba->host);
+ ufshcd_exit_clk_gating(hba);
ufshcd_hba_exit(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -5205,11 +5746,12 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
/* Initialize device management tag acquire wait queue */
init_waitqueue_head(&hba->dev_cmd.tag_wq);
+ ufshcd_init_clk_gating(hba);
/* IRQ registration */
err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
if (err) {
dev_err(hba->dev, "request irq failed\n");
- goto out_disable;
+ goto exit_gating;
} else {
hba->is_irq_enabled = true;
}
@@ -5218,13 +5760,13 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
err = scsi_init_shared_tag_map(host, host->can_queue);
if (err) {
dev_err(hba->dev, "init shared queue failed\n");
- goto out_disable;
+ goto exit_gating;
}
err = scsi_add_host(host, hba->dev);
if (err) {
dev_err(hba->dev, "scsi_add_host failed\n");
- goto out_disable;
+ goto exit_gating;
}
/* Host controller enable */
@@ -5246,6 +5788,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
out_remove_scsi_host:
scsi_remove_host(hba->host);
+exit_gating:
+ ufshcd_exit_clk_gating(hba);
out_disable:
hba->is_irq_enabled = false;
scsi_host_put(host);
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index 5f518b522170..1c356c17db12 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -102,10 +102,12 @@ struct uic_command {
enum ufs_pm_op {
UFS_RUNTIME_PM,
UFS_SYSTEM_PM,
+ UFS_SHUTDOWN_PM,
};
#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
+#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
/* Host <-> Device UniPro Link state */
enum uic_link_state {
@@ -281,6 +283,38 @@ struct ufs_hba_variant_ops {
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
};
+/* clock gating state */
+enum clk_gating_state {
+ CLKS_OFF,
+ CLKS_ON,
+ REQ_CLKS_OFF,
+ REQ_CLKS_ON,
+};
+
+/**
+ * struct ufs_clk_gating - UFS clock gating related info
+ * @gate_work: worker to turn off clocks after some delay as specified in
+ * delay_ms
+ * @ungate_work: worker to turn on clocks that will be used in case of
+ * interrupt context
+ * @state: the current clocks state
+ * @delay_ms: gating delay in ms
+ * @is_suspended: clk gating is suspended when set to 1 which can be used
+ * during suspend/resume
+ * @delay_attr: sysfs attribute to control delay_attr
+ * @active_reqs: number of requests that are pending and should be waited for
+ * completion before gating clocks.
+ */
+struct ufs_clk_gating {
+ struct delayed_work gate_work;
+ struct work_struct ungate_work;
+ enum clk_gating_state state;
+ unsigned long delay_ms;
+ bool is_suspended;
+ struct device_attribute delay_attr;
+ int active_reqs;
+};
+
/**
* struct ufs_hba - per adapter private structure
* @mmio_base: UFSHCI base register address
@@ -408,6 +442,8 @@ struct ufs_hba {
*/
#define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS (1 << 7)
+ #define UFSHCD_QUIRK_BROKEN_2_TX_LANES (1 << 8)
+
wait_queue_head_t tm_wq;
wait_queue_head_t tm_tag_wq;
unsigned long tm_condition;
@@ -436,16 +472,39 @@ struct ufs_hba {
/* Device management request data */
struct ufs_dev_cmd dev_cmd;
+ /* Keeps information of the UFS device connected to this host */
+ struct ufs_dev_info dev_info;
bool auto_bkops_enabled;
struct ufs_vreg_info vreg_info;
struct list_head clk_list_head;
+ struct ufs_pa_layer_attr pwr_info;
+ struct ufs_clk_gating clk_gating;
+ /* Control to enable/disable host capabilities */
+ u32 caps;
+ /* Allow dynamic clk gating */
+#define UFSHCD_CAP_CLK_GATING (1 << 0)
+ /* Allow hiberb8 with clk gating */
+#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)
+
#ifdef CONFIG_DEBUG_FS
struct ufs_stats ufs_stats;
struct debugfs_files debugfs_files;
#endif
};
+/* Returns true if clocks can be gated. Otherwise false */
+static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
+{
+ return hba->caps & UFSHCD_CAP_CLK_GATING;
+}
+static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
+{
+ if (!(hba->quirks & UFSHCD_QUIRK_BROKEN_HIBERN8))
+ return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
+ else
+ return false;
+}
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \
@@ -502,6 +561,7 @@ extern int ufshcd_runtime_resume(struct ufs_hba *hba);
extern int ufshcd_runtime_idle(struct ufs_hba *hba);
extern int ufshcd_system_suspend(struct ufs_hba *hba);
extern int ufshcd_system_resume(struct ufs_hba *hba);
+extern int ufshcd_shutdown(struct ufs_hba *hba);
extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
@@ -572,4 +632,6 @@ int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
int ufshcd_query_descriptor(struct ufs_hba *hba, enum query_opcode opcode,
enum attr_idn idn, u8 index, u8 selector, u8 *desc_buf, int *buf_len);
+int ufshcd_hold(struct ufs_hba *hba, bool async);
+void ufshcd_release(struct ufs_hba *hba);
#endif /* End of Header */
diff --git a/drivers/scsi/ufs/unipro.h b/drivers/scsi/ufs/unipro.h
index baf45aeece05..fb572aa301ee 100644
--- a/drivers/scsi/ufs/unipro.h
+++ b/drivers/scsi/ufs/unipro.h
@@ -92,6 +92,24 @@ enum {
PA_HS_MODE_B = 2,
};
+enum ufs_pwm_gear_tag {
+ UFS_PWM_DONT_CHANGE, /* Don't change Gear */
+ UFS_PWM_G1, /* PWM Gear 1 (default for reset) */
+ UFS_PWM_G2, /* PWM Gear 2 */
+ UFS_PWM_G3, /* PWM Gear 3 */
+ UFS_PWM_G4, /* PWM Gear 4 */
+ UFS_PWM_G5, /* PWM Gear 5 */
+ UFS_PWM_G6, /* PWM Gear 6 */
+ UFS_PWM_G7, /* PWM Gear 7 */
+};
+
+enum ufs_hs_gear_tag {
+ UFS_HS_DONT_CHANGE, /* Don't change Gear */
+ UFS_HS_G1, /* HS Gear 1 (default for reset) */
+ UFS_HS_G2, /* HS Gear 2 */
+ UFS_HS_G3, /* HS Gear 3 */
+};
+
/*
* Data Link Layer Attributes
*/
diff --git a/drivers/sensors/sensors_class.c b/drivers/sensors/sensors_class.c
index 74e0d8d3551f..bbf168c10623 100644
--- a/drivers/sensors/sensors_class.c
+++ b/drivers/sensors/sensors_class.c
@@ -105,6 +105,78 @@ static ssize_t sensors_fifo_max_show(struct device *dev,
sensors_cdev->fifo_max_event_count);
}
+static ssize_t sensors_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+ unsigned long data = 0;
+
+ ret = kstrtoul(buf, 10, &data);
+ if (ret)
+ return ret;
+ if (data > 1) {
+ dev_err(dev, "Invalid value of input, input=%ld\n", data);
+ return -EINVAL;
+ }
+
+ if (sensors_cdev->sensors_enable == NULL) {
+ dev_err(dev, "Invalid sensor class enable handle\n");
+ return -EINVAL;
+ }
+ ret = sensors_cdev->sensors_enable(sensors_cdev, data);
+ if (ret)
+ return ret;
+
+ sensors_cdev->enabled = data;
+ return size;
+}
+
+
+static ssize_t sensors_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ sensors_cdev->enabled);
+}
+
+static ssize_t sensors_delay_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+ unsigned long data = 0;
+
+ ret = kstrtoul(buf, 10, &data);
+ if (ret)
+ return ret;
+ /* The data unit is millisecond, the min_delay unit is microseconds. */
+ if ((data * 1000) < sensors_cdev->min_delay) {
+ dev_err(dev, "Invalid value of delay, delay=%ld\n", data);
+ return -EINVAL;
+ }
+ if (sensors_cdev->sensors_poll_delay == NULL) {
+ dev_err(dev, "Invalid sensor class delay handle\n");
+ return -EINVAL;
+ }
+ ret = sensors_cdev->sensors_poll_delay(sensors_cdev, data);
+ if (ret)
+ return ret;
+
+ sensors_cdev->delay_msec = data;
+ return size;
+}
+
+static ssize_t sensors_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev);
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ sensors_cdev->delay_msec);
+}
+
+
static struct device_attribute sensors_class_attrs[] = {
__ATTR(name, 0444, sensors_name_show, NULL),
__ATTR(vendor, 0444, sensors_vendor_show, NULL),
@@ -117,6 +189,8 @@ static struct device_attribute sensors_class_attrs[] = {
__ATTR(min_delay, 0444, sensors_min_delay_show, NULL),
__ATTR(fifo_reserved_event_count, 0444, sensors_fifo_event_show, NULL),
__ATTR(fifo_max_event_count, 0444, sensors_fifo_max_show, NULL),
+ __ATTR(enable, 0664, sensors_enable_show, sensors_enable_store),
+ __ATTR(poll_delay, 0664, sensors_delay_show, sensors_delay_store),
__ATTR_NULL,
};
diff --git a/drivers/sh/clk/core.c b/drivers/sh/clk/core.c
index 7715de2629c1..74727851820d 100644
--- a/drivers/sh/clk/core.c
+++ b/drivers/sh/clk/core.c
@@ -63,12 +63,12 @@ void clk_rate_table_build(struct clk *clk,
else
freq = clk->parent->rate * mult / div;
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = freq;
}
/* Termination entry */
- freq_table[i].index = i;
+ freq_table[i].driver_data = i;
freq_table[i].frequency = CPUFREQ_TABLE_END;
}
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
index 74a4fd953bfa..7f09ba780045 100644
--- a/drivers/slimbus/slim-msm-ngd.c
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -27,7 +27,7 @@
#include <linux/timer.h>
#include <mach/sps.h>
#include "slim-msm.h"
-#include <mach/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr.h>
#define NGD_SLIM_NAME "ngd_msm_ctrl"
#define SLIM_LA_MGR 0xFF
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
new file mode 100644
index 000000000000..b6f0ff198396
--- /dev/null
+++ b/drivers/soc/Kconfig
@@ -0,0 +1 @@
+source "drivers/soc/qcom/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
new file mode 100644
index 000000000000..9d8ecdc6a875
--- /dev/null
+++ b/drivers/soc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_MSM) += qcom/
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
new file mode 100644
index 000000000000..1ea417e3b313
--- /dev/null
+++ b/drivers/soc/qcom/Kconfig
@@ -0,0 +1,43 @@
+# When adding new entries keep the list in alphabetical order
+
+if ARCH_MSM
+
+config MSM_SMEM
+ depends on REMOTE_SPINLOCK_MSM
+ bool "MSM Shared Memory (SMEM)"
+ help
+ Support for the shared memory interface between the various
+ processors in the System on a Chip (SoC) which allows basic
+ inter-processor communication.
+
+config MSM_QDSP6_APRV2
+ bool "Audio QDSP6 APRv2 support"
+ depends on MSM_SMD
+ help
+ Enable APRv2 IPC protocol support between
+ application processor and QDSP6. APR is
+ used by audio driver to configure QDSP6's
+ ASM, ADM and AFE.
+
+config MSM_QDSP6_APRV3
+ bool "Audio QDSP6 APRv3 support"
+ depends on MSM_SMD
+ help
+ Enable APRv2 IPC protocol support between
+ application processor and QDSP6. APR is
+ used by audio driver to configure QDSP6v2's
+ ASM, ADM and AFE.
+
+config MSM_ADSP_LOADER
+ tristate "ADSP loader support"
+ select SND_SOC_MSM_APRV2_INTF
+ depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3
+ help
+ Enable ADSP image loader.
+ The ADSP loader brings ADSP out of reset
+ for the platforms that use APRv2.
+ Say M if you want to enable this module.
+
+source "drivers/soc/qcom/memshare/Kconfig"
+
+endif # ARCH_MSM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
new file mode 100644
index 000000000000..83ec3bab5f99
--- /dev/null
+++ b/drivers/soc/qcom/Makefile
@@ -0,0 +1,6 @@
+# When adding new entries keep the list in alphabetical order
+
+obj-y += qdsp6v2/
+
+obj-$(CONFIG_MSM_SMEM) += smem.o smem_debug.o
+obj-$(CONFIG_MSM_QMI_INTERFACE) += memshare/
diff --git a/drivers/soc/qcom/memshare/Kconfig b/drivers/soc/qcom/memshare/Kconfig
new file mode 100644
index 000000000000..7eb1415b350b
--- /dev/null
+++ b/drivers/soc/qcom/memshare/Kconfig
@@ -0,0 +1,9 @@
+config MEM_SHARE_QMI_SERVICE
+ depends on MSM_QMI_INTERFACE
+ bool "Shared Heap for external processors"
+ help
+ Memory Share Kernel Qualcomm Messaging Interface Service
+ receives requests from Modem Processor Sub System
+ for heap alloc/free from Application Processor
+ Sub System and send a response back to client with
+ proper handle/address.
diff --git a/drivers/soc/qcom/memshare/Makefile b/drivers/soc/qcom/memshare/Makefile
new file mode 100644
index 000000000000..c119ad317ed7
--- /dev/null
+++ b/drivers/soc/qcom/memshare/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MSM_QMI_INTERFACE) := heap_mem_ext_v01.o msm_memshare.o \ No newline at end of file
diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.c b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c
new file mode 100644
index 000000000000..3f9fe995fc87
--- /dev/null
+++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.c
@@ -0,0 +1,138 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/qmi_encdec.h>
+#include <mach/msm_qmi_interface.h>
+#include "heap_mem_ext_v01.h"
+
+struct elem_info mem_alloc_req_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct mem_alloc_req_msg_v01,
+ num_bytes),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct mem_alloc_req_msg_v01,
+ block_alignment_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct mem_alloc_req_msg_v01,
+ block_alignment),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info mem_alloc_resp_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_2_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct mem_alloc_resp_msg_v01,
+ resp),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct mem_alloc_resp_msg_v01,
+ handle_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x10,
+ .offset = offsetof(struct mem_alloc_resp_msg_v01,
+ handle),
+ },
+ {
+ .data_type = QMI_OPT_FLAG,
+ .elem_len = 1,
+ .elem_size = sizeof(uint8_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct mem_alloc_resp_msg_v01,
+ num_bytes_valid),
+ },
+ {
+ .data_type = QMI_UNSIGNED_4_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint32_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x11,
+ .offset = offsetof(struct mem_alloc_resp_msg_v01,
+ num_bytes),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info mem_free_req_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_UNSIGNED_8_BYTE,
+ .elem_len = 1,
+ .elem_size = sizeof(uint64_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct mem_free_req_msg_v01,
+ handle),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
+
+struct elem_info mem_free_resp_msg_data_v01_ei[] = {
+ {
+ .data_type = QMI_SIGNED_2_BYTE_ENUM,
+ .elem_len = 1,
+ .elem_size = sizeof(uint16_t),
+ .is_array = NO_ARRAY,
+ .tlv_type = 0x01,
+ .offset = offsetof(struct mem_free_resp_msg_v01,
+ resp),
+ },
+ {
+ .data_type = QMI_EOTI,
+ .is_array = NO_ARRAY,
+ .tlv_type = QMI_COMMON_TLV_TYPE,
+ },
+};
diff --git a/drivers/soc/qcom/memshare/heap_mem_ext_v01.h b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h
new file mode 100644
index 000000000000..bc2a8cd69b98
--- /dev/null
+++ b/drivers/soc/qcom/memshare/heap_mem_ext_v01.h
@@ -0,0 +1,143 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef HEAP_MEM_EXT_SERVICE_01_H
+#define HEAP_MEM_EXT_SERVICE_01_H
+
+#include <mach/msm_qmi_interface.h>
+
+#define MEM_ALLOC_REQ_MAX_MSG_LEN_V01 255
+#define MEM_FREE_REQ_MAX_MSG_LEN_V01 255
+
+enum dhms_mem_block_align_enum_v01 {
+ /* To force a 32 bit signed enum. Do not change or use
+ */
+ DHMS_MEM_BLOCK_ALIGN_ENUM_MIN_ENUM_VAL_V01 = -2147483647,
+ /* Align allocated memory by 2 bytes */
+ DHMS_MEM_BLOCK_ALIGN_2_V01 = 0,
+ /* Align allocated memory by 4 bytes */
+ DHMS_MEM_BLOCK_ALIGN_4_V01 = 1,
+ /**< Align allocated memory by 8 bytes */
+ DHMS_MEM_BLOCK_ALIGN_8_V01 = 2,
+ /**< Align allocated memory by 16 bytes */
+ DHMS_MEM_BLOCK_ALIGN_16_V01 = 3,
+ /**< Align allocated memory by 32 bytes */
+ DHMS_MEM_BLOCK_ALIGN_32_V01 = 4,
+ /**< Align allocated memory by 64 bytes */
+ DHMS_MEM_BLOCK_ALIGN_64_V01 = 5,
+ /**< Align allocated memory by 128 bytes */
+ DHMS_MEM_BLOCK_ALIGN_128_V01 = 6,
+ /**< Align allocated memory by 256 bytes */
+ DHMS_MEM_BLOCK_ALIGN_256_V01 = 7,
+ /**< Align allocated memory by 512 bytes */
+ DHMS_MEM_BLOCK_ALIGN_512_V01 = 8,
+ /**< Align allocated memory by 1024 bytes */
+ DHMS_MEM_BLOCK_ALIGN_1K_V01 = 9,
+ /**< Align allocated memory by 2048 bytes */
+ DHMS_MEM_BLOCK_ALIGN_2K_V01 = 10,
+ /**< Align allocated memory by 4096 bytes */
+ DHMS_MEM_BLOCK_ALIGN_4K_V01 = 11,
+ DHMS_MEM_BLOCK_ALIGN_ENUM_MAX_ENUM_VAL_V01 = 2147483647
+ /* To force a 32 bit signed enum. Do not change or use
+ */
+};
+
+/* Request Message; This command is used for getting
+ * the multiple physically contiguous
+ * memory blocks from the server memory subsystem
+ */
+struct mem_alloc_req_msg_v01 {
+
+ /* Mandatory */
+ /*requested size*/
+ uint32_t num_bytes;
+
+ /* Optional */
+ /* Must be set to true if block_alignment
+ * is being passed
+ */
+ uint8_t block_alignment_valid;
+ /* The block alignment for the memory block to be allocated
+ */
+ enum dhms_mem_block_align_enum_v01 block_alignment;
+}; /* Message */
+
+/* Response Message; This command is used for getting
+ * the multiple physically contiguous memory blocks
+ * from the server memory subsystem
+ */
+struct mem_alloc_resp_msg_v01 {
+
+ /* Mandatory */
+ /* Result Code */
+ /* The result of the requested memory operation
+ */
+ enum qmi_result_type_v01 resp;
+ /* Optional */
+ /* Memory Block Handle
+ */
+ /* Must be set to true if handle is being passed
+ */
+ uint8_t handle_valid;
+ /* The physical address of the memory allocated on the HLOS
+ */
+ uint64_t handle;
+ /* Optional */
+ /* Memory block size */
+ /* Must be set to true if num_bytes is being passed
+ */
+ uint8_t num_bytes_valid;
+ /* The number of bytes actually allocated for the request.
+ * This value can be smaller than the size requested in
+ * QMI_DHMS_MEM_ALLOC_REQ_MSG.
+ */
+ uint32_t num_bytes;
+}; /* Message */
+
+/* Request Message; This command is used for releasing
+ * the multiple physically contiguous
+ * memory blocks to the server memory subsystem
+ */
+struct mem_free_req_msg_v01 {
+
+ /* Mandatory */
+ /* Physical address of memory to be freed
+ */
+ uint32_t handle;
+}; /* Message */
+
+/* Response Message; This command is used for releasing
+ * the multiple physically contiguous
+ * memory blocks to the server memory subsystem
+ */
+struct mem_free_resp_msg_v01 {
+
+ /* Mandatory */
+ /* Result of the requested memory operation, todo,
+ * need to check the async operation for free
+ */
+ enum qmi_result_type_v01 resp;
+}; /* Message */
+
+extern struct elem_info mem_alloc_req_msg_data_v01_ei[];
+extern struct elem_info mem_alloc_resp_msg_data_v01_ei[];
+extern struct elem_info mem_free_req_msg_data_v01_ei[];
+extern struct elem_info mem_free_resp_msg_data_v01_ei[];
+
+/*Service Message Definition*/
+#define MEM_ALLOC_REQ_MSG_V01 0x0020
+#define MEM_ALLOC_RESP_MSG_V01 0x0020
+#define MEM_FREE_REQ_MSG_V01 0x0021
+#define MEM_FREE_RESP_MSG_V01 0x0021
+
+#endif
diff --git a/drivers/soc/qcom/memshare/msm_memshare.c b/drivers/soc/qcom/memshare/msm_memshare.c
new file mode 100644
index 000000000000..7371717f268a
--- /dev/null
+++ b/drivers/soc/qcom/memshare/msm_memshare.c
@@ -0,0 +1,312 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/err.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <mach/scm.h>
+#include <mach/msm_qmi_interface.h>
+#include "msm_memshare.h"
+#include "heap_mem_ext_v01.h"
+#define MEM_SHARE_SERVICE_SVC_ID 0x00000034
+#define MEM_SHARE_SERVICE_INS_ID 1
+#define MEM_SHARE_SERVICE_VERS 1
+
+static struct qmi_handle *mem_share_svc_handle;
+static void mem_share_svc_recv_msg(struct work_struct *work);
+static DECLARE_DELAYED_WORK(work_recv_msg, mem_share_svc_recv_msg);
+static struct workqueue_struct *mem_share_svc_workqueue;
+static void *curr_conn;
+struct mutex connection;
+static struct mem_blocks memblock;
+struct mutex mem_share;
+struct mutex mem_free;
+static uint32_t size;
+
+static struct msg_desc mem_share_svc_alloc_req_desc = {
+ .max_msg_len = MEM_ALLOC_REQ_MAX_MSG_LEN_V01,
+ .msg_id = MEM_ALLOC_REQ_MSG_V01,
+ .ei_array = mem_alloc_req_msg_data_v01_ei,
+};
+
+static struct msg_desc mem_share_svc_alloc_resp_desc = {
+ .max_msg_len = MEM_ALLOC_REQ_MAX_MSG_LEN_V01,
+ .msg_id = MEM_ALLOC_RESP_MSG_V01,
+ .ei_array = mem_alloc_resp_msg_data_v01_ei,
+};
+
+static struct msg_desc mem_share_svc_free_req_desc = {
+ .max_msg_len = MEM_FREE_REQ_MAX_MSG_LEN_V01,
+ .msg_id = MEM_FREE_REQ_MSG_V01,
+ .ei_array = mem_free_req_msg_data_v01_ei,
+};
+
+static struct msg_desc mem_share_svc_free_resp_desc = {
+ .max_msg_len = MEM_FREE_REQ_MAX_MSG_LEN_V01,
+ .msg_id = MEM_FREE_RESP_MSG_V01,
+ .ei_array = mem_free_resp_msg_data_v01_ei,
+};
+
+
+static int handle_alloc_req(void *req_h, void *req)
+{
+ struct mem_alloc_req_msg_v01 *alloc_req;
+ struct mem_alloc_resp_msg_v01 alloc_resp;
+ int rc;
+
+ alloc_req = (struct mem_alloc_req_msg_v01 *)req;
+ pr_debug("%s: Received Alloc Request\n", __func__);
+ pr_debug("%s: req->num_bytes = %d\n", __func__, alloc_req->num_bytes);
+ alloc_resp.resp = QMI_RESULT_FAILURE_V01;
+ mutex_lock(&mem_share);
+ memset(&alloc_resp, 0, sizeof(struct mem_alloc_resp_msg_v01));
+ rc = memshare_alloc(alloc_req->num_bytes,
+ alloc_req->block_alignment,
+ &memblock);
+ if (rc) {
+ mutex_unlock(&mem_share);
+ return -ENOMEM;
+ }
+
+ alloc_resp.num_bytes_valid = 1;
+ alloc_resp.num_bytes = alloc_req->num_bytes;
+ size = alloc_req->num_bytes;
+ alloc_resp.handle_valid = 1;
+ alloc_resp.handle = memblock.phy_addr;
+ alloc_resp.resp = QMI_RESULT_SUCCESS_V01;
+ mutex_unlock(&mem_share);
+ pr_debug("alloc_resp.num_bytes :%d, alloc_resp.handle :%lx, alloc_resp.mem_req_result :%lx\n",
+ alloc_resp.num_bytes,
+ (unsigned long int)alloc_resp.handle,
+ (unsigned long int)alloc_resp.resp);
+ rc = qmi_send_resp_from_cb(mem_share_svc_handle, curr_conn, req_h,
+ &mem_share_svc_alloc_resp_desc, &alloc_resp,
+ sizeof(alloc_resp));
+ return rc;
+}
+static int handle_free_req(void *req_h, void *req)
+{
+ struct mem_free_req_msg_v01 *free_req;
+ struct mem_free_resp_msg_v01 free_resp;
+ int rc;
+
+ free_req = (struct mem_free_req_msg_v01 *)req;
+ pr_debug("%s: Received Free Request\n", __func__);
+ mutex_lock(&mem_free);
+ free_resp.resp = QMI_RESULT_FAILURE_V01;
+
+ memset(&free_resp, 0, sizeof(struct mem_free_resp_msg_v01));
+ pr_debug("In %s: pblk->virtual_addr :%lx, pblk->phy_addr %lx\n,size: %d",
+ __func__,
+ (unsigned long int)memblock.virtual_addr,
+ (unsigned long int)free_req->handle, size);
+ dma_free_coherent(NULL, size,
+ memblock.virtual_addr, free_req->handle);
+ mutex_unlock(&mem_free);
+ free_resp.resp = QMI_RESULT_SUCCESS_V01;
+
+ rc = qmi_send_resp_from_cb(mem_share_svc_handle, curr_conn, req_h,
+ &mem_share_svc_free_resp_desc, &free_resp,
+ sizeof(free_resp));
+
+ return rc;
+}
+
+static int mem_share_svc_connect_cb(struct qmi_handle *handle,
+ void *conn_h)
+{
+ if (mem_share_svc_handle != handle || !conn_h)
+ return -EINVAL;
+ mutex_lock(&connection);
+ if (curr_conn) {
+ pr_err("%s: Service is busy\n", __func__);
+ mutex_unlock(&connection);
+ return -EBUSY;
+ }
+ curr_conn = conn_h;
+ mutex_unlock(&connection);
+ return 0;
+}
+
+static int mem_share_svc_disconnect_cb(struct qmi_handle *handle,
+ void *conn_h)
+{
+ mutex_lock(&connection);
+ if (mem_share_svc_handle != handle || curr_conn != conn_h) {
+ mutex_unlock(&connection);
+ return -EINVAL;
+ }
+ curr_conn = NULL;
+ mutex_unlock(&connection);
+ return 0;
+}
+
+static int mem_share_svc_req_desc_cb(unsigned int msg_id,
+ struct msg_desc **req_desc)
+{
+ int rc;
+
+ pr_debug("memshare: In %s\n", __func__);
+ switch (msg_id) {
+ case MEM_ALLOC_REQ_MSG_V01:
+ *req_desc = &mem_share_svc_alloc_req_desc;
+ rc = sizeof(struct mem_alloc_req_msg_v01);
+ break;
+
+ case MEM_FREE_REQ_MSG_V01:
+ *req_desc = &mem_share_svc_free_req_desc;
+ rc = sizeof(struct mem_free_req_msg_v01);
+ break;
+
+ default:
+ rc = -ENOTSUPP;
+ break;
+ }
+ return rc;
+}
+
+static int mem_share_svc_req_cb(struct qmi_handle *handle, void *conn_h,
+ void *req_h, unsigned int msg_id, void *req)
+{
+ int rc;
+
+ pr_debug("memshare: In %s\n", __func__);
+ if (mem_share_svc_handle != handle || curr_conn != conn_h)
+ return -EINVAL;
+
+ switch (msg_id) {
+ case MEM_ALLOC_REQ_MSG_V01:
+ rc = handle_alloc_req(req_h, req);
+ break;
+
+ case MEM_FREE_REQ_MSG_V01:
+ rc = handle_free_req(req_h, req);
+ break;
+
+ default:
+ rc = -ENOTSUPP;
+ break;
+ }
+ return rc;
+}
+
+static void mem_share_svc_recv_msg(struct work_struct *work)
+{
+ int rc;
+
+ pr_debug("memshare: In %s\n", __func__);
+ do {
+ pr_debug("%s: Notified about a Receive Event", __func__);
+ } while ((rc = qmi_recv_msg(mem_share_svc_handle)) == 0);
+
+ if (rc != -ENOMSG)
+ pr_err("%s: Error receiving message\n", __func__);
+}
+
+static void qmi_mem_share_svc_ntfy(struct qmi_handle *handle,
+ enum qmi_event_type event, void *priv)
+{
+ pr_debug("memshare: In %s\n", __func__);
+ switch (event) {
+ case QMI_RECV_MSG:
+ queue_delayed_work(mem_share_svc_workqueue,
+ &work_recv_msg, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static struct qmi_svc_ops_options mem_share_svc_ops_options = {
+ .version = 1,
+ .service_id = MEM_SHARE_SERVICE_SVC_ID,
+ .service_vers = MEM_SHARE_SERVICE_VERS,
+ .service_ins = MEM_SHARE_SERVICE_INS_ID,
+ .connect_cb = mem_share_svc_connect_cb,
+ .disconnect_cb = mem_share_svc_disconnect_cb,
+ .req_desc_cb = mem_share_svc_req_desc_cb,
+ .req_cb = mem_share_svc_req_cb,
+};
+
+int memshare_alloc(unsigned int block_size,
+ unsigned int block_algn,
+ struct mem_blocks *pblk)
+{
+
+ int ret;
+
+ pr_debug("%s: memshare_alloc called", __func__);
+ if (!pblk) {
+ pr_err("%s: Failed to alloc\n", __func__);
+ return -ENOMEM;
+ }
+
+ pblk->virtual_addr = dma_alloc_coherent(NULL, block_size,
+ &pblk->phy_addr, GFP_KERNEL);
+ if (pblk->virtual_addr == NULL) {
+ pr_err("allocation failed, %d\n", block_size);
+ ret = -ENOMEM;
+ return ret;
+ }
+ pr_debug("pblk->phy_addr :%lx, pblk->virtual_addr %lx\n",
+ (unsigned long int)pblk->phy_addr,
+ (unsigned long int)pblk->virtual_addr);
+ return 0;
+}
+
+static int __init memshare_init(void)
+{
+ int rc;
+
+ mem_share_svc_workqueue =
+ create_singlethread_workqueue("mem_share_svc");
+ if (!mem_share_svc_workqueue)
+ return -ENOMEM;
+
+ mem_share_svc_handle = qmi_handle_create(qmi_mem_share_svc_ntfy, NULL);
+ if (!mem_share_svc_handle) {
+ pr_err("%s: Creating mem_share_svc qmi handle failed\n",
+ __func__);
+ destroy_workqueue(mem_share_svc_workqueue);
+ return -ENOMEM;
+ }
+ rc = qmi_svc_register(mem_share_svc_handle, &mem_share_svc_ops_options);
+ if (rc < 0) {
+ pr_err("%s: Registering mem share svc failed %d\n",
+ __func__, rc);
+ qmi_handle_destroy(mem_share_svc_handle);
+ destroy_workqueue(mem_share_svc_workqueue);
+ return rc;
+ }
+ mutex_init(&connection);
+ mutex_init(&mem_share);
+ mutex_init(&mem_free);
+ pr_info("memshare: memshare_init successful\n");
+
+ return 0;
+}
+
+static void __exit memshare_exit(void)
+{
+ qmi_svc_unregister(mem_share_svc_handle);
+ flush_workqueue(mem_share_svc_workqueue);
+ qmi_handle_destroy(mem_share_svc_handle);
+ destroy_workqueue(mem_share_svc_workqueue);
+}
+
+module_init(memshare_init);
+module_exit(memshare_exit);
+
+MODULE_DESCRIPTION("Mem Share QMI Service Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/memshare/msm_memshare.h b/drivers/soc/qcom/memshare/msm_memshare.h
new file mode 100644
index 000000000000..9f12f3a267a3
--- /dev/null
+++ b/drivers/soc/qcom/memshare/msm_memshare.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _LINUX_MEM_SHARE_H
+#define _LINUX_MEM_SHARE_H
+
+
+struct mem_blocks {
+ /* start address of the memory block reserved by server memory
+ * subsystem to client
+ */
+ phys_addr_t phy_addr;
+ /* virtual address during allocation of the physical memory
+ */
+ void *virtual_addr;
+};
+int memshare_alloc(unsigned int block_size,
+ unsigned int block_algn,
+ struct mem_blocks *pblk);
+void memshare_free(unsigned int block_size,
+ struct mem_blocks *pblk);
+#endif /* _LINUX_MEM_SHARE_H */
diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile
new file mode 100644
index 000000000000..f4770d11596f
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o dsp_debug.o
+obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += msm_audio_ion.o
+obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
new file mode 100644
index 000000000000..d2a463194353
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <mach/subsystem_restart.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+
+#define Q6_PIL_GET_DELAY_MS 100
+#define BOOT_CMD 1
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count);
+
+struct adsp_loader_private {
+ void *pil_h;
+ struct kobject *boot_adsp_obj;
+ struct attribute_group *attr_group;
+};
+
+static struct kobj_attribute adsp_boot_attribute =
+ __ATTR(boot, 0220, NULL, adsp_boot_store);
+
+static struct attribute *attrs[] = {
+ &adsp_boot_attribute.attr,
+ NULL,
+};
+
+static struct platform_device *adsp_private;
+
+static void adsp_loader_do(struct platform_device *pdev)
+{
+
+ struct adsp_loader_private *priv = NULL;
+
+ const char *adsp_dt = "qcom,adsp-state";
+ int rc = 0;
+ u32 adsp_state;
+
+ if (!pdev) {
+ dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
+ goto fail;
+ }
+
+ if (!pdev->dev.of_node) {
+ dev_err(&pdev->dev,
+ "%s: Device tree information missing\n", __func__);
+ goto fail;
+ }
+
+ rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "%s: ADSP state = %x\n", __func__, adsp_state);
+ goto fail;
+ }
+
+ if (adsp_state == APR_SUBSYS_DOWN) {
+ priv = platform_get_drvdata(pdev);
+ if (!priv) {
+ dev_err(&pdev->dev,
+ " %s: Private data get failed\n", __func__);
+ goto fail;
+ }
+
+
+ priv->pil_h = subsystem_get("adsp");
+ if (IS_ERR(priv->pil_h)) {
+ dev_err(&pdev->dev, "%s: pil get failed,\n",
+ __func__);
+ goto fail;
+ }
+
+ /* Set the state of the ADSP in APR driver */
+ apr_set_q6_state(APR_SUBSYS_LOADED);
+ } else if (adsp_state == APR_SUBSYS_LOADED) {
+ dev_dbg(&pdev->dev,
+ "%s: ADSP state = %x\n", __func__, adsp_state);
+ apr_set_q6_state(APR_SUBSYS_LOADED);
+ }
+
+
+ dev_info(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__);
+ return;
+fail:
+
+ dev_err(&pdev->dev, "%s: Q6/ADSP image loading failed\n", __func__);
+ return;
+}
+
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int boot = 0;
+ sscanf(buf, "%du", &boot);
+
+ if (boot == BOOT_CMD) {
+ pr_debug("%s:going to call adsp_loader_do", __func__);
+ adsp_loader_do(adsp_private);
+ }
+ return count;
+}
+
+static int adsp_loader_init_sysfs(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct adsp_loader_private *priv = NULL;
+ adsp_private = NULL;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "%s: memory alloc failed\n", __func__);
+ ret = -ENOMEM;
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ priv->pil_h = NULL;
+ priv->boot_adsp_obj = NULL;
+ priv->attr_group = devm_kzalloc(&pdev->dev,
+ sizeof(*(priv->attr_group)),
+ GFP_KERNEL);
+ if (!priv->attr_group) {
+ dev_err(&pdev->dev, "%s: malloc attr_group failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error_return;
+ }
+
+ priv->attr_group->attrs = attrs;
+
+ priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj);
+ if (!priv->boot_adsp_obj) {
+ dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
+ __func__);
+ ret = -ENOMEM;
+ goto error_return;
+ }
+
+ ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
+ __func__, ret);
+ goto error_return;
+ }
+
+ adsp_private = pdev;
+
+ return 0;
+
+error_return:
+
+ if (priv->boot_adsp_obj) {
+ kobject_del(priv->boot_adsp_obj);
+ priv->boot_adsp_obj = NULL;
+ }
+
+ return ret;
+}
+
+static int adsp_loader_remove(struct platform_device *pdev)
+{
+ struct adsp_loader_private *priv = NULL;
+
+ priv = platform_get_drvdata(pdev);
+
+ if (!priv)
+ return 0;
+
+ if (priv->pil_h) {
+ subsystem_put(priv->pil_h);
+ priv->pil_h = NULL;
+ }
+
+ if (priv->boot_adsp_obj) {
+ sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group);
+ kobject_del(priv->boot_adsp_obj);
+ priv->boot_adsp_obj = NULL;
+ }
+
+ return 0;
+}
+
+static int adsp_loader_probe(struct platform_device *pdev)
+{
+ int ret = adsp_loader_init_sysfs(pdev);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id adsp_loader_dt_match[] = {
+ { .compatible = "qcom,adsp-loader" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adsp_loader_dt_match);
+
+static struct platform_driver adsp_loader_driver = {
+ .driver = {
+ .name = "adsp-loader",
+ .owner = THIS_MODULE,
+ .of_match_table = adsp_loader_dt_match,
+ },
+ .probe = adsp_loader_probe,
+ .remove = adsp_loader_remove,
+};
+
+static int __init adsp_loader_init(void)
+{
+ return platform_driver_register(&adsp_loader_driver);
+}
+module_init(adsp_loader_init);
+
+static void __exit adsp_loader_exit(void)
+{
+ platform_driver_unregister(&adsp_loader_driver);
+}
+module_exit(adsp_loader_exit);
+
+MODULE_DESCRIPTION("ADSP Loader module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
new file mode 100644
index 000000000000..c3dd3d367099
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -0,0 +1,834 @@
+/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <sound/apr_audio-v2.h>
+#include <mach/subsystem_restart.h>
+#include <mach/msm_smd.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+#include <mach/subsystem_notif.h>
+#include <mach/subsystem_restart.h>
+
+static struct apr_q6 q6;
+static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
+
+static wait_queue_head_t dsp_wait;
+static wait_queue_head_t modem_wait;
+/* Subsystem restart: QDSP6 data, functions */
+static struct workqueue_struct *apr_reset_workqueue;
+static void apr_reset_deregister(struct work_struct *work);
+struct apr_reset_work {
+ void *handle;
+ struct work_struct work;
+};
+
+struct apr_svc_table {
+ char name[64];
+ int idx;
+ int id;
+ int client_id;
+};
+
+static const struct apr_svc_table svc_tbl_qdsp6[] = {
+ {
+ .name = "AFE",
+ .idx = 0,
+ .id = APR_SVC_AFE,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "ASM",
+ .idx = 1,
+ .id = APR_SVC_ASM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "ADM",
+ .idx = 2,
+ .id = APR_SVC_ADM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CORE",
+ .idx = 3,
+ .id = APR_SVC_ADSP_CORE,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "TEST",
+ .idx = 4,
+ .id = APR_SVC_TEST_CLIENT,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "MVM",
+ .idx = 5,
+ .id = APR_SVC_ADSP_MVM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CVS",
+ .idx = 6,
+ .id = APR_SVC_ADSP_CVS,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "CVP",
+ .idx = 7,
+ .id = APR_SVC_ADSP_CVP,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "USM",
+ .idx = 8,
+ .id = APR_SVC_USM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+ {
+ .name = "VIDC",
+ .idx = 9,
+ .id = APR_SVC_VIDC,
+ },
+ {
+ .name = "LSM",
+ .idx = 10,
+ .id = APR_SVC_LSM,
+ .client_id = APR_CLIENT_AUDIO,
+ },
+};
+
+static struct apr_svc_table svc_tbl_voice[] = {
+ {
+ .name = "VSM",
+ .idx = 0,
+ .id = APR_SVC_VSM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "VPM",
+ .idx = 1,
+ .id = APR_SVC_VPM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "MVS",
+ .idx = 2,
+ .id = APR_SVC_MVS,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "MVM",
+ .idx = 3,
+ .id = APR_SVC_MVM,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "CVS",
+ .idx = 4,
+ .id = APR_SVC_CVS,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "CVP",
+ .idx = 5,
+ .id = APR_SVC_CVP,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "SRD",
+ .idx = 6,
+ .id = APR_SVC_SRD,
+ .client_id = APR_CLIENT_VOICE,
+ },
+ {
+ .name = "TEST",
+ .idx = 7,
+ .id = APR_SVC_TEST_CLIENT,
+ .client_id = APR_CLIENT_VOICE,
+ },
+};
+
+enum apr_subsys_state apr_get_modem_state(void)
+{
+ return atomic_read(&q6.modem_state);
+}
+
+void apr_set_modem_state(enum apr_subsys_state state)
+{
+ atomic_set(&q6.modem_state, state);
+}
+
+enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
+ enum apr_subsys_state new)
+{
+ return atomic_cmpxchg(&q6.modem_state, prev, new);
+}
+
+enum apr_subsys_state apr_get_q6_state(void)
+{
+ return atomic_read(&q6.q6_state);
+}
+EXPORT_SYMBOL_GPL(apr_get_q6_state);
+
+int apr_set_q6_state(enum apr_subsys_state state)
+{
+ pr_debug("%s: setting adsp state %d\n", __func__, state);
+ if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
+ return -EINVAL;
+ atomic_set(&q6.q6_state, state);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(apr_set_q6_state);
+
+enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
+ enum apr_subsys_state new)
+{
+ return atomic_cmpxchg(&q6.q6_state, prev, new);
+}
+
+int apr_wait_for_device_up(int dest_id)
+{
+ int rc = -1;
+ if (dest_id == APR_DEST_MODEM)
+ rc = wait_event_interruptible_timeout(modem_wait,
+ (apr_get_modem_state() == APR_SUBSYS_UP),
+ (1 * HZ));
+ else if (dest_id == APR_DEST_QDSP6)
+ rc = wait_event_interruptible_timeout(dsp_wait,
+ (apr_get_q6_state() == APR_SUBSYS_UP),
+ (1 * HZ));
+ else
+ pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
+ /* returns left time */
+ return rc;
+}
+
+int apr_load_adsp_image(void)
+{
+ int rc = 0;
+ mutex_lock(&q6.lock);
+ if (apr_get_q6_state() == APR_SUBSYS_UP) {
+ q6.pil = subsystem_get("adsp");
+ if (IS_ERR(q6.pil)) {
+ rc = PTR_ERR(q6.pil);
+ pr_err("APR: Unable to load q6 image, error:%d\n", rc);
+ } else {
+ apr_set_q6_state(APR_SUBSYS_LOADED);
+ pr_debug("APR: Image is loaded, stated\n");
+ }
+ } else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
+ pr_debug("APR: q6 image already loaded\n");
+ } else {
+ pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
+ }
+ mutex_unlock(&q6.lock);
+ return rc;
+}
+
+struct apr_client *apr_get_client(int dest_id, int client_id)
+{
+ return &client[dest_id][client_id];
+}
+
+int apr_send_pkt(void *handle, uint32_t *buf)
+{
+ struct apr_svc *svc = handle;
+ struct apr_client *clnt;
+ struct apr_hdr *hdr;
+ uint16_t dest_id;
+ uint16_t client_id;
+ uint16_t w_len;
+ unsigned long flags;
+
+ if (!handle || !buf) {
+ pr_err("APR: Wrong parameters\n");
+ return -EINVAL;
+ }
+ if (svc->need_reset) {
+ pr_err("apr: send_pkt service need reset\n");
+ return -ENETRESET;
+ }
+
+ if ((svc->dest_id == APR_DEST_QDSP6) &&
+ (apr_get_q6_state() != APR_SUBSYS_LOADED)) {
+ pr_err("%s: Still dsp is not Up\n", __func__);
+ return -ENETRESET;
+ } else if ((svc->dest_id == APR_DEST_MODEM) &&
+ (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
+ pr_err("apr: Still Modem is not Up\n");
+ return -ENETRESET;
+ }
+
+ spin_lock_irqsave(&svc->w_lock, flags);
+ dest_id = svc->dest_id;
+ client_id = svc->client_id;
+ clnt = &client[dest_id][client_id];
+
+ if (!client[dest_id][client_id].handle) {
+ pr_err("APR: Still service is not yet opened\n");
+ spin_unlock_irqrestore(&svc->w_lock, flags);
+ return -EINVAL;
+ }
+ hdr = (struct apr_hdr *)buf;
+
+ hdr->src_domain = APR_DOMAIN_APPS;
+ hdr->src_svc = svc->id;
+ hdr->dest_domain = svc->dest_domain;
+ hdr->dest_svc = svc->id;
+
+ w_len = apr_tal_write(clnt->handle, buf, hdr->pkt_size);
+ if (w_len != hdr->pkt_size)
+ pr_err("Unable to write APR pkt successfully: %d\n", w_len);
+ spin_unlock_irqrestore(&svc->w_lock, flags);
+
+ return w_len;
+}
+
+struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
+ uint32_t src_port, void *priv)
+{
+ struct apr_client *clnt;
+ int client_id = 0;
+ int svc_idx = 0;
+ int svc_id = 0;
+ int dest_id = 0;
+ int domain_id = 0;
+ int temp_port = 0;
+ struct apr_svc *svc = NULL;
+ int rc = 0;
+
+ if (!dest || !svc_name || !svc_fn)
+ return NULL;
+
+ if (!strcmp(dest, "ADSP"))
+ domain_id = APR_DOMAIN_ADSP;
+ else if (!strcmp(dest, "MODEM"))
+ domain_id = APR_DOMAIN_MODEM;
+ else {
+ pr_err("APR: wrong destination\n");
+ goto done;
+ }
+
+ dest_id = apr_get_dest_id(dest);
+
+ if (dest_id == APR_DEST_QDSP6) {
+ if (apr_get_q6_state() != APR_SUBSYS_LOADED) {
+ pr_err("%s: adsp not up\n", __func__);
+ return NULL;
+ }
+ pr_debug("%s: adsp Up\n", __func__);
+ } else if (dest_id == APR_DEST_MODEM) {
+ if (apr_get_modem_state() == APR_SUBSYS_DOWN) {
+ pr_debug("%s: Wait for modem to bootup\n", __func__);
+ rc = apr_wait_for_device_up(APR_DEST_MODEM);
+ if (rc == 0) {
+ pr_err("%s: Modem is not Up\n", __func__);
+ return NULL;
+ }
+ }
+ pr_debug("%s: modem Up\n", __func__);
+ }
+
+ if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) {
+ pr_err("%s: apr_get_svc failed\n", __func__);
+ goto done;
+ }
+
+ clnt = &client[dest_id][client_id];
+ mutex_lock(&clnt->m_lock);
+ if (!clnt->handle) {
+ clnt->handle = apr_tal_open(client_id, dest_id,
+ APR_DL_SMD, apr_cb_func, NULL);
+ if (!clnt->handle) {
+ svc = NULL;
+ pr_err("APR: Unable to open handle\n");
+ mutex_unlock(&clnt->m_lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&clnt->m_lock);
+ svc = &clnt->svc[svc_idx];
+ mutex_lock(&svc->m_lock);
+ clnt->id = client_id;
+ if (svc->need_reset) {
+ mutex_unlock(&svc->m_lock);
+ pr_err("APR: Service needs reset\n");
+ goto done;
+ }
+ svc->priv = priv;
+ svc->id = svc_id;
+ svc->dest_id = dest_id;
+ svc->client_id = client_id;
+ svc->dest_domain = domain_id;
+ if (src_port != 0xFFFFFFFF) {
+ temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
+ pr_debug("port = %d t_port = %d\n", src_port, temp_port);
+ if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
+ pr_err("APR: temp_port out of bounds\n");
+ mutex_unlock(&svc->m_lock);
+ return NULL;
+ }
+ if (!svc->port_cnt && !svc->svc_cnt)
+ clnt->svc_cnt++;
+ svc->port_cnt++;
+ svc->port_fn[temp_port] = svc_fn;
+ svc->port_priv[temp_port] = priv;
+ } else {
+ if (!svc->fn) {
+ if (!svc->port_cnt && !svc->svc_cnt)
+ clnt->svc_cnt++;
+ svc->fn = svc_fn;
+ if (svc->port_cnt)
+ svc->svc_cnt++;
+ }
+ }
+
+ mutex_unlock(&svc->m_lock);
+done:
+ return svc;
+}
+
+
+void apr_cb_func(void *buf, int len, void *priv)
+{
+ struct apr_client_data data;
+ struct apr_client *apr_client;
+ struct apr_svc *c_svc;
+ struct apr_hdr *hdr;
+ uint16_t hdr_size;
+ uint16_t msg_type;
+ uint16_t ver;
+ uint16_t src;
+ uint16_t svc;
+ uint16_t clnt;
+ int i;
+ int temp_port = 0;
+ uint32_t *ptr;
+
+ pr_debug("APR2: len = %d\n", len);
+ ptr = buf;
+ pr_debug("\n*****************\n");
+ for (i = 0; i < len/4; i++)
+ pr_debug("%x ", ptr[i]);
+ pr_debug("\n");
+ pr_debug("\n*****************\n");
+
+ if (!buf || len <= APR_HDR_SIZE) {
+ pr_err("APR: Improper apr pkt received:%p %d\n", buf, len);
+ return;
+ }
+ hdr = buf;
+
+ ver = hdr->hdr_field;
+ ver = (ver & 0x000F);
+ if (ver > APR_PKT_VER + 1) {
+ pr_err("APR: Wrong version: %d\n", ver);
+ return;
+ }
+
+ hdr_size = hdr->hdr_field;
+ hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
+ if (hdr_size < APR_HDR_SIZE) {
+ pr_err("APR: Wrong hdr size:%d\n", hdr_size);
+ return;
+ }
+
+ if (hdr->pkt_size < APR_HDR_SIZE) {
+ pr_err("APR: Wrong paket size\n");
+ return;
+ }
+ msg_type = hdr->hdr_field;
+ msg_type = (msg_type >> 0x08) & 0x0003;
+ if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
+ pr_err("APR: Wrong message type: %d\n", msg_type);
+ return;
+ }
+
+ if (hdr->src_domain >= APR_DOMAIN_MAX ||
+ hdr->dest_domain >= APR_DOMAIN_MAX ||
+ hdr->src_svc >= APR_SVC_MAX ||
+ hdr->dest_svc >= APR_SVC_MAX) {
+ pr_err("APR: Wrong APR header\n");
+ return;
+ }
+
+ svc = hdr->dest_svc;
+ if (hdr->src_domain == APR_DOMAIN_MODEM) {
+ if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
+ svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
+ svc == APR_SVC_TEST_CLIENT)
+ clnt = APR_CLIENT_VOICE;
+ else {
+ pr_err("APR: Wrong svc :%d\n", svc);
+ return;
+ }
+ } else if (hdr->src_domain == APR_DOMAIN_ADSP) {
+ if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
+ svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
+ svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
+ svc == APR_SVC_USM ||
+ svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
+ svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
+ svc == APR_SVC_LSM)
+ clnt = APR_CLIENT_AUDIO;
+ else if (svc == APR_SVC_VIDC)
+ clnt = APR_CLIENT_AUDIO;
+ else {
+ pr_err("APR: Wrong svc :%d\n", svc);
+ return;
+ }
+ } else {
+ pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
+ return;
+ }
+
+ src = apr_get_data_src(hdr);
+ if (src == APR_DEST_MAX)
+ return;
+
+ pr_debug("src =%d clnt = %d\n", src, clnt);
+ apr_client = &client[src][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++)
+ if (apr_client->svc[i].id == svc) {
+ pr_debug("%d\n", apr_client->svc[i].id);
+ c_svc = &apr_client->svc[i];
+ break;
+ }
+
+ if (i == APR_SVC_MAX) {
+ pr_err("APR: service is not registered\n");
+ return;
+ }
+ pr_debug("svc_idx = %d\n", i);
+ pr_debug("%x %x %x %p %p\n", c_svc->id, c_svc->dest_id,
+ c_svc->client_id, c_svc->fn, c_svc->priv);
+ data.payload_size = hdr->pkt_size - hdr_size;
+ data.opcode = hdr->opcode;
+ data.src = src;
+ data.src_port = hdr->src_port;
+ data.dest_port = hdr->dest_port;
+ data.token = hdr->token;
+ data.msg_type = msg_type;
+ if (data.payload_size > 0)
+ data.payload = (char *)hdr + hdr_size;
+
+ temp_port = ((data.src_port >> 8) * 8) + (data.src_port & 0xFF);
+ pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
+ if (c_svc->port_cnt && c_svc->port_fn[temp_port])
+ c_svc->port_fn[temp_port](&data, c_svc->port_priv[temp_port]);
+ else if (c_svc->fn)
+ c_svc->fn(&data, c_svc->priv);
+ else
+ pr_err("APR: Rxed a packet for NULL callback\n");
+}
+
+int apr_get_svc(const char *svc_name, int domain_id, int *client_id,
+ int *svc_idx, int *svc_id)
+{
+ int i;
+ int size;
+ struct apr_svc_table *tbl;
+ int ret = 0;
+
+ if ((domain_id == APR_DOMAIN_ADSP)) {
+ tbl = (struct apr_svc_table *)&svc_tbl_qdsp6;
+ size = ARRAY_SIZE(svc_tbl_qdsp6);
+ } else {
+ tbl = (struct apr_svc_table *)&svc_tbl_voice;
+ size = ARRAY_SIZE(svc_tbl_voice);
+ }
+
+ for (i = 0; i < size; i++) {
+ if (!strcmp(svc_name, tbl[i].name)) {
+ *client_id = tbl[i].client_id;
+ *svc_idx = tbl[i].idx;
+ *svc_id = tbl[i].id;
+ break;
+ }
+ }
+
+ pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n",
+ __func__, svc_name, *client_id, domain_id);
+ if (i == size) {
+ pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static void apr_reset_deregister(struct work_struct *work)
+{
+ struct apr_svc *handle = NULL;
+ struct apr_reset_work *apr_reset =
+ container_of(work, struct apr_reset_work, work);
+
+ handle = apr_reset->handle;
+ pr_debug("%s:handle[%p]\n", __func__, handle);
+ apr_deregister(handle);
+ kfree(apr_reset);
+}
+
+int apr_deregister(void *handle)
+{
+ struct apr_svc *svc = handle;
+ struct apr_client *clnt;
+ uint16_t dest_id;
+ uint16_t client_id;
+
+ if (!handle)
+ return -EINVAL;
+
+ mutex_lock(&svc->m_lock);
+ dest_id = svc->dest_id;
+ client_id = svc->client_id;
+ clnt = &client[dest_id][client_id];
+
+ if (svc->port_cnt > 0 || svc->svc_cnt > 0) {
+ if (svc->port_cnt)
+ svc->port_cnt--;
+ else if (svc->svc_cnt)
+ svc->svc_cnt--;
+ if (!svc->port_cnt && !svc->svc_cnt) {
+ client[dest_id][client_id].svc_cnt--;
+ svc->need_reset = 0x0;
+ }
+ } else if (client[dest_id][client_id].svc_cnt > 0) {
+ client[dest_id][client_id].svc_cnt--;
+ if (!client[dest_id][client_id].svc_cnt) {
+ svc->need_reset = 0x0;
+ pr_debug("%s: service is reset %p\n", __func__, svc);
+ }
+ }
+
+ if (!svc->port_cnt && !svc->svc_cnt) {
+ svc->priv = NULL;
+ svc->id = 0;
+ svc->fn = NULL;
+ svc->dest_id = 0;
+ svc->client_id = 0;
+ svc->need_reset = 0x0;
+ }
+ if (client[dest_id][client_id].handle &&
+ !client[dest_id][client_id].svc_cnt) {
+ apr_tal_close(client[dest_id][client_id].handle);
+ client[dest_id][client_id].handle = NULL;
+ }
+ mutex_unlock(&svc->m_lock);
+
+ return 0;
+}
+
+void apr_reset(void *handle)
+{
+ struct apr_reset_work *apr_reset_worker = NULL;
+
+ if (!handle)
+ return;
+ pr_debug("%s: handle[%p]\n", __func__, handle);
+
+ if (apr_reset_workqueue == NULL) {
+ pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
+ return;
+ }
+
+ apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
+ GFP_ATOMIC);
+
+ if (apr_reset_worker == NULL) {
+ pr_err("%s: mem failure\n", __func__);
+ return;
+ }
+
+ apr_reset_worker->handle = handle;
+ INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
+ queue_work(apr_reset_workqueue, &apr_reset_worker->work);
+}
+
+/* Dispatch the Reset events to Modem and audio clients */
+void dispatch_event(unsigned long code, unsigned short proc)
+{
+ struct apr_client *apr_client;
+ struct apr_client_data data;
+ struct apr_svc *svc;
+ uint16_t clnt;
+ int i, j;
+
+ data.opcode = RESET_EVENTS;
+ data.reset_event = code;
+ data.reset_proc = proc;
+
+ clnt = APR_CLIENT_AUDIO;
+ apr_client = &client[proc][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++) {
+ mutex_lock(&apr_client->svc[i].m_lock);
+ if (apr_client->svc[i].fn) {
+ apr_client->svc[i].need_reset = 0x1;
+ apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+ }
+ if (apr_client->svc[i].port_cnt) {
+ svc = &(apr_client->svc[i]);
+ svc->need_reset = 0x1;
+ for (j = 0; j < APR_MAX_PORTS; j++)
+ if (svc->port_fn[j])
+ svc->port_fn[j](&data,
+ svc->port_priv[j]);
+ }
+ mutex_unlock(&apr_client->svc[i].m_lock);
+ }
+
+ clnt = APR_CLIENT_VOICE;
+ apr_client = &client[proc][clnt];
+ for (i = 0; i < APR_SVC_MAX; i++) {
+ mutex_lock(&apr_client->svc[i].m_lock);
+ if (apr_client->svc[i].fn) {
+ apr_client->svc[i].need_reset = 0x1;
+ apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+ }
+ if (apr_client->svc[i].port_cnt) {
+ svc = &(apr_client->svc[i]);
+ svc->need_reset = 0x1;
+ for (j = 0; j < APR_MAX_PORTS; j++)
+ if (svc->port_fn[j])
+ svc->port_fn[j](&data,
+ svc->port_priv[j]);
+ }
+ mutex_unlock(&apr_client->svc[i].m_lock);
+ }
+}
+
+static int modem_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *_cmd)
+{
+ static int boot_count = 2;
+
+ if (boot_count) {
+ boot_count--;
+ return NOTIFY_OK;
+ }
+
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("M-Notify: Shutdown started\n");
+ apr_set_modem_state(APR_SUBSYS_DOWN);
+ dispatch_event(code, APR_DEST_MODEM);
+ break;
+ case SUBSYS_AFTER_SHUTDOWN:
+ pr_debug("M-Notify: Shutdown Completed\n");
+ break;
+ case SUBSYS_BEFORE_POWERUP:
+ pr_debug("M-notify: Bootup started\n");
+ break;
+ case SUBSYS_AFTER_POWERUP:
+ if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
+ APR_SUBSYS_DOWN)
+ wake_up(&modem_wait);
+ pr_debug("M-Notify: Bootup Completed\n");
+ break;
+ default:
+ pr_err("M-Notify: General: %lu\n", code);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block mnb = {
+ .notifier_call = modem_notifier_cb,
+};
+
+static int lpass_notifier_cb(struct notifier_block *this, unsigned long code,
+ void *_cmd)
+{
+ static int boot_count = 2;
+
+ if (boot_count) {
+ boot_count--;
+ return NOTIFY_OK;
+ }
+
+ switch (code) {
+ case SUBSYS_BEFORE_SHUTDOWN:
+ pr_debug("L-Notify: Shutdown started\n");
+ apr_set_q6_state(APR_SUBSYS_DOWN);
+ dispatch_event(code, APR_DEST_QDSP6);
+ break;
+ case SUBSYS_AFTER_SHUTDOWN:
+ pr_debug("L-Notify: Shutdown Completed\n");
+ break;
+ case SUBSYS_BEFORE_POWERUP:
+ pr_debug("L-notify: Bootup started\n");
+ break;
+ case SUBSYS_AFTER_POWERUP:
+ if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN,
+ APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN)
+ wake_up(&dsp_wait);
+ pr_debug("L-Notify: Bootup Completed\n");
+ break;
+ default:
+ pr_err("L-Notify: Generel: %lu\n", code);
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block lnb = {
+ .notifier_call = lpass_notifier_cb,
+};
+
+
+static int __init apr_init(void)
+{
+ int i, j, k;
+
+ for (i = 0; i < APR_DEST_MAX; i++)
+ for (j = 0; j < APR_CLIENT_MAX; j++) {
+ mutex_init(&client[i][j].m_lock);
+ for (k = 0; k < APR_SVC_MAX; k++) {
+ mutex_init(&client[i][j].svc[k].m_lock);
+ spin_lock_init(&client[i][j].svc[k].w_lock);
+ }
+ }
+ apr_set_subsys_state();
+ mutex_init(&q6.lock);
+ apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
+ if (!apr_reset_workqueue)
+ return -ENOMEM;
+ return 0;
+}
+device_initcall(apr_init);
+
+static int __init apr_late_init(void)
+{
+ int ret = 0;
+ init_waitqueue_head(&dsp_wait);
+ init_waitqueue_head(&modem_wait);
+ subsys_notif_register(&mnb, &lnb);
+ return ret;
+}
+late_initcall(apr_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal.c b/drivers/soc/qcom/qdsp6v2/apr_tal.c
new file mode 100644
index 000000000000..684bc5c76161
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2010-2011, 2013 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <mach/msm_smd.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = {
+ {
+ "apr_audio_svc",
+ "apr_voice_svc",
+ },
+ {
+ "apr_audio_svc",
+ "apr_voice_svc",
+ },
+};
+
+struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
+
+int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len)
+{
+ int w_len;
+ unsigned long flags;
+
+
+ spin_lock_irqsave(&apr_ch->w_lock, flags);
+ if (smd_write_avail(apr_ch->ch) < len) {
+ spin_unlock_irqrestore(&apr_ch->w_lock, flags);
+ return -EAGAIN;
+ }
+
+ w_len = smd_write(apr_ch->ch, data, len);
+ spin_unlock_irqrestore(&apr_ch->w_lock, flags);
+ pr_debug("apr_tal:w_len = %d\n", w_len);
+
+ if (w_len != len) {
+ pr_err("apr_tal: Error in write\n");
+ return -ENETRESET;
+ }
+ return w_len;
+}
+
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, int len)
+{
+ int rc = 0, retries = 0;
+
+ if (!apr_ch->ch)
+ return -EINVAL;
+
+ do {
+ if (rc == -EAGAIN)
+ udelay(50);
+
+ rc = __apr_tal_write(apr_ch, data, len);
+ } while (rc == -EAGAIN && retries++ < 300);
+
+ if (rc == -EAGAIN)
+ pr_err("apr_tal: TIMEOUT for write\n");
+
+ return rc;
+}
+
+static void apr_tal_notify(void *priv, unsigned event)
+{
+ struct apr_svc_ch_dev *apr_ch = priv;
+ int len, r_len, sz;
+ int pkt_cnt = 0;
+ unsigned long flags;
+
+ pr_debug("event = %d\n", event);
+ switch (event) {
+ case SMD_EVENT_DATA:
+ pkt_cnt = 0;
+ spin_lock_irqsave(&apr_ch->lock, flags);
+check_pending:
+ len = smd_read_avail(apr_ch->ch);
+ if (len < 0) {
+ pr_err("apr_tal: Invalid Read Event :%d\n", len);
+ spin_unlock_irqrestore(&apr_ch->lock, flags);
+ return;
+ }
+ sz = smd_cur_packet_size(apr_ch->ch);
+ if (sz < 0) {
+ pr_debug("pkt size is zero\n");
+ spin_unlock_irqrestore(&apr_ch->lock, flags);
+ return;
+ }
+ if (!len && !sz && !pkt_cnt)
+ goto check_write_avail;
+ if (!len) {
+ pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt);
+ spin_unlock_irqrestore(&apr_ch->lock, flags);
+ return;
+ }
+ r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len);
+ if (len != r_len) {
+ pr_err("apr_tal: Invalid Read\n");
+ spin_unlock_irqrestore(&apr_ch->lock, flags);
+ return;
+ }
+ pkt_cnt++;
+ pr_debug("%d %d %d\n", len, sz, pkt_cnt);
+ if (apr_ch->func)
+ apr_ch->func(apr_ch->data, r_len, apr_ch->priv);
+ goto check_pending;
+check_write_avail:
+ if (smd_write_avail(apr_ch->ch))
+ wake_up(&apr_ch->wait);
+ spin_unlock_irqrestore(&apr_ch->lock, flags);
+ break;
+ case SMD_EVENT_OPEN:
+ pr_debug("apr_tal: SMD_EVENT_OPEN\n");
+ apr_ch->smd_state = 1;
+ wake_up(&apr_ch->wait);
+ break;
+ case SMD_EVENT_CLOSE:
+ pr_debug("apr_tal: SMD_EVENT_CLOSE\n");
+ break;
+ }
+}
+
+struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
+ uint32_t dl, apr_svc_cb_fn func, void *priv)
+{
+ int rc;
+
+ if ((svc >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
+ (dl >= APR_DL_MAX)) {
+ pr_err("apr_tal: Invalid params\n");
+ return NULL;
+ }
+
+ if (apr_svc_ch[dl][dest][svc].ch) {
+ pr_err("apr_tal: This channel alreday openend\n");
+ return NULL;
+ }
+
+ mutex_lock(&apr_svc_ch[dl][dest][svc].m_lock);
+ if (!apr_svc_ch[dl][dest][svc].dest_state) {
+ rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].dest,
+ apr_svc_ch[dl][dest][svc].dest_state,
+ msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
+ if (rc == 0) {
+ pr_err("apr_tal:open timeout\n");
+ mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock);
+ return NULL;
+ }
+ pr_debug("apr_tal:Wakeup done\n");
+ apr_svc_ch[dl][dest][svc].dest_state = 0;
+ }
+ rc = smd_named_open_on_edge(svc_names[dest][svc], dest,
+ &apr_svc_ch[dl][dest][svc].ch,
+ &apr_svc_ch[dl][dest][svc],
+ apr_tal_notify);
+ if (rc < 0) {
+ pr_err("apr_tal: smd_open failed %s\n",
+ svc_names[dest][svc]);
+ mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock);
+ return NULL;
+ }
+ rc = wait_event_timeout(apr_svc_ch[dl][dest][svc].wait,
+ (apr_svc_ch[dl][dest][svc].smd_state == 1), 5 * HZ);
+ if (rc == 0) {
+ pr_err("apr_tal:TIMEOUT for OPEN event\n");
+ mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock);
+ apr_tal_close(&apr_svc_ch[dl][dest][svc]);
+ return NULL;
+ }
+ if (!apr_svc_ch[dl][dest][svc].dest_state) {
+ apr_svc_ch[dl][dest][svc].dest_state = 1;
+ pr_debug("apr_tal:Waiting for apr svc init\n");
+ msleep(200);
+ pr_debug("apr_tal:apr svc init done\n");
+ }
+ apr_svc_ch[dl][dest][svc].smd_state = 0;
+
+ apr_svc_ch[dl][dest][svc].func = func;
+ apr_svc_ch[dl][dest][svc].priv = priv;
+ mutex_unlock(&apr_svc_ch[dl][dest][svc].m_lock);
+
+ return &apr_svc_ch[dl][dest][svc];
+}
+
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
+{
+ int r;
+
+ if (!apr_ch->ch)
+ return -EINVAL;
+
+ mutex_lock(&apr_ch->m_lock);
+ r = smd_close(apr_ch->ch);
+ apr_ch->ch = NULL;
+ apr_ch->func = NULL;
+ apr_ch->priv = NULL;
+ mutex_unlock(&apr_ch->m_lock);
+ return r;
+}
+
+static int apr_smd_probe(struct platform_device *pdev)
+{
+ int dest;
+ int clnt;
+
+ if (pdev->id == APR_DEST_MODEM) {
+ pr_info("apr_tal:Modem Is Up\n");
+ dest = APR_DEST_MODEM;
+ if (!strcmp(pdev->name, "apr_audio_svc"))
+ clnt = APR_CLIENT_AUDIO;
+ else
+ clnt = APR_CLIENT_VOICE;
+ apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
+ wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
+ } else if (pdev->id == APR_DEST_QDSP6) {
+ pr_info("apr_tal:Q6 Is Up\n");
+ dest = APR_DEST_QDSP6;
+ clnt = APR_CLIENT_AUDIO;
+ apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
+ wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
+ } else
+ pr_err("apr_tal:Invalid Dest Id: %d\n", pdev->id);
+
+ return 0;
+}
+
+static struct platform_driver apr_q6_driver = {
+ .probe = apr_smd_probe,
+ .driver = {
+ .name = "apr_audio_svc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct platform_driver apr_modem_driver = {
+ .probe = apr_smd_probe,
+ .driver = {
+ .name = "apr_voice_svc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init apr_tal_init(void)
+{
+ int i, j, k;
+
+ for (i = 0; i < APR_DL_MAX; i++)
+ for (j = 0; j < APR_DEST_MAX; j++)
+ for (k = 0; k < APR_CLIENT_MAX; k++) {
+ init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
+ init_waitqueue_head(&apr_svc_ch[i][j][k].dest);
+ spin_lock_init(&apr_svc_ch[i][j][k].lock);
+ spin_lock_init(&apr_svc_ch[i][j][k].w_lock);
+ mutex_init(&apr_svc_ch[i][j][k].m_lock);
+ }
+ platform_driver_register(&apr_q6_driver);
+ platform_driver_register(&apr_modem_driver);
+ return 0;
+}
+device_initcall(apr_tal_init);
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c
new file mode 100644
index 000000000000..4770a8a31ba8
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012, 2013 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+
+static const char *lpass_subsys_name = "adsp";
+
+void apr_set_subsys_state(void)
+{
+ apr_set_q6_state(APR_SUBSYS_DOWN);
+ apr_set_modem_state(APR_SUBSYS_UP);
+}
+
+const char *apr_get_lpass_subsys_name(void)
+{
+ return lpass_subsys_name;
+}
+
+uint16_t apr_get_data_src(struct apr_hdr *hdr)
+{
+ if (hdr->src_domain == APR_DOMAIN_MODEM)
+ return APR_DEST_MODEM;
+ else if (hdr->src_domain == APR_DOMAIN_ADSP)
+ return APR_DEST_QDSP6;
+ else {
+ pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
+ return APR_DEST_MAX; /*RETURN INVALID VALUE*/
+ }
+}
+
+int apr_get_dest_id(char *dest)
+{
+ if (!strcmp(dest, "ADSP"))
+ return APR_DEST_QDSP6;
+ else
+ return APR_DEST_MODEM;
+}
+
+void subsys_notif_register(struct notifier_block *mod_notif,
+ struct notifier_block *lp_notif)
+{
+ subsys_notif_register_notifier("modem", mod_notif);
+ subsys_notif_register_notifier(apr_get_lpass_subsys_name(), lp_notif);
+}
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c
new file mode 100644
index 000000000000..bbef6a5af809
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <mach/qdsp6v2/apr.h>
+#include <mach/qdsp6v2/apr_tal.h>
+#include <mach/qdsp6v2/dsp_debug.h>
+
+#define DEST_ID APR_DEST_MODEM
+
+void apr_set_subsys_state(void)
+{
+ apr_set_modem_state(APR_SUBSYS_UP);
+}
+
+uint16_t apr_get_data_src(struct apr_hdr *hdr)
+{
+ return DEST_ID;
+}
+
+int apr_get_dest_id(char *dest)
+{
+ return DEST_ID;
+}
+
+void subsys_notif_register(struct notifier_block *mod_notif,
+ struct notifier_block *lp_notif)
+{
+ subsys_notif_register_notifier("modem", mod_notif);
+}
+
diff --git a/drivers/soc/qcom/qdsp6v2/dsp_debug.c b/drivers/soc/qcom/qdsp6v2/dsp_debug.c
new file mode 100644
index 000000000000..be47f587da40
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/dsp_debug.c
@@ -0,0 +1,261 @@
+/* arch/arm/mach-msm/qdsp6/dsp_dump.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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/io.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <asm/atomic.h>
+
+#include <mach/proc_comm.h>
+#include <mach/debug_mm.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+
+static wait_queue_head_t dsp_wait;
+static int dsp_has_crashed;
+static int dsp_wait_count;
+
+static atomic_t dsp_crash_count = ATOMIC_INIT(0);
+dsp_state_cb cb_ptr;
+
+void q6audio_dsp_not_responding(void)
+{
+ int i;
+
+ if (cb_ptr)
+ cb_ptr(DSP_STATE_CRASHED);
+ if (atomic_add_return(1, &dsp_crash_count) != 1) {
+ pr_err("q6audio_dsp_not_responding() \
+ - parking additional crasher...\n");
+ for (i = 0; i < 600; i++)
+ msleep(1000);
+ }
+ if (dsp_wait_count) {
+ dsp_has_crashed = 1;
+ wake_up(&dsp_wait);
+
+ while (dsp_has_crashed != 2)
+ wait_event(dsp_wait, dsp_has_crashed == 2);
+ } else {
+ pr_err("q6audio_dsp_not_responding() - no waiter?\n");
+ }
+ if (cb_ptr)
+ cb_ptr(DSP_STATE_CRASH_DUMP_DONE);
+}
+
+static int dsp_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+#define DSP_NMI_ADDR 0x28800010
+
+static ssize_t dsp_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ char cmd[32];
+ void __iomem *ptr;
+ void *mem_buffer;
+
+ if (count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, buf, count))
+ return -EFAULT;
+ cmd[count] = 0;
+
+ if ((count > 1) && (cmd[count-1] == '\n'))
+ cmd[count-1] = 0;
+
+ if (!strcmp(cmd, "wait-for-crash")) {
+ while (!dsp_has_crashed) {
+ int res;
+ dsp_wait_count++;
+ res = wait_event_interruptible(dsp_wait,
+ dsp_has_crashed);
+ if (res < 0) {
+ dsp_wait_count--;
+ return res;
+ }
+ }
+ /* assert DSP NMI */
+ mem_buffer = ioremap(DSP_NMI_ADDR, 0x16);
+ if (IS_ERR((void *)mem_buffer)) {
+ pr_err("%s:map_buffer failed, error = %ld\n", __func__,
+ PTR_ERR((void *)mem_buffer));
+ return -ENOMEM;
+ }
+ ptr = mem_buffer;
+ if (!ptr) {
+ pr_err("Unable to map DSP NMI\n");
+ return -EFAULT;
+ }
+ writel(0x1, (void *)ptr);
+ iounmap(mem_buffer);
+ } else if (!strcmp(cmd, "boom")) {
+ q6audio_dsp_not_responding();
+ } else if (!strcmp(cmd, "continue-crash")) {
+ dsp_has_crashed = 2;
+ wake_up(&dsp_wait);
+ } else {
+ pr_err("[%s:%s] unknown dsp_debug command: %s\n", __MM_FILE__,
+ __func__, cmd);
+ }
+
+ return count;
+}
+
+static unsigned copy_ok_count;
+static uint32_t dsp_ram_size;
+static uint32_t dsp_ram_base;
+
+static ssize_t dsp_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ size_t actual = 0;
+ size_t mapsize = PAGE_SIZE;
+ unsigned addr;
+ void __iomem *ptr;
+ void *mem_buffer;
+
+ if ((dsp_ram_base == 0) || (dsp_ram_size == 0)) {
+ pr_err("[%s:%s] Memory Invalid or not initialized, Base = 0x%x,"
+ " size = 0x%x\n", __MM_FILE__,
+ __func__, dsp_ram_base, dsp_ram_size);
+ return -EINVAL;
+ }
+
+ if (*pos >= dsp_ram_size)
+ return 0;
+
+ if (*pos & (PAGE_SIZE - 1))
+ return -EINVAL;
+
+ addr = (*pos + dsp_ram_base);
+
+ /* don't blow up if we're unaligned */
+ if (addr & (PAGE_SIZE - 1))
+ mapsize *= 2;
+
+ while (count >= PAGE_SIZE) {
+ mem_buffer = ioremap(addr, mapsize);
+ if (IS_ERR((void *)mem_buffer)) {
+ pr_err("%s:map_buffer failed, error = %ld\n",
+ __func__, PTR_ERR((void *)mem_buffer));
+ return -ENOMEM;
+ }
+ ptr = mem_buffer;
+ if (!ptr) {
+ pr_err("[%s:%s] map error @ %x\n", __MM_FILE__,
+ __func__, addr);
+ return -EFAULT;
+ }
+ if (copy_to_user(buf, ptr, PAGE_SIZE)) {
+ iounmap(mem_buffer);
+ pr_err("[%s:%s] copy error @ %p\n", __MM_FILE__,
+ __func__, buf);
+ return -EFAULT;
+ }
+ copy_ok_count += PAGE_SIZE;
+ iounmap(mem_buffer);
+ addr += PAGE_SIZE;
+ buf += PAGE_SIZE;
+ actual += PAGE_SIZE;
+ count -= PAGE_SIZE;
+ }
+
+ *pos += actual;
+ return actual;
+}
+
+static int dsp_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+int dsp_debug_register(dsp_state_cb ptr)
+{
+ if (ptr == NULL)
+ return -EINVAL;
+ cb_ptr = ptr;
+
+ return 0;
+}
+
+static int dspcrashd_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct resource *res;
+ int *pdata;
+
+ pdata = pdev->dev.platform_data;
+ res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
+ "msm_dspcrashd");
+ if (!res) {
+ pr_err("%s: failed to get resources for dspcrashd\n", __func__);
+ return -ENODEV;
+ }
+
+ dsp_ram_base = res->start;
+ dsp_ram_size = res->end - res->start;
+ pr_info("%s: Platform driver values: Base = 0x%x, Size = 0x%x,"
+ "pdata = 0x%x\n", __func__,
+ dsp_ram_base, dsp_ram_size, *pdata);
+ return rc;
+}
+
+static const struct file_operations dsp_fops = {
+ .owner = THIS_MODULE,
+ .open = dsp_open,
+ .read = dsp_read,
+ .write = dsp_write,
+ .release = dsp_release,
+};
+
+static struct miscdevice dsp_misc = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "dsp_debug",
+ .fops = &dsp_fops,
+};
+
+static struct platform_driver dspcrashd_driver = {
+ .probe = dspcrashd_probe,
+ .driver = { .name = "msm_dspcrashd"}
+};
+
+static int __init dsp_init(void)
+{
+ int rc = 0;
+ init_waitqueue_head(&dsp_wait);
+ rc = platform_driver_register(&dspcrashd_driver);
+ if (IS_ERR_VALUE(rc)) {
+ pr_err("%s: platform_driver_register for dspcrashd failed\n",
+ __func__);
+ }
+ return misc_register(&dsp_misc);
+}
+
+static int __exit dsp_exit(void)
+{
+ platform_driver_unregister(&dspcrashd_driver);
+ return 0;
+}
+
+device_initcall(dsp_init);
diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
new file mode 100644
index 000000000000..c63cc62c0e24
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <mach/subsystem_restart.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/of_device.h>
+#include <linux/msm_audio_ion.h>
+
+#include <linux/iommu.h>
+#include <linux/msm_iommu_domains.h>
+
+struct msm_audio_ion_private {
+ bool smmu_enabled;
+ bool audioheap_enabled;
+ struct iommu_group *group;
+ u32 domain_id;
+ struct iommu_domain *domain;
+};
+
+static struct msm_audio_ion_private msm_audio_ion_data = {0,};
+
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+ struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len);
+
+
+
+int msm_audio_ion_alloc(const char *name, struct ion_client **client,
+ struct ion_handle **handle, size_t bufsz,
+ ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+ int rc = 0;
+
+ if ((msm_audio_ion_data.smmu_enabled == true) &&
+ (msm_audio_ion_data.group == NULL)) {
+ pr_debug("%s:probe is not done, deferred\n", __func__);
+ return -EPROBE_DEFER;
+ }
+ if (!name || !client || !handle || !paddr || !vaddr
+ || !bufsz || !pa_len) {
+ pr_err("%s: Invalid params\n", __func__);
+ return -EINVAL;
+ }
+ *client = msm_audio_ion_client_create(UINT_MAX, name);
+ if (IS_ERR_OR_NULL((void *)(*client))) {
+ pr_err("%s: ION create client for AUDIO failed\n", __func__);
+ goto err;
+ }
+
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_debug("system heap is used");
+ msm_audio_ion_data.audioheap_enabled = 0;
+ *handle = ion_alloc(*client, bufsz, SZ_4K,
+ ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+
+ } else {
+ pr_debug("audio heap is used");
+ msm_audio_ion_data.audioheap_enabled = 1;
+ }
+
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_err("%s: ION memory allocation for AUDIO failed rc=%d, smmu_enabled=%d\n",
+ __func__, rc, msm_audio_ion_data.smmu_enabled);
+ goto err_ion_client;
+ }
+
+ rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+ if (rc) {
+ pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+ __func__, rc);
+ goto err_ion_handle;
+ }
+
+ *vaddr = ion_map_kernel(*client, *handle);
+ if (IS_ERR_OR_NULL((void *)*vaddr)) {
+ pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+ goto err_ion_handle;
+ }
+ pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz);
+
+ if (bufsz != 0) {
+ pr_debug("%s: memset to 0 %p %d\n", __func__, *vaddr, bufsz);
+ memset((void *)*vaddr, 0, bufsz);
+ }
+
+ return 0;
+
+err_ion_handle:
+ ion_free(*client, *handle);
+err_ion_client:
+ msm_audio_ion_client_destroy(*client);
+ *handle = NULL;
+ *client = NULL;
+err:
+ return -EINVAL;
+}
+
+int msm_audio_ion_import(const char *name, struct ion_client **client,
+ struct ion_handle **handle, int fd,
+ unsigned long *ionflag, size_t bufsz,
+ ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+ int rc = 0;
+ if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+ pr_err("%s: Invalid params\n", __func__);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ if ((msm_audio_ion_data.smmu_enabled == true) &&
+ (msm_audio_ion_data.group == NULL)) {
+ pr_debug("%s:probe is not done, deferred\n", __func__);
+ return -EPROBE_DEFER;
+ }
+
+ *client = msm_audio_ion_client_create(UINT_MAX, name);
+ if (IS_ERR_OR_NULL((void *)(*client))) {
+ pr_err("%s: ION create client for AUDIO failed\n", __func__);
+ rc = -EINVAL;
+ goto err;
+ }
+
+ /* name should be audio_acdb_client or Audio_Dec_Client,
+ bufsz should be 0 and fd shouldn't be 0 as of now
+ */
+ *handle = ion_import_dma_buf(*client, fd);
+ pr_err("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
+ name, fd, *handle);
+ if (IS_ERR_OR_NULL((void *) (*handle))) {
+ pr_err("%s: ion import dma buffer failed\n",
+ __func__);
+ rc = -EINVAL;
+ goto err_destroy_client;
+ }
+
+ if (ionflag != NULL) {
+ rc = ion_handle_get_flags(*client, *handle, ionflag);
+ if (rc) {
+ pr_err("%s: could not get flags for the handle\n",
+ __func__);
+ goto err_ion_handle;
+ }
+ }
+
+ rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+ if (rc) {
+ pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+ __func__, rc);
+ goto err_ion_handle;
+ }
+
+ *vaddr = ion_map_kernel(*client, *handle);
+ if (IS_ERR_OR_NULL((void *)*vaddr)) {
+ pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+ rc = -ENOMEM;
+ goto err_ion_handle;
+ }
+ pr_debug("%s: mapped address = %p, size=%d\n", __func__, *vaddr, bufsz);
+
+ return 0;
+
+err_ion_handle:
+ ion_free(*client, *handle);
+err_destroy_client:
+ msm_audio_ion_client_destroy(*client);
+ *client = NULL;
+ *handle = NULL;
+err:
+ return rc;
+}
+
+int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
+{
+ if (!client || !handle) {
+ pr_err("%s Invalid params\n", __func__);
+ return -EINVAL;
+ }
+ if (msm_audio_ion_data.smmu_enabled) {
+ /* Need to populate book kept infomation */
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ ion_unmap_iommu(client, handle,
+ msm_audio_ion_data.domain_id, 0);
+ }
+
+ ion_unmap_kernel(client, handle);
+
+ ion_free(client, handle);
+ msm_audio_ion_client_destroy(client);
+ return 0;
+}
+
+int msm_audio_ion_mmap(struct audio_buffer *ab,
+ struct vm_area_struct *vma)
+{
+ struct sg_table *table;
+ unsigned long addr = vma->vm_start;
+ unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+ struct scatterlist *sg;
+ unsigned int i;
+ struct page *page;
+ int ret;
+
+ pr_debug("%s\n", __func__);
+
+ table = ion_sg_table(ab->client, ab->handle);
+
+ if (IS_ERR(table)) {
+ pr_err("%s: Unable to get sg_table from ion: %ld\n",
+ __func__, PTR_ERR(table));
+ return PTR_ERR(table);
+ } else if (!table) {
+ pr_err("%s: sg_list is NULL\n", __func__);
+ return -EINVAL;
+ }
+
+ /* uncached */
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ /* We need to check if a page is associated with this sg list because:
+ * If the allocation came from a carveout we currently don't have
+ * pages associated with carved out memory. This might change in the
+ * future and we can remove this check and the else statement.
+ */
+ page = sg_page(table->sgl);
+ if (page) {
+ pr_debug("%s: page is NOT null\n", __func__);
+ for_each_sg(table->sgl, sg, table->nents, i) {
+ unsigned long remainder = vma->vm_end - addr;
+ unsigned long len = sg_dma_len(sg);
+
+ page = sg_page(sg);
+
+ if (offset >= sg_dma_len(sg)) {
+ offset -= sg_dma_len(sg);
+ continue;
+ } else if (offset) {
+ page += offset / PAGE_SIZE;
+ len = sg_dma_len(sg) - offset;
+ offset = 0;
+ }
+ len = min(len, remainder);
+ pr_debug("vma=%p, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%ld\n",
+ vma, (unsigned int)addr, len,
+ (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end,
+ (unsigned long int)vma->vm_page_prot);
+ remap_pfn_range(vma, addr, page_to_pfn(page), len,
+ vma->vm_page_prot);
+ addr += len;
+ if (addr >= vma->vm_end)
+ return 0;
+ }
+ } else {
+ ion_phys_addr_t phys_addr;
+ size_t phys_len;
+ size_t va_len = 0;
+ pr_debug("%s: page is NULL\n", __func__);
+
+ ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
+ if (ret) {
+ pr_err("%s: Unable to get phys address from ION buffer: %d\n"
+ , __func__ , ret);
+ return ret;
+ }
+ pr_debug("phys=%x len=%d\n", (unsigned int)phys_addr, phys_len);
+ pr_debug("vma=%p, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%ld\n",
+ vma, (unsigned int)vma->vm_start,
+ (unsigned int)vma->vm_end, vma->vm_pgoff,
+ (unsigned long int)vma->vm_page_prot);
+ va_len = vma->vm_end - vma->vm_start;
+ if ((offset > phys_len) || (va_len > phys_len-offset)) {
+ pr_err("wrong offset size %ld, lens= %d, va_len=%d\n",
+ offset, phys_len, va_len);
+ return -EINVAL;
+ }
+ ret = remap_pfn_range(vma, vma->vm_start,
+ __phys_to_pfn(phys_addr) + vma->vm_pgoff,
+ vma->vm_end - vma->vm_start,
+ vma->vm_page_prot);
+ }
+ return 0;
+}
+
+
+bool msm_audio_ion_is_smmu_available(void)
+{
+ return msm_audio_ion_data.smmu_enabled;
+}
+
+/* move to static section again */
+struct ion_client *msm_audio_ion_client_create(unsigned int heap_mask,
+ const char *name)
+{
+ struct ion_client *pclient = NULL;
+ /*IOMMU group and domain are moved to probe()*/
+ pclient = msm_ion_client_create(heap_mask, name);
+ return pclient;
+}
+
+
+void msm_audio_ion_client_destroy(struct ion_client *client)
+{
+ pr_debug("%s: client = %p smmu_enabled = %d\n", __func__,
+ client, msm_audio_ion_data.smmu_enabled);
+
+ ion_client_destroy(client);
+}
+
+int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
+ struct ion_handle **handle, int fd,
+ unsigned long *ionflag, size_t bufsz,
+ ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+ int rc = 0;
+ if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+ pr_err("%s: Invalid params\n", __func__);
+ rc = -EINVAL;
+ goto err;
+ }
+ /* client is already created for legacy and given*/
+ /* name should be audio_acdb_client or Audio_Dec_Client,
+ bufsz should be 0 and fd shouldn't be 0 as of now
+ */
+ *handle = ion_import_dma_buf(client, fd);
+ pr_debug("%s: DMA Buf name=%s, fd=%d handle=%p\n", __func__,
+ name, fd, *handle);
+ if (IS_ERR_OR_NULL((void *)(*handle))) {
+ pr_err("%s: ion import dma buffer failed\n",
+ __func__);
+ rc = -EINVAL;
+ goto err_destroy_client;
+ }
+
+ if (ionflag != NULL) {
+ rc = ion_handle_get_flags(client, *handle, ionflag);
+ if (rc) {
+ pr_err("%s: could not get flags for the handle\n",
+ __func__);
+ rc = -EINVAL;
+ goto err_ion_handle;
+ }
+ }
+
+ rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len);
+ if (rc) {
+ pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+ __func__, rc);
+ rc = -EINVAL;
+ goto err_ion_handle;
+ }
+
+ /*Need to add condition SMMU enable or not */
+ *vaddr = ion_map_kernel(client, *handle);
+ if (IS_ERR_OR_NULL((void *)*vaddr)) {
+ pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+ rc = -EINVAL;
+ goto err_ion_handle;
+ }
+
+ if (bufsz != 0)
+ memset((void *)*vaddr, 0, bufsz);
+
+ return 0;
+
+err_ion_handle:
+ ion_free(client, *handle);
+err_destroy_client:
+ msm_audio_ion_client_destroy(client);
+ client = NULL;
+ *handle = NULL;
+err:
+ return rc;
+}
+
+int msm_audio_ion_free_legacy(struct ion_client *client,
+ struct ion_handle *handle)
+{
+ /* To add condition for SMMU enabled */
+ ion_unmap_kernel(client, handle);
+
+ ion_free(client, handle);
+ /* no client_destrody in legacy*/
+ return 0;
+}
+
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
+{
+ unsigned long ionflag = 0;
+ int rc = 0;
+ int msm_cache_ops = 0;
+
+ if (!abuff) {
+ pr_err("Invalid params: %p, %p\n", __func__, abuff);
+ return -EINVAL;
+ }
+ rc = ion_handle_get_flags(abuff->client, abuff->handle,
+ &ionflag);
+ if (rc) {
+ pr_err("ion_handle_get_flags failed: %d\n", rc);
+ goto cache_op_failed;
+ }
+
+ /* has to be CACHED */
+ if (ION_IS_CACHED(ionflag)) {
+ /* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
+ msm_cache_ops = cache_op;
+ rc = msm_ion_do_cache_op(abuff->client,
+ abuff->handle,
+ (unsigned long *) abuff->data,
+ (unsigned long)abuff->size,
+ msm_cache_ops);
+ if (rc) {
+ pr_err("cache operation failed %d\n", rc);
+ goto cache_op_failed;
+ }
+ }
+cache_op_failed:
+ return rc;
+}
+
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+ struct ion_handle *handle,
+ ion_phys_addr_t *addr, size_t *len)
+{
+ int rc = 0;
+ pr_debug("%s: smmu_enabled = %d\n", __func__,
+ msm_audio_ion_data.smmu_enabled);
+
+ if (msm_audio_ion_data.smmu_enabled) {
+ rc = ion_map_iommu(client, handle, msm_audio_ion_data.domain_id,
+ 0 /*partition_num*/, SZ_4K /*align*/, 0/*iova_length*/,
+ addr, (unsigned long *)len,
+ 0, 0);
+ if (rc) {
+ pr_err("%s: ION map iommu failed %d\n", __func__, rc);
+ return rc;
+ }
+ pr_debug("client=%p, domain=%p, domain_id=%d, group=%p",
+ client, msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+ } else {
+ /* SMMU is disabled*/
+ rc = ion_phys(client, handle, addr, len);
+ }
+ pr_debug("phys=%x, len=%d, rc=%d\n", (unsigned int)*addr, *len, rc);
+ return rc;
+}
+
+static int msm_audio_ion_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ const char *msm_audio_ion_dt = "qcom,smmu-enabled";
+ bool smmu_enabled;
+ enum apr_subsys_state q6_state;
+
+ if (pdev->dev.of_node == NULL) {
+ pr_err("%s: device tree is not found\n", __func__);
+ msm_audio_ion_data.smmu_enabled = 0;
+ return 0;
+ }
+
+ smmu_enabled = of_property_read_bool(pdev->dev.of_node,
+ msm_audio_ion_dt);
+ msm_audio_ion_data.smmu_enabled = smmu_enabled;
+
+ if (smmu_enabled) {
+ q6_state = apr_get_q6_state();
+ if (q6_state == APR_SUBSYS_DOWN) {
+ pr_debug("defering %s, adsp_state %d\n", __func__,
+ q6_state);
+ return -EPROBE_DEFER;
+ } else
+ pr_debug("%s: adsp is ready\n", __func__);
+
+ msm_audio_ion_data.group = iommu_group_find("lpass_audio");
+ if (!msm_audio_ion_data.group) {
+ pr_debug("Failed to find group lpass_audio deferred\n");
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain =
+ iommu_group_get_iommudata(msm_audio_ion_data.group);
+ if (IS_ERR_OR_NULL(msm_audio_ion_data.domain)) {
+ pr_err("Failed to get domain data for group %p",
+ msm_audio_ion_data.group);
+ goto fail_group;
+ }
+ msm_audio_ion_data.domain_id =
+ msm_find_domain_no(msm_audio_ion_data.domain);
+ if (msm_audio_ion_data.domain_id < 0) {
+ pr_err("Failed to get domain index for domain %p",
+ msm_audio_ion_data.domain);
+ goto fail_group;
+ }
+ pr_debug("domain=%p, domain_id=%d, group=%p",
+ msm_audio_ion_data.domain,
+ msm_audio_ion_data.domain_id, msm_audio_ion_data.group);
+
+ /* iommu_attach_group() will make AXI clock ON. For future PL
+ this will require to be called in once per session */
+ rc = iommu_attach_group(msm_audio_ion_data.domain,
+ msm_audio_ion_data.group);
+ if (rc) {
+ pr_err("%s:ION attach group failed %d\n", __func__, rc);
+ return rc;
+ }
+
+ }
+
+ pr_debug("%s: SMMU-Enabled = %d\n", __func__, smmu_enabled);
+ return rc;
+
+fail_group:
+ return -EPROBE_DEFER;
+}
+
+static int msm_audio_ion_remove(struct platform_device *pdev)
+{
+ pr_debug("%s: msm audio ion is unloaded, domain=%p, group=%p\n",
+ __func__, msm_audio_ion_data.domain, msm_audio_ion_data.group);
+ iommu_detach_group(msm_audio_ion_data.domain, msm_audio_ion_data.group);
+
+ return 0;
+}
+
+static const struct of_device_id msm_audio_ion_dt_match[] = {
+ { .compatible = "qcom,msm-audio-ion" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match);
+
+static struct platform_driver msm_audio_ion_driver = {
+ .driver = {
+ .name = "msm-audio-ion",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_audio_ion_dt_match,
+ },
+ .probe = msm_audio_ion_probe,
+ .remove = msm_audio_ion_remove,
+};
+
+static int __init msm_audio_ion_init(void)
+{
+ return platform_driver_register(&msm_audio_ion_driver);
+}
+module_init(msm_audio_ion_init);
+
+static void __exit msm_audio_ion_exit(void)
+{
+ platform_driver_unregister(&msm_audio_ion_driver);
+}
+module_exit(msm_audio_ion_exit);
+
+MODULE_DESCRIPTION("MSM Audio ION module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c
new file mode 100644
index 000000000000..ffbb47573523
--- /dev/null
+++ b/drivers/soc/qcom/smem.c
@@ -0,0 +1,1375 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/export.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/ipc_logging.h>
+#include <linux/kernel.h>
+#include <linux/moduleparam.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+
+#include <soc/qcom/smem.h>
+
+#include <mach/ramdump.h>
+#include <mach/subsystem_notif.h>
+
+#include "smem_private.h"
+
+/**
+ * OVERFLOW_ADD_UNSIGNED() - check for unsigned overflow
+ *
+ * @type: type to check for overflow
+ * @a: left value to use
+ * @b: right value to use
+ * @returns: true if a + b will result in overflow; false otherwise
+ */
+#define OVERFLOW_ADD_UNSIGNED(type, a, b) \
+ (((type)~0 - (a)) < (b) ? true : false)
+
+#define MODEM_SBL_VERSION_INDEX 7
+#define SMEM_VERSION_INFO_SIZE (32 * 4)
+#define SMEM_VERSION 0x000B
+
+enum {
+ MSM_SMEM_DEBUG = 1U << 0,
+ MSM_SMEM_INFO = 1U << 1,
+};
+
+static int msm_smem_debug_mask = MSM_SMEM_INFO;
+module_param_named(debug_mask, msm_smem_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+static void *smem_ipc_log_ctx;
+#define NUM_LOG_PAGES 4
+
+#define IPC_LOG(x...) do { \
+ if (smem_ipc_log_ctx) \
+ ipc_log_string(smem_ipc_log_ctx, x); \
+ } while (0)
+
+
+#define LOG_ERR(x...) do { \
+ pr_err(x); \
+ IPC_LOG(x); \
+ } while (0)
+#define SMEM_DBG(x...) do { \
+ if (msm_smem_debug_mask & MSM_SMEM_DEBUG) \
+ IPC_LOG(x); \
+ } while (0)
+#define SMEM_INFO(x...) do { \
+ if (msm_smem_debug_mask & MSM_SMEM_INFO) \
+ IPC_LOG(x); \
+ } while (0)
+
+#define SMEM_SPINLOCK_SMEM_ALLOC "S:3"
+
+static void *smem_ram_base;
+static resource_size_t smem_ram_size;
+static phys_addr_t smem_ram_phys;
+static remote_spinlock_t remote_spinlock;
+static uint32_t num_smem_areas;
+static struct smem_area *smem_areas;
+static struct ramdump_segment *smem_ramdump_segments;
+static int spinlocks_initialized;
+static void *smem_ramdump_dev;
+static DEFINE_MUTEX(spinlock_init_lock);
+static DEFINE_SPINLOCK(smem_init_check_lock);
+static int smem_module_inited;
+static RAW_NOTIFIER_HEAD(smem_module_init_notifier_list);
+static DEFINE_MUTEX(smem_module_init_notifier_lock);
+
+/* smem security feature components */
+#define SMEM_TOC_IDENTIFIER 0x434f5424 /* "$TOC" */
+#define SMEM_TOC_MAX_EXCLUSIONS 4
+#define SMEM_PART_HDR_IDENTIFIER 0x54525024 /* "$PRT" */
+#define SMEM_ALLOCATION_CANARY 0xa5a5
+
+struct smem_toc_entry {
+ uint32_t offset;
+ uint32_t size;
+ uint32_t flags;
+ uint16_t host0;
+ uint16_t host1;
+ uint32_t size_cacheline;
+ uint32_t reserved[3];
+ uint32_t exclusion_sizes[SMEM_TOC_MAX_EXCLUSIONS];
+};
+
+struct smem_toc {
+ /* Identifier is a constant, set to SMEM_TOC_IDENTIFIER. */
+ uint32_t identifier;
+ uint32_t version;
+ uint32_t num_entries;
+ uint32_t reserved[5];
+ struct smem_toc_entry entry[];
+};
+
+struct smem_partition_header {
+ /* Identifier is a constant, set to SMEM_PART_HDR_IDENTIFIER. */
+ uint32_t identifier;
+ uint16_t host0;
+ uint16_t host1;
+ uint32_t size;
+ uint32_t offset_free_uncached;
+ uint32_t offset_free_cached;
+ uint32_t reserved[3];
+};
+
+struct smem_partition_allocation_header {
+ /* Canary is a constant, set to SMEM_ALLOCATION_CANARY */
+ uint16_t canary;
+ uint16_t smem_type;
+ uint32_t size; /* includes padding bytes */
+ uint16_t padding_data;
+ uint16_t padding_hdr;
+ uint32_t reserved[1];
+};
+
+struct smem_partition_info {
+ uint32_t partition_num;
+ uint32_t offset;
+ uint32_t size_cacheline;
+};
+
+static struct smem_partition_info partitions[NUM_SMEM_SUBSYSTEMS];
+/* end smem security feature components */
+
+struct restart_notifier_block {
+ unsigned processor;
+ char *name;
+ struct notifier_block nb;
+};
+
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data);
+
+static struct restart_notifier_block restart_notifiers[] = {
+ {SMEM_MODEM, "modem", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_Q6, "lpass", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_WCNSS, "wcnss", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_DSPS, "dsps", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_MODEM, "gss", .nb.notifier_call = restart_notifier_cb},
+ {SMEM_Q6, "adsp", .nb.notifier_call = restart_notifier_cb},
+};
+
+static int init_smem_remote_spinlock(void);
+
+/**
+ * smem_phys_to_virt() - Convert a physical base and offset to virtual address
+ *
+ * @base: physical base address to check
+ * @offset: offset from the base to get the final address
+ * @returns: virtual SMEM address; NULL for failure
+ *
+ * Takes a physical address and an offset and checks if the resulting physical
+ * address would fit into one of the smem regions. If so, returns the
+ * corresponding virtual address. Otherwise returns NULL.
+ */
+static void *smem_phys_to_virt(phys_addr_t base, unsigned offset)
+{
+ int i;
+ phys_addr_t phys_addr;
+ resource_size_t size;
+
+ if (OVERFLOW_ADD_UNSIGNED(phys_addr_t, base, offset))
+ return NULL;
+
+ if (!smem_areas) {
+ /*
+ * Early boot - no area configuration yet, so default
+ * to using the main memory region.
+ *
+ * To remove the MSM_SHARED_RAM_BASE and the static
+ * mapping of SMEM in the future, add dump_stack()
+ * to identify the early callers of smem_get_entry()
+ * (which calls this function) and replace those calls
+ * with a new function that knows how to lookup the
+ * SMEM base address before SMEM has been probed.
+ */
+ phys_addr = smem_ram_phys;
+ size = smem_ram_size;
+
+ if (base >= phys_addr && base + offset < phys_addr + size) {
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_ram_base, offset)) {
+ SMEM_INFO("%s: overflow %p %x\n", __func__,
+ smem_ram_base, offset);
+ return NULL;
+ }
+
+ return smem_ram_base + offset;
+ } else {
+ return NULL;
+ }
+ }
+ for (i = 0; i < num_smem_areas; ++i) {
+ phys_addr = smem_areas[i].phys_addr;
+ size = smem_areas[i].size;
+
+ if (base < phys_addr || base + offset >= phys_addr + size)
+ continue;
+
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_areas[i].virt_addr, offset)) {
+ SMEM_INFO("%s: overflow %p %x\n", __func__,
+ smem_areas[i].virt_addr, offset);
+ return NULL;
+ }
+
+ return smem_areas[i].virt_addr + offset;
+ }
+
+ return NULL;
+}
+
+/**
+ * smem_virt_to_phys() - Convert SMEM address to physical address.
+ *
+ * @smem_address: Address of SMEM item (returned by smem_alloc(), etc)
+ * @returns: Physical address (or NULL if there is a failure)
+ *
+ * This function should only be used if an SMEM item needs to be handed
+ * off to a DMA engine.
+ */
+phys_addr_t smem_virt_to_phys(void *smem_address)
+{
+ phys_addr_t phys_addr = 0;
+ int i;
+ void *vend;
+
+ if (!smem_areas)
+ return phys_addr;
+
+ for (i = 0; i < num_smem_areas; ++i) {
+ vend = (void *)(smem_areas[i].virt_addr + smem_areas[i].size);
+
+ if (smem_address >= smem_areas[i].virt_addr &&
+ smem_address < vend) {
+ phys_addr = smem_address - smem_areas[i].virt_addr;
+ phys_addr += smem_areas[i].phys_addr;
+ break;
+ }
+ }
+
+ return phys_addr;
+}
+EXPORT_SYMBOL(smem_virt_to_phys);
+
+/**
+ * __smem_get_entry_nonsecure - Get pointer and size of existing SMEM item
+ *
+ * @id: ID of SMEM item
+ * @size: Pointer to size variable for storing the result
+ * @skip_init_check: True means do not verify that SMEM has been initialized
+ * @use_rspinlock: True to use the remote spinlock
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ */
+static void *__smem_get_entry_nonsecure(unsigned id, unsigned *size,
+ bool skip_init_check, bool use_rspinlock)
+{
+ struct smem_shared *shared = smem_ram_base;
+ struct smem_heap_entry *toc = shared->heap_toc;
+ int use_spinlocks = spinlocks_initialized && use_rspinlock;
+ void *ret = 0;
+ unsigned long flags = 0;
+
+ if (!skip_init_check && !smem_initialized_check())
+ return ret;
+
+ if (id >= SMEM_NUM_ITEMS)
+ return ret;
+
+ if (use_spinlocks)
+ remote_spin_lock_irqsave(&remote_spinlock, flags);
+ /* toc is in device memory and cannot be speculatively accessed */
+ if (toc[id].allocated) {
+ phys_addr_t phys_base;
+
+ *size = toc[id].size;
+ barrier();
+
+ phys_base = toc[id].reserved & BASE_ADDR_MASK;
+ if (!phys_base)
+ phys_base = smem_ram_phys;
+ ret = smem_phys_to_virt(phys_base, toc[id].offset);
+ } else {
+ *size = 0;
+ }
+ if (use_spinlocks)
+ remote_spin_unlock_irqrestore(&remote_spinlock, flags);
+
+ return ret;
+}
+
+/**
+ * __smem_get_entry_secure - Get pointer and size of existing SMEM item with
+ * security support
+ *
+ * @id: ID of SMEM item
+ * @size: Pointer to size variable for storing the result
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @skip_init_check: True means do not verify that SMEM has been initialized
+ * @use_rspinlock: True to use the remote spinlock
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ */
+static void *__smem_get_entry_secure(unsigned id,
+ unsigned *size,
+ unsigned to_proc,
+ unsigned flags,
+ bool skip_init_check,
+ bool use_rspinlock)
+{
+ struct smem_partition_header *hdr;
+ unsigned long lflags = 0;
+ void *item = NULL;
+ struct smem_partition_allocation_header *alloc_hdr;
+ uint32_t partition_num;
+ uint32_t a_hdr_size;
+ int rc;
+
+ SMEM_DBG("%s(%u, %u, %u, %u, %d, %d)\n", __func__, id, *size, to_proc,
+ flags, skip_init_check, use_rspinlock);
+
+ if (!skip_init_check && !smem_initialized_check())
+ return NULL;
+
+ if (id >= SMEM_NUM_ITEMS) {
+ SMEM_INFO("%s: invalid id %d\n", __func__, id);
+ return NULL;
+ }
+
+ if (!(flags & SMEM_ANY_HOST_FLAG) && to_proc >= NUM_SMEM_SUBSYSTEMS) {
+ SMEM_INFO("%s: id %u invalid to_proc %d\n", __func__, id,
+ to_proc);
+ return NULL;
+ }
+
+ if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset)
+ return __smem_get_entry_nonsecure(id, size, skip_init_check,
+ use_rspinlock);
+
+ partition_num = partitions[to_proc].partition_num;
+ hdr = smem_areas[0].virt_addr + partitions[to_proc].offset;
+ if (unlikely(!spinlocks_initialized)) {
+ rc = init_smem_remote_spinlock();
+ if (unlikely(rc)) {
+ SMEM_INFO(
+ "%s: id:%u remote spinlock init failed %d\n",
+ __func__, id, rc);
+ return NULL;
+ }
+ }
+ if (use_rspinlock)
+ remote_spin_lock_irqsave(&remote_spinlock, lflags);
+ if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) {
+ LOG_ERR(
+ "%s: SMEM corruption detected. Partition %d to %d at %p\n",
+ __func__,
+ partition_num,
+ to_proc,
+ hdr);
+ BUG();
+ }
+
+ if (flags & SMEM_ITEM_CACHED_FLAG) {
+ a_hdr_size = ALIGN(sizeof(*alloc_hdr),
+ partitions[to_proc].size_cacheline);
+ for (alloc_hdr = (void *)(hdr) + hdr->size - a_hdr_size;
+ (void *)(alloc_hdr) > (void *)(hdr) +
+ hdr->offset_free_cached;
+ alloc_hdr = (void *)(alloc_hdr) -
+ alloc_hdr->size - a_hdr_size) {
+ if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) {
+ LOG_ERR(
+ "%s: SMEM corruption detected. Partition %d to %d at %p\n",
+ __func__,
+ partition_num,
+ to_proc,
+ alloc_hdr);
+ BUG();
+
+ }
+ if (alloc_hdr->smem_type == id) {
+ /* 8 byte alignment to match legacy */
+ *size = ALIGN(alloc_hdr->size -
+ alloc_hdr->padding_data, 8);
+ item = (void *)(alloc_hdr) - alloc_hdr->size;
+ break;
+ }
+ }
+ } else {
+ for (alloc_hdr = (void *)(hdr) + sizeof(*hdr);
+ (void *)(alloc_hdr) < (void *)(hdr) +
+ hdr->offset_free_uncached;
+ alloc_hdr = (void *)(alloc_hdr) +
+ sizeof(*alloc_hdr) +
+ alloc_hdr->padding_hdr +
+ alloc_hdr->size) {
+ if (alloc_hdr->canary != SMEM_ALLOCATION_CANARY) {
+ LOG_ERR(
+ "%s: SMEM corruption detected. Partition %d to %d at %p\n",
+ __func__,
+ partition_num,
+ to_proc,
+ alloc_hdr);
+ BUG();
+
+ }
+ if (alloc_hdr->smem_type == id) {
+ /* 8 byte alignment to match legacy */
+ *size = ALIGN(alloc_hdr->size -
+ alloc_hdr->padding_data, 8);
+ item = (void *)(alloc_hdr) +
+ sizeof(*alloc_hdr) +
+ alloc_hdr->padding_hdr;
+ break;
+ }
+ }
+ }
+ if (use_rspinlock)
+ remote_spin_unlock_irqrestore(&remote_spinlock, lflags);
+
+ return item;
+}
+
+static void *__smem_find(unsigned id, unsigned size_in, bool skip_init_check)
+{
+ unsigned size;
+ void *ptr;
+
+ ptr = __smem_get_entry_nonsecure(id, &size, skip_init_check, true);
+ if (!ptr)
+ return 0;
+
+ size_in = ALIGN(size_in, 8);
+ if (size_in != size) {
+ SMEM_INFO("smem_find(%u, %u): wrong size %u\n",
+ id, size_in, size);
+ return 0;
+ }
+
+ return ptr;
+}
+
+/**
+ * smem_find - Find existing item with security support
+ *
+ * @id: ID of SMEM item
+ * @size_in: Size of the SMEM item
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ */
+void *smem_find(unsigned id, unsigned size_in, unsigned to_proc, unsigned flags)
+{
+ unsigned size;
+ void *ptr;
+
+ SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, size_in, to_proc,
+ flags);
+
+ ptr = smem_get_entry(id, &size, to_proc, flags);
+ if (!ptr)
+ return 0;
+
+ size_in = ALIGN(size_in, 8);
+ if (size_in != size) {
+ SMEM_INFO("smem_find(%u, %u, %u, %u): wrong size %u\n",
+ id, size_in, to_proc, flags, size);
+ return 0;
+ }
+
+ return ptr;
+}
+EXPORT_SYMBOL(smem_find);
+
+/**
+ * alloc_item_nonsecure - Allocate an SMEM item in the nonsecure partition
+ *
+ * @id: ID of SMEM item
+ * @size_in: Size to allocate
+ * @returns: Pointer to SMEM item or NULL for error
+ *
+ * Assumes the id parameter is valid and does not already exist. Assumes
+ * size_in is already adjusted for alignment, if necessary. Requires the
+ * remote spinlock to already be locked.
+ */
+static void *alloc_item_nonsecure(unsigned id, unsigned size_in)
+{
+ void *smem_base = smem_ram_base;
+ struct smem_shared *shared = smem_base;
+ struct smem_heap_entry *toc = shared->heap_toc;
+ void *ret = NULL;
+
+ if (shared->heap_info.heap_remaining >= size_in) {
+ toc[id].offset = shared->heap_info.free_offset;
+ toc[id].size = size_in;
+ /*
+ * wmb() is necessary to ensure the allocation data is
+ * consistent before setting the allocated flag to prevent race
+ * conditions with remote processors
+ */
+ wmb();
+ toc[id].allocated = 1;
+
+ shared->heap_info.free_offset += size_in;
+ shared->heap_info.heap_remaining -= size_in;
+ ret = smem_base + toc[id].offset;
+ /*
+ * wmb() is necessary to ensure the heap data is consistent
+ * before continuing to prevent race conditions with remote
+ * processors
+ */
+ wmb();
+ } else {
+ SMEM_INFO("%s: id %u not enough memory %u (required %u)\n",
+ __func__, id, shared->heap_info.heap_remaining,
+ size_in);
+ }
+
+ return ret;
+}
+
+/**
+ * alloc_item_secure - Allocate an SMEM item in a secure partition
+ *
+ * @id: ID of SMEM item
+ * @size_in: Size to allocate
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @returns: Pointer to SMEM item or NULL for error
+ *
+ * Assumes the id parameter is valid and does not already exist. Assumes
+ * size_in is the raw size requested by the client. Assumes to_proc is a valid
+ * host, and a valid partition to that host exists. Requires the remote
+ * spinlock to already be locked.
+ */
+static void *alloc_item_secure(unsigned id, unsigned size_in, unsigned to_proc,
+ unsigned flags)
+{
+ void *smem_base = smem_ram_base;
+ struct smem_partition_header *hdr;
+ struct smem_partition_allocation_header *alloc_hdr;
+ uint32_t a_hdr_size;
+ uint32_t a_data_size;
+ uint32_t size_cacheline;
+ uint32_t free_space;
+ uint32_t partition_num;
+ void *ret = NULL;
+
+ hdr = smem_base + partitions[to_proc].offset;
+ partition_num = partitions[to_proc].partition_num;
+
+ if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) {
+ LOG_ERR(
+ "%s: SMEM corruption detected. Partition %d to %d at %p\n",
+ __func__,
+ partition_num,
+ to_proc,
+ hdr);
+ BUG();
+ }
+
+ size_cacheline = partitions[to_proc].size_cacheline;
+ free_space = hdr->offset_free_cached -
+ hdr->offset_free_uncached;
+
+ if (flags & SMEM_ITEM_CACHED_FLAG) {
+ a_hdr_size = ALIGN(sizeof(*alloc_hdr), size_cacheline);
+ a_data_size = ALIGN(size_in, size_cacheline);
+ if (free_space < a_hdr_size + a_data_size) {
+ SMEM_INFO(
+ "%s: id %u not enough memory %u (required %u)\n",
+ __func__, id, free_space,
+ a_hdr_size + a_data_size);
+ return ret;
+ }
+ alloc_hdr = (void *)(hdr) + hdr->offset_free_cached -
+ a_hdr_size;
+ alloc_hdr->canary = SMEM_ALLOCATION_CANARY;
+ alloc_hdr->smem_type = id;
+ alloc_hdr->size = a_data_size;
+ alloc_hdr->padding_data = a_data_size - size_in;
+ alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr);
+ hdr->offset_free_cached = hdr->offset_free_cached -
+ a_hdr_size - a_data_size;
+ ret = (void *)(alloc_hdr) - a_data_size;
+ /*
+ * The SMEM protocol currently does not support cacheable
+ * areas within the smem region, but if it ever does in the
+ * future, then cache management needs to be done here.
+ * The area of memory this item is allocated from will need to
+ * be dynamically made cachable, and a cache flush of the
+ * allocation header using __cpuc_flush_dcache_area and
+ * outer_flush_area will need to be done.
+ */
+ } else {
+ a_hdr_size = sizeof(*alloc_hdr);
+ a_data_size = ALIGN(size_in, 8);
+ if (free_space < a_hdr_size + a_data_size) {
+ SMEM_INFO(
+ "%s: id %u not enough memory %u (required %u)\n",
+ __func__, id, free_space,
+ a_hdr_size + a_data_size);
+ return ret;
+ }
+ alloc_hdr = (void *)(hdr) + hdr->offset_free_uncached;
+ alloc_hdr->canary = SMEM_ALLOCATION_CANARY;
+ alloc_hdr->smem_type = id;
+ alloc_hdr->size = a_data_size;
+ alloc_hdr->padding_data = a_data_size - size_in;
+ alloc_hdr->padding_hdr = a_hdr_size - sizeof(*alloc_hdr);
+ hdr->offset_free_uncached = hdr->offset_free_uncached +
+ a_hdr_size + a_data_size;
+ ret = alloc_hdr + 1;
+ }
+ /*
+ * wmb() is necessary to ensure the heap and allocation data is
+ * consistent before continuing to prevent race conditions with remote
+ * processors
+ */
+ wmb();
+
+ return ret;
+}
+
+/**
+ * smem_alloc - Find an existing item, otherwise allocate it with security
+ * support
+ *
+ * @id: ID of SMEM item
+ * @size_in: Size of the SMEM item
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @returns: Pointer to SMEM item or NULL if it couldn't be found/allocated
+ */
+void *smem_alloc(unsigned id, unsigned size_in, unsigned to_proc,
+ unsigned flags)
+{
+ unsigned long lflags;
+ void *ret = NULL;
+ int rc;
+ unsigned size_out;
+ unsigned a_size_in;
+
+ SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, size_in, to_proc,
+ flags);
+
+ if (!smem_initialized_check())
+ return NULL;
+
+ if (id >= SMEM_NUM_ITEMS) {
+ SMEM_INFO("%s: invalid id %u\n", __func__, id);
+ return NULL;
+ }
+
+ if (!(flags & SMEM_ANY_HOST_FLAG) && to_proc >= NUM_SMEM_SUBSYSTEMS) {
+ SMEM_INFO("%s: invalid to_proc %u for id %u\n", __func__,
+ to_proc, id);
+ return NULL;
+ }
+
+ if (unlikely(!spinlocks_initialized)) {
+ rc = init_smem_remote_spinlock();
+ if (unlikely(rc)) {
+ SMEM_INFO("%s: id:%u remote spinlock init failed %d\n",
+ __func__, id, rc);
+ return NULL;
+ }
+ }
+
+ a_size_in = ALIGN(size_in, 8);
+ remote_spin_lock_irqsave(&remote_spinlock, lflags);
+
+ ret = __smem_get_entry_secure(id, &size_out, to_proc, flags, true,
+ false);
+ if (ret) {
+ SMEM_INFO("%s: %u already allocated\n", __func__, id);
+ if (a_size_in == size_out) {
+ remote_spin_unlock_irqrestore(&remote_spinlock, lflags);
+ return ret;
+ } else {
+ remote_spin_unlock_irqrestore(&remote_spinlock, lflags);
+ SMEM_INFO("%s: id %u wrong size %u (expected %u)\n",
+ __func__, id, size_out, a_size_in);
+ return NULL;
+ }
+ }
+
+ if (id > SMEM_FIXED_ITEM_LAST) {
+ SMEM_INFO("%s: allocating %u size %u to_proc %u flags %u\n",
+ __func__, id, size_in, to_proc, flags);
+ if (flags & SMEM_ANY_HOST_FLAG || !partitions[to_proc].offset)
+ ret = alloc_item_nonsecure(id, a_size_in);
+ else
+ ret = alloc_item_secure(id, size_in, to_proc, flags);
+
+ } else {
+ SMEM_INFO("%s: attempted to allocate non-dynamic item %u\n",
+ __func__, id);
+ }
+
+ remote_spin_unlock_irqrestore(&remote_spinlock, lflags);
+ return ret;
+}
+EXPORT_SYMBOL(smem_alloc);
+
+/**
+ * smem_get_entry - Get existing item with security support
+ *
+ * @id: ID of SMEM item
+ * @size: Pointer to size variable for storing the result
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ */
+void *smem_get_entry(unsigned id, unsigned *size, unsigned to_proc,
+ unsigned flags)
+{
+ SMEM_DBG("%s(%u, %u, %u, %u)\n", __func__, id, *size, to_proc, flags);
+
+ return __smem_get_entry_secure(id, size, to_proc, flags, false, true);
+}
+EXPORT_SYMBOL(smem_get_entry);
+
+/**
+ * smem_get_entry_no_rlock - Get existing item without using remote spinlock
+ *
+ * @id: ID of SMEM item
+ * @size_out: Pointer to size variable for storing the result
+ * @to_proc: SMEM host that shares the item with apps
+ * @flags: Item attribute flags
+ * @returns: Pointer to SMEM item or NULL if it doesn't exist
+ *
+ * This function does not lock the remote spinlock and should only be used in
+ * failure-recover cases such as retrieving the subsystem failure reason during
+ * subsystem restart.
+ */
+void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out, unsigned to_proc,
+ unsigned flags)
+{
+ return __smem_get_entry_secure(id, size_out, to_proc, flags, false,
+ false);
+}
+EXPORT_SYMBOL(smem_get_entry_no_rlock);
+
+/**
+ * smem_get_remote_spinlock - Remote spinlock pointer for unit testing.
+ *
+ * @returns: pointer to SMEM remote spinlock
+ */
+remote_spinlock_t *smem_get_remote_spinlock(void)
+{
+ if (unlikely(!spinlocks_initialized))
+ init_smem_remote_spinlock();
+ return &remote_spinlock;
+}
+EXPORT_SYMBOL(smem_get_remote_spinlock);
+
+/**
+ * smem_get_free_space() - Get the available allocation free space for a
+ * partition
+ *
+ * @to_proc: remote SMEM host. Determines the applicable partition
+ * @returns: size in bytes available to allocate
+ *
+ * Helper function for SMD so that SMD only scans the channel allocation
+ * table for a partition when it is reasonably certain that a channel has
+ * actually been created, because scanning can be expensive. Creating a channel
+ * will consume some of the free space in a partition, so SMD can compare the
+ * last free space size against the current free space size to determine if
+ * a channel may have been created. SMD can't do this directly, because the
+ * necessary partition internals are restricted to just SMEM.
+ */
+unsigned smem_get_free_space(unsigned to_proc)
+{
+ struct smem_partition_header *hdr;
+ struct smem_shared *shared;
+
+ if (to_proc >= NUM_SMEM_SUBSYSTEMS) {
+ pr_err("%s: invalid to_proc:%d\n", __func__, to_proc);
+ return UINT_MAX;
+ }
+
+ if (partitions[to_proc].offset) {
+ if (unlikely(OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_areas[0].virt_addr,
+ partitions[to_proc].offset))) {
+ pr_err("%s: unexpected overflow detected\n", __func__);
+ return UINT_MAX;
+ }
+ hdr = smem_areas[0].virt_addr + partitions[to_proc].offset;
+ return hdr->offset_free_cached - hdr->offset_free_uncached;
+ } else {
+ shared = smem_ram_base;
+ return shared->heap_info.heap_remaining;
+ }
+}
+EXPORT_SYMBOL(smem_get_free_space);
+
+/**
+ * smem_get_version() - Get the smem user version number
+ *
+ * @idx: SMEM user idx in SMEM_VERSION_INFO table.
+ * @returns: smem version number if success otherwise zero.
+ */
+unsigned smem_get_version(unsigned idx)
+{
+ int *version_array;
+
+ if (idx > 32) {
+ pr_err("%s: invalid idx:%d\n", __func__, idx);
+ return 0;
+ }
+
+ version_array = __smem_find(SMEM_VERSION_INFO, SMEM_VERSION_INFO_SIZE,
+ true);
+ if (version_array == NULL)
+ return 0;
+
+ return version_array[idx];
+}
+EXPORT_SYMBOL(smem_get_version);
+
+/**
+ * init_smem_remote_spinlock - Reentrant remote spinlock initialization
+ *
+ * @returns: success or error code for failure
+ */
+static int init_smem_remote_spinlock(void)
+{
+ int rc = 0;
+
+ /*
+ * Optimistic locking. Init only needs to be done once by the first
+ * caller. After that, serializing inits between different callers
+ * is unnecessary. The second check after the lock ensures init
+ * wasn't previously completed by someone else before the lock could
+ * be grabbed.
+ */
+ if (!spinlocks_initialized) {
+ mutex_lock(&spinlock_init_lock);
+ if (!spinlocks_initialized) {
+ rc = remote_spin_lock_init(&remote_spinlock,
+ SMEM_SPINLOCK_SMEM_ALLOC);
+ if (!rc)
+ spinlocks_initialized = 1;
+ }
+ mutex_unlock(&spinlock_init_lock);
+ }
+ return rc;
+}
+
+/**
+ * smem_initialized_check - Reentrant check that smem has been initialized
+ *
+ * @returns: true if initialized, false if not.
+ */
+bool smem_initialized_check(void)
+{
+ static int checked;
+ static int is_inited;
+ unsigned long flags;
+ struct smem_shared *smem;
+
+ if (likely(checked)) {
+ if (unlikely(!is_inited))
+ LOG_ERR("%s: smem not initialized\n", __func__);
+ return is_inited;
+ }
+
+ spin_lock_irqsave(&smem_init_check_lock, flags);
+ if (checked) {
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ if (unlikely(!is_inited))
+ LOG_ERR("%s: smem not initialized\n", __func__);
+ return is_inited;
+ }
+
+ smem = smem_ram_base;
+
+ if (smem->heap_info.initialized != 1)
+ goto failed;
+ if (smem->heap_info.reserved != 0)
+ goto failed;
+
+ /*
+ * The Modem SBL is now the Master SBL version and is required to
+ * pre-initialize SMEM and fill in any necessary configuration
+ * structures. Without the extra configuration data, the SMEM driver
+ * cannot be properly initialized.
+ */
+ if (smem_get_version(MODEM_SBL_VERSION_INDEX) != SMEM_VERSION << 16) {
+ pr_err("%s: SBL version not correct\n", __func__);
+ goto failed;
+ }
+
+ is_inited = 1;
+ checked = 1;
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ return is_inited;
+
+failed:
+ is_inited = 0;
+ checked = 1;
+ spin_unlock_irqrestore(&smem_init_check_lock, flags);
+ LOG_ERR(
+ "%s: shared memory needs to be initialized by SBL before booting\n",
+ __func__);
+ return is_inited;
+}
+EXPORT_SYMBOL(smem_initialized_check);
+
+static int restart_notifier_cb(struct notifier_block *this,
+ unsigned long code,
+ void *data)
+{
+ if (code == SUBSYS_AFTER_SHUTDOWN) {
+ struct restart_notifier_block *notifier;
+
+ notifier = container_of(this,
+ struct restart_notifier_block, nb);
+ SMEM_INFO("%s: ssrestart for processor %d ('%s')\n",
+ __func__, notifier->processor,
+ notifier->name);
+
+ remote_spin_release(&remote_spinlock, notifier->processor);
+ remote_spin_release_all(notifier->processor);
+
+ if (smem_ramdump_dev) {
+ int ret;
+
+ SMEM_DBG("%s: saving ramdump\n", __func__);
+ /*
+ * XPU protection does not currently allow the
+ * auxiliary memory regions to be dumped. If this
+ * changes, then num_smem_areas + 1 should be passed
+ * into do_elf_ramdump() to dump all regions.
+ */
+ ret = do_elf_ramdump(smem_ramdump_dev,
+ smem_ramdump_segments, 1);
+ if (ret < 0)
+ LOG_ERR("%s: unable to dump smem %d\n",
+ __func__, ret);
+ }
+ }
+
+ return NOTIFY_DONE;
+}
+
+static __init int modem_restart_late_init(void)
+{
+ int i;
+ void *handle;
+ struct restart_notifier_block *nb;
+
+ smem_ramdump_dev = create_ramdump_device("smem", NULL);
+ if (IS_ERR_OR_NULL(smem_ramdump_dev)) {
+ LOG_ERR("%s: Unable to create smem ramdump device.\n",
+ __func__);
+ smem_ramdump_dev = NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(restart_notifiers); i++) {
+ nb = &restart_notifiers[i];
+ handle = subsys_notif_register_notifier(nb->name, &nb->nb);
+ SMEM_DBG("%s: registering notif for '%s', handle=%p\n",
+ __func__, nb->name, handle);
+ }
+
+ return 0;
+}
+late_initcall(modem_restart_late_init);
+
+int smem_module_init_notifier_register(struct notifier_block *nb)
+{
+ int ret;
+ if (!nb)
+ return -EINVAL;
+ mutex_lock(&smem_module_init_notifier_lock);
+ ret = raw_notifier_chain_register(&smem_module_init_notifier_list, nb);
+ if (smem_module_inited)
+ nb->notifier_call(nb, 0, NULL);
+ mutex_unlock(&smem_module_init_notifier_lock);
+ return ret;
+}
+EXPORT_SYMBOL(smem_module_init_notifier_register);
+
+int smem_module_init_notifier_unregister(struct notifier_block *nb)
+{
+ int ret;
+ if (!nb)
+ return -EINVAL;
+ mutex_lock(&smem_module_init_notifier_lock);
+ ret = raw_notifier_chain_unregister(&smem_module_init_notifier_list,
+ nb);
+ mutex_unlock(&smem_module_init_notifier_lock);
+ return ret;
+}
+EXPORT_SYMBOL(smem_module_init_notifier_unregister);
+
+static void smem_module_init_notify(uint32_t state, void *data)
+{
+ mutex_lock(&smem_module_init_notifier_lock);
+ smem_module_inited = 1;
+ raw_notifier_call_chain(&smem_module_init_notifier_list,
+ state, data);
+ mutex_unlock(&smem_module_init_notifier_lock);
+}
+
+/**
+ * smem_init_security_partition - Init local structures for a secured smem
+ * partition that has apps as one of the hosts
+ *
+ * @entry: Entry in the security TOC for the partition to init
+ * @num: Partition ID
+ *
+ * Initialize local data structures to point to a secured smem partition
+ * that is accessible by apps and another processor. Assumes that one of the
+ * listed hosts is apps. Verifiess that the partition is valid, otherwise will
+ * skip. Checks for memory corruption and will BUG() if detected. Assumes
+ * smem_areas is already initialized and that smem_areas[0] corresponds to the
+ * smem region with the secured partitions.
+ */
+static void smem_init_security_partition(struct smem_toc_entry *entry,
+ uint32_t num)
+{
+ uint16_t remote_host;
+ struct smem_partition_header *hdr;
+
+ if (!entry->offset) {
+ SMEM_INFO("Skipping smem partition %d - bad offset\n", num);
+ return;
+ }
+ if (!entry->size) {
+ SMEM_INFO("Skipping smem partition %d - bad size\n", num);
+ return;
+ }
+ if (!entry->size_cacheline) {
+ SMEM_INFO("Skipping smem partition %d - bad cacheline\n", num);
+ return;
+ }
+
+ if (entry->host0 == SMEM_APPS)
+ remote_host = entry->host1;
+ else
+ remote_host = entry->host0;
+
+ if (remote_host >= NUM_SMEM_SUBSYSTEMS) {
+ SMEM_INFO("Skipping smem partition %d - bad remote:%d\n", num,
+ remote_host);
+ return;
+ }
+ if (partitions[remote_host].offset) {
+ SMEM_INFO("Skipping smem partition %d - duplicate of %d\n", num,
+ partitions[remote_host].partition_num);
+ return;
+ }
+
+ hdr = smem_areas[0].virt_addr + entry->offset;
+
+ if (hdr->identifier != SMEM_PART_HDR_IDENTIFIER) {
+ LOG_ERR("Smem partition %d hdr magic is bad\n", num);
+ BUG();
+ }
+ if (!hdr->size) {
+ LOG_ERR("Smem partition %d size is 0\n", num);
+ BUG();
+ }
+ if (hdr->offset_free_uncached > hdr->size) {
+ LOG_ERR("Smem partition %d uncached heap exceeds size\n", num);
+ BUG();
+ }
+ if (hdr->offset_free_cached > hdr->size) {
+ LOG_ERR("Smem partition %d cached heap exceeds size\n", num);
+ BUG();
+ }
+ if (hdr->host0 != SMEM_APPS && hdr->host1 != SMEM_APPS) {
+ LOG_ERR("Smem partition %d hosts don't match TOC\n", num);
+ BUG();
+ }
+ if (hdr->host0 != remote_host && hdr->host1 != remote_host) {
+ LOG_ERR("Smem partition %d hosts don't match TOC\n", num);
+ BUG();
+ }
+
+ partitions[remote_host].partition_num = num;
+ partitions[remote_host].offset = entry->offset;
+ partitions[remote_host].size_cacheline = entry->size_cacheline;
+ SMEM_INFO("Partition %d offset:%x remote:%d\n", num, entry->offset,
+ remote_host);
+}
+
+/**
+ * smem_init_security - Init local support for secured smem
+ *
+ * Looks for a valid security TOC, and if one is found, parses it looking for
+ * partitions that apps can access. If any such partitions are found, do the
+ * required local initialization to support them. Assumes smem_areas is inited
+ * and smem_area[0] corresponds to the smem region with the TOC.
+ */
+static void smem_init_security(void)
+{
+ struct smem_toc *toc;
+ uint32_t i;
+
+ SMEM_DBG("%s\n", __func__);
+
+ toc = smem_areas[0].virt_addr + smem_areas[0].size - 4 * 1024;
+
+ if (toc->identifier != SMEM_TOC_IDENTIFIER) {
+ LOG_ERR("%s failed: invalid TOC magic\n", __func__);
+ return;
+ }
+
+ for (i = 0; i < toc->num_entries; ++i) {
+ SMEM_DBG("Partition %d host0:%d host1:%d\n", i,
+ toc->entry[i].host0,
+ toc->entry[i].host1);
+
+ if (toc->entry[i].host0 == SMEM_APPS ||
+ toc->entry[i].host1 == SMEM_APPS)
+ smem_init_security_partition(&toc->entry[i], i);
+ }
+
+ SMEM_DBG("%s done\n", __func__);
+}
+
+static int msm_smem_probe(struct platform_device *pdev)
+{
+ char *key;
+ struct resource *r;
+ phys_addr_t aux_mem_base;
+ resource_size_t aux_mem_size;
+ int temp_string_size = 11; /* max 3 digit count */
+ char temp_string[temp_string_size];
+ int ret;
+ struct ramdump_segment *ramdump_segments_tmp = NULL;
+ struct smem_area *smem_areas_tmp = NULL;
+ int smem_idx = 0;
+ bool security_enabled;
+
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "smem");
+ if (!r) {
+ LOG_ERR("%s: missing reg\n", __func__);
+ return -ENODEV;
+ }
+
+ smem_ram_base = ioremap(r->start, resource_size(r));
+ smem_ram_size = resource_size(r);
+ smem_ram_phys = r->start;
+ if (!smem_ram_base) {
+ LOG_ERR("%s: ioremap_nocache() of addr:%pa size: %pa\n",
+ __func__,
+ &smem_ram_phys, &smem_ram_size);
+ return -ENODEV;
+ }
+
+ if (!smem_initialized_check())
+ return -ENODEV;
+
+ /*
+ * The software implementation requires smem_find(), which needs
+ * smem_ram_base to be intitialized. The remote spinlock item is
+ * guarenteed to be allocated by the bootloader, so this is the
+ * safest and earliest place to init the spinlock.
+ */
+ ret = init_smem_remote_spinlock();
+ if (ret) {
+ LOG_ERR("%s: remote spinlock init failed %d\n", __func__, ret);
+ return ret;
+ }
+
+ key = "irq-reg-base";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ LOG_ERR("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+
+ num_smem_areas = 1;
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ num_smem_areas);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+
+ ++num_smem_areas;
+ if (num_smem_areas > 999) {
+ LOG_ERR("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+ /* Initialize main SMEM region and SSR ramdump region */
+ key = "smem";
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
+ if (!r) {
+ LOG_ERR("%s: missing '%s'\n", __func__, key);
+ return -ENODEV;
+ }
+
+ smem_areas_tmp = kmalloc_array(num_smem_areas, sizeof(struct smem_area),
+ GFP_KERNEL);
+ if (!smem_areas_tmp) {
+ LOG_ERR("%s: smem areas kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ ramdump_segments_tmp = kmalloc_array(num_smem_areas,
+ sizeof(struct ramdump_segment), GFP_KERNEL);
+ if (!ramdump_segments_tmp) {
+ LOG_ERR("%s: ramdump segment kmalloc failed\n", __func__);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+ smem_areas_tmp[smem_idx].phys_addr = r->start;
+ smem_areas_tmp[smem_idx].size = resource_size(r);
+ smem_areas_tmp[smem_idx].virt_addr = smem_ram_base;
+
+ ramdump_segments_tmp[smem_idx].address = r->start;
+ ramdump_segments_tmp[smem_idx].size = resource_size(r);
+ ++smem_idx;
+
+ /* Configure auxiliary SMEM regions */
+ while (1) {
+ scnprintf(temp_string, temp_string_size, "aux-mem%d",
+ smem_idx);
+ r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ temp_string);
+ if (!r)
+ break;
+ aux_mem_base = r->start;
+ aux_mem_size = resource_size(r);
+
+ ramdump_segments_tmp[smem_idx].address = aux_mem_base;
+ ramdump_segments_tmp[smem_idx].size = aux_mem_size;
+
+ smem_areas_tmp[smem_idx].phys_addr = aux_mem_base;
+ smem_areas_tmp[smem_idx].size = aux_mem_size;
+ smem_areas_tmp[smem_idx].virt_addr = ioremap_nocache(
+ (unsigned long)(smem_areas_tmp[smem_idx].phys_addr),
+ smem_areas_tmp[smem_idx].size);
+ SMEM_DBG("%s: %s = %pa %pa -> %p", __func__, temp_string,
+ &aux_mem_base, &aux_mem_size,
+ smem_areas_tmp[smem_idx].virt_addr);
+
+ if (!smem_areas_tmp[smem_idx].virt_addr) {
+ LOG_ERR("%s: ioremap_nocache() of addr:%pa size: %pa\n",
+ __func__,
+ &smem_areas_tmp[smem_idx].phys_addr,
+ &smem_areas_tmp[smem_idx].size);
+ ret = -ENOMEM;
+ goto free_smem_areas;
+ }
+
+ if (OVERFLOW_ADD_UNSIGNED(uintptr_t,
+ (uintptr_t)smem_areas_tmp[smem_idx].virt_addr,
+ smem_areas_tmp[smem_idx].size)) {
+ LOG_ERR(
+ "%s: invalid virtual address block %i: %p:%pa\n",
+ __func__, smem_idx,
+ smem_areas_tmp[smem_idx].virt_addr,
+ &smem_areas_tmp[smem_idx].size);
+ ++smem_idx;
+ ret = -EINVAL;
+ goto free_smem_areas;
+ }
+
+ ++smem_idx;
+ if (smem_idx > 999) {
+ LOG_ERR("%s: max num aux mem regions reached\n",
+ __func__);
+ break;
+ }
+ }
+
+ smem_areas = smem_areas_tmp;
+ smem_ramdump_segments = ramdump_segments_tmp;
+
+ key = "qcom,mpu-enabled";
+ security_enabled = of_property_read_bool(pdev->dev.of_node, key);
+ if (security_enabled) {
+ SMEM_INFO("smem security enabled\n");
+ smem_init_security();
+ }
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ LOG_ERR("%s: of_platform_populate failed %d\n", __func__, ret);
+
+ return 0;
+
+free_smem_areas:
+ for (smem_idx = smem_idx - 1; smem_idx >= 1; --smem_idx)
+ iounmap(smem_areas_tmp[smem_idx].virt_addr);
+
+ num_smem_areas = 0;
+ kfree(ramdump_segments_tmp);
+ kfree(smem_areas_tmp);
+ return ret;
+}
+
+static struct of_device_id msm_smem_match_table[] = {
+ { .compatible = "qcom,smem" },
+ {},
+};
+
+static struct platform_driver msm_smem_driver = {
+ .probe = msm_smem_probe,
+ .driver = {
+ .name = "msm_smem",
+ .owner = THIS_MODULE,
+ .of_match_table = msm_smem_match_table,
+ },
+};
+
+int __init msm_smem_init(void)
+{
+ static bool registered;
+ int rc;
+
+ if (registered)
+ return 0;
+
+ registered = true;
+
+ smem_ipc_log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smem");
+ if (!smem_ipc_log_ctx) {
+ pr_err("%s: unable to create logging context\n", __func__);
+ msm_smem_debug_mask = 0;
+ }
+
+ rc = platform_driver_register(&msm_smem_driver);
+ if (rc) {
+ LOG_ERR("%s: msm_smem_driver register failed %d\n",
+ __func__, rc);
+ return rc;
+ }
+
+ smem_module_init_notify(0, NULL);
+
+ return 0;
+}
+
+module_init(msm_smem_init);
diff --git a/drivers/soc/qcom/smem_debug.c b/drivers/soc/qcom/smem_debug.c
new file mode 100644
index 000000000000..ace89afb614c
--- /dev/null
+++ b/drivers/soc/qcom/smem_debug.c
@@ -0,0 +1,139 @@
+/* arch/arm/mach-msm/smem_debug.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * 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/debugfs.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/jiffies.h>
+
+#include <soc/qcom/smem.h>
+
+#include "smem_private.h"
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define SZ_SMEM_ALLOCATION_TABLE 8192
+
+static void debug_read_mem(struct seq_file *s)
+{
+ unsigned n;
+ struct smem_heap_info *heap_info;
+ struct smem_heap_entry *toc;
+
+ heap_info = smem_find(SMEM_HEAP_INFO, sizeof(struct smem_heap_info),
+ 0,
+ SMEM_ANY_HOST_FLAG);
+ if (!heap_info) {
+ seq_puts(s, "SMEM_HEAP_INFO is NULL\n");
+ return;
+ }
+ toc = smem_find(SMEM_ALLOCATION_TABLE, SZ_SMEM_ALLOCATION_TABLE,
+ 0, SMEM_ANY_HOST_FLAG);
+ if (!toc) {
+ seq_puts(s, "SMEM_ALLOCATION_TABLE is NULL\n");
+ return;
+ }
+
+ seq_printf(s, "heap: init=%d free=%d remain=%d\n",
+ heap_info->initialized,
+ heap_info->free_offset,
+ heap_info->heap_remaining);
+
+ for (n = 0; n < SMEM_NUM_ITEMS; n++) {
+ if (toc[n].allocated == 0)
+ continue;
+ seq_printf(s, "%04d: offset %08x size %08x\n",
+ n, toc[n].offset, toc[n].size);
+ }
+}
+
+static void debug_read_smem_version(struct seq_file *s)
+{
+ uint32_t n, version;
+
+ for (n = 0; n < 32; n++) {
+ version = smem_get_version(n);
+ seq_printf(s, "entry %d: smem = %d proc_comm = %d\n", n,
+ version >> 16,
+ version & 0xffff);
+ }
+}
+
+static void debug_read_build_id(struct seq_file *s)
+{
+ unsigned size;
+ void *data;
+
+ data = smem_get_entry(SMEM_HW_SW_BUILD_ID, &size, 0,
+ SMEM_ANY_HOST_FLAG);
+ if (!data)
+ return;
+
+ seq_write(s, data, size);
+}
+
+static int debugfs_show(struct seq_file *s, void *data)
+{
+ void (*show)(struct seq_file *) = s->private;
+
+ show(s);
+
+ return 0;
+}
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debugfs_show, inode->i_private);
+}
+
+static const struct file_operations debug_ops = {
+ .open = debug_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek,
+};
+
+static void debug_create(const char *name, umode_t mode,
+ struct dentry *dent,
+ void (*show)(struct seq_file *))
+{
+ struct dentry *file;
+
+ file = debugfs_create_file(name, mode, dent, show, &debug_ops);
+ if (!file)
+ pr_err("%s: unable to create file '%s'\n", __func__, name);
+}
+
+static int __init smem_debugfs_init(void)
+{
+ struct dentry *dent;
+
+ dent = debugfs_create_dir("smem", 0);
+ if (IS_ERR(dent))
+ return PTR_ERR(dent);
+
+ debug_create("mem", 0444, dent, debug_read_mem);
+ debug_create("version", 0444, dent, debug_read_smem_version);
+
+ /* NNV: this is google only stuff */
+ debug_create("build", 0444, dent, debug_read_build_id);
+
+ return 0;
+}
+
+late_initcall(smem_debugfs_init);
+#endif
diff --git a/drivers/soc/qcom/smem_private.h b/drivers/soc/qcom/smem_private.h
new file mode 100644
index 000000000000..02e2f0e77c13
--- /dev/null
+++ b/drivers/soc/qcom/smem_private.h
@@ -0,0 +1,104 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#ifndef _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_
+#define _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_
+
+#include <linux/remote_spinlock.h>
+
+#include <mach/ramdump.h>
+
+#define SMD_HEAP_SIZE 512
+
+struct smem_heap_info {
+ unsigned initialized;
+ unsigned free_offset;
+ unsigned heap_remaining;
+ unsigned reserved;
+};
+
+struct smem_heap_entry {
+ unsigned allocated;
+ unsigned offset;
+ unsigned size;
+ unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */
+};
+#define BASE_ADDR_MASK 0xfffffffc
+
+struct smem_proc_comm {
+ unsigned command;
+ unsigned status;
+ unsigned data1;
+ unsigned data2;
+};
+
+struct smem_shared {
+ struct smem_proc_comm proc_comm[4];
+ unsigned version[32];
+ struct smem_heap_info heap_info;
+ struct smem_heap_entry heap_toc[SMD_HEAP_SIZE];
+};
+
+struct smem_area {
+ phys_addr_t phys_addr;
+ resource_size_t size;
+ void __iomem *virt_addr;
+};
+
+/* used for unit testing spinlocks */
+remote_spinlock_t *smem_get_remote_spinlock(void);
+
+bool smem_initialized_check(void);
+
+/**
+ * smem_module_init_notifier_register() - Register a smem module
+ * init notifier block
+ * @nb: Notifier block to be registered
+ *
+ * In order to mark the dependency on SMEM Driver module initialization
+ * register a notifier using this API. Once the smem module_init is
+ * done, notification will be passed to the registered module.
+ */
+int smem_module_init_notifier_register(struct notifier_block *nb);
+
+/**
+ * smem_module_init_notifier_register() - Unregister a smem module
+ * init notifier block
+ * @nb: Notifier block to be unregistered
+ */
+int smem_module_init_notifier_unregister(struct notifier_block *nb);
+
+/**
+ * smem_get_free_space() - Get the available allocation free space for a
+ * partition
+ *
+ * @to_proc: remote SMEM host. Determines the applicable partition
+ * @returns: size in bytes available to allocate
+ *
+ * Helper function for SMD so that SMD only scans the channel allocation
+ * table for a partition when it is reasonably certain that a channel has
+ * actually been created, because scanning can be expensive. Creating a channel
+ * will consume some of the free space in a partition, so SMD can compare the
+ * last free space size against the current free space size to determine if
+ * a channel may have been created. SMD can't do this directly, because the
+ * necessary partition internals are restricted to just SMEM.
+ */
+unsigned smem_get_free_space(unsigned to_proc);
+
+/**
+ * smem_get_version() - Get the smem user version number
+ *
+ * @idx: SMEM user idx in SMEM_VERSION_INFO table.
+ * @returns: smem version number if success otherwise zero.
+ */
+unsigned smem_get_version(unsigned idx);
+#endif /* _ARCH_ARM_MACH_MSM_SMEM_PRIVATE_H_ */
diff --git a/drivers/spi/spi_qsd.c b/drivers/spi/spi_qsd.c
index c185d2f06066..712b621ee16b 100644
--- a/drivers/spi/spi_qsd.c
+++ b/drivers/spi/spi_qsd.c
@@ -48,7 +48,7 @@
static int msm_spi_pm_resume_runtime(struct device *device);
static int msm_spi_pm_suspend_runtime(struct device *device);
-
+static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd);
static inline int msm_spi_configure_gsbi(struct msm_spi *dd,
struct platform_device *pdev)
@@ -133,6 +133,40 @@ static inline void msm_spi_free_gpios(struct msm_spi *dd)
}
}
+static inline int msm_spi_request_cs_gpio(struct msm_spi *dd)
+{
+ int cs_num;
+ int rc;
+
+ cs_num = dd->cur_msg->spi->chip_select;
+ if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) &&
+ (!(dd->cs_gpios[cs_num].valid)) &&
+ (dd->cs_gpios[cs_num].gpio_num >= 0)) {
+ rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
+ spi_cs_rsrcs[cs_num]);
+ if (rc) {
+ dev_err(dd->dev,
+ "gpio_request for pin %d failed,error %d\n",
+ dd->cs_gpios[cs_num].gpio_num, rc);
+ return rc;
+ }
+ dd->cs_gpios[cs_num].valid = 1;
+ }
+ return 0;
+}
+
+static inline void msm_spi_free_cs_gpio(struct msm_spi *dd)
+{
+ int cs_num;
+
+ cs_num = dd->cur_msg->spi->chip_select;
+ if (dd->cs_gpios[cs_num].valid) {
+ gpio_free(dd->cs_gpios[cs_num].gpio_num);
+ dd->cs_gpios[cs_num].valid = 0;
+ }
+}
+
+
/**
* msm_spi_clk_max_rate: finds the nearest lower rate for a clk
* @clk the clock for which to find nearest lower rate
@@ -783,117 +817,190 @@ static void msm_spi_bam_flush(struct msm_spi *dd)
msm_spi_bam_pipe_flush(dd, SPI_BAM_PRODUCER_PIPE);
}
-/**
- * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
- * using BAM.
- * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
- * transfer. Between transfer QUP must change to reset state. A loop is
- * issuing a single BAM transfer at a time. If another tsranfer is
- * required, it waits for the trasfer to finish, then moving to reset
- * state, and back to run state to issue the next transfer.
- * The function dose not wait for the last transfer to end, or if only
- * a single transfer is required, the function dose not wait for it to
- * end.
- * @timeout max time in jiffies to wait for a transfer to finish.
- * @return zero on success
- */
static int
-msm_spi_bam_begin_transfer(struct msm_spi *dd, u32 timeout, u8 bpw)
+msm_spi_bam_process_rx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
{
- u32 bytes_to_send, bytes_sent, n_words_xfr, cons_flags, prod_flags;
- int ret;
+ int ret = 0;
+ u32 data_xfr_size = 0, rem_bc = 0;
+ u32 prod_flags = 0;
+
+ rem_bc = dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd;
+ data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
+
/*
- * QUP must move to reset mode every 64K-1 bytes of transfer
- * (counter is 16 bit)
+ * set flags for last descriptor only
*/
- if (dd->tx_bytes_remaining > SPI_MAX_TRFR_BTWN_RESETS) {
- /* assert chip select unconditionally */
- u32 spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- if (!(spi_ioc & SPI_IO_C_FORCE_CS))
- writel_relaxed(spi_ioc | SPI_IO_C_FORCE_CS,
- dd->base + SPI_IO_CONTROL);
- }
+ if ((desc_cnt == 1)
+ || (*bytes_to_send == data_xfr_size))
+ prod_flags = (dd->write_buf)
+ ? 0 : (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD);
- /* Following flags are required since we are waiting on all transfers */
- cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
/*
- * on a balanced transaction, BAM will set the flags on the producer
- * pipe based on the flags set on the consumer pipe
+ * enqueue read buffer in BAM
*/
- prod_flags = (dd->write_buf) ? 0 : cons_flags;
+ ret = sps_transfer_one(dd->bam.prod.handle,
+ dd->cur_rx_transfer->rx_dma
+ + dd->bam.curr_rx_bytes_recvd,
+ data_xfr_size, dd, prod_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue producer BAM transfer",
+ __func__);
+ return ret;
+ }
- while (dd->tx_bytes_remaining > 0) {
- bytes_sent = dd->cur_transfer->len - dd->tx_bytes_remaining;
- bytes_to_send = min_t(u32, dd->tx_bytes_remaining
- , SPI_MAX_TRFR_BTWN_RESETS);
- n_words_xfr = DIV_ROUND_UP(bytes_to_send
- , dd->bytes_per_word);
+ dd->bam.curr_rx_bytes_recvd += data_xfr_size;
+ *bytes_to_send -= data_xfr_size;
+ dd->bam.bam_rx_len -= data_xfr_size;
+
+ if (!(dd->cur_rx_transfer->len - dd->bam.curr_rx_bytes_recvd)) {
+ struct spi_transfer *t = dd->cur_rx_transfer;
+ struct spi_transfer *next;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ next = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->read_buf = next->rx_buf;
+ dd->cur_rx_transfer = next;
+ dd->bam.curr_rx_bytes_recvd = 0;
+ }
+ }
+ return data_xfr_size;
+}
- msm_spi_set_mx_counts(dd, n_words_xfr);
+static int
+msm_spi_bam_process_tx(struct msm_spi *dd, u32 *bytes_to_send, u32 desc_cnt)
+{
+ int ret = 0;
+ u32 data_xfr_size = 0, rem_bc = 0;
+ u32 cons_flags = 0;
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to set QUP state to run",
- __func__);
- goto xfr_err;
- }
+ rem_bc = dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent;
+ data_xfr_size = (rem_bc < *bytes_to_send) ? rem_bc : *bytes_to_send;
- /* enqueue read buffer in BAM */
- if (dd->read_buf) {
- ret = sps_transfer_one(dd->bam.prod.handle,
- dd->cur_transfer->rx_dma + bytes_sent,
- bytes_to_send, dd, prod_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue producer BAM transfer",
- __func__);
- goto xfr_err;
- }
- }
+ /*
+ * set flags for last descriptor only
+ */
+ if ((desc_cnt == 1)
+ || (*bytes_to_send == data_xfr_size))
+ cons_flags = SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_NWD;
- /* enqueue write buffer in BAM */
- if (dd->write_buf) {
- ret = sps_transfer_one(dd->bam.cons.handle,
- dd->cur_transfer->tx_dma + bytes_sent,
- bytes_to_send, dd, cons_flags);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to queue consumer BAM transfer",
- __func__);
- goto xfr_err;
- }
+ /*
+ * enqueue write buffer in BAM
+ */
+ ret = sps_transfer_one(dd->bam.cons.handle,
+ dd->cur_tx_transfer->tx_dma
+ + dd->bam.curr_tx_bytes_sent,
+ data_xfr_size, dd, cons_flags);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to queue consumer BAM transfer",
+ __func__);
+ return ret;
+ }
+
+ dd->bam.curr_tx_bytes_sent += data_xfr_size;
+ *bytes_to_send -= data_xfr_size;
+ dd->bam.bam_tx_len -= data_xfr_size;
+
+ if (!(dd->cur_tx_transfer->len - dd->bam.curr_tx_bytes_sent)) {
+ struct spi_transfer *t = dd->cur_tx_transfer;
+ struct spi_transfer *next;
+ if (t->transfer_list.next != &dd->cur_msg->transfers) {
+ next = list_entry(t->transfer_list.next,
+ struct spi_transfer,
+ transfer_list);
+ dd->write_buf = next->tx_buf;
+ dd->cur_tx_transfer = next;
+ dd->bam.curr_tx_bytes_sent = 0;
}
+ }
+ return data_xfr_size;
+}
- dd->tx_bytes_remaining -= bytes_to_send;
- /* move to reset state after SPI_MAX_TRFR_BTWN_RESETS */
- if (dd->tx_bytes_remaining > 0) {
- if (!wait_for_completion_timeout(
- &dd->transfer_complete, timeout)) {
- dev_err(dd->dev,
- "%s: SPI transaction timeout",
- __func__);
- dd->cur_msg->status = -EIO;
- ret = -EIO;
+/**
+ * msm_spi_bam_begin_transfer: transfer dd->tx_bytes_remaining bytes
+ * using BAM.
+ * @brief BAM can transfer SPI_MAX_TRFR_BTWN_RESETS byte at a single
+ * transfer. Between transfer QUP must change to reset state. A loop is
+ * issuing a single BAM transfer at a time.
+ * @return zero on success
+ */
+static int
+msm_spi_bam_begin_transfer(struct msm_spi *dd)
+{
+ u32 tx_bytes_to_send = 0, rx_bytes_to_recv = 0;
+ u32 n_words_xfr;
+ s32 ret = 0;
+ u32 prod_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
+ u32 cons_desc_cnt = SPI_BAM_MAX_DESC_NUM - 1;
+ u32 byte_count = 0;
+
+
+ rx_bytes_to_recv = min_t(u32, dd->bam.bam_rx_len,
+ SPI_MAX_TRFR_BTWN_RESETS);
+ tx_bytes_to_send = min_t(u32, dd->bam.bam_tx_len,
+ SPI_MAX_TRFR_BTWN_RESETS);
+ n_words_xfr = DIV_ROUND_UP(rx_bytes_to_recv,
+ dd->bytes_per_word);
+
+ msm_spi_set_mx_counts(dd, n_words_xfr);
+ ret = msm_spi_set_state(dd, SPI_OP_STATE_RUN);
+ if (ret < 0) {
+ dev_err(dd->dev,
+ "%s: Failed to set QUP state to run",
+ __func__);
+ goto xfr_err;
+ }
+
+ while ((rx_bytes_to_recv + tx_bytes_to_send) &&
+ ((cons_desc_cnt + prod_desc_cnt) > 0)) {
+ if (dd->read_buf && (prod_desc_cnt > 0)) {
+ ret = msm_spi_bam_process_rx(dd, &rx_bytes_to_recv,
+ prod_desc_cnt);
+ if (ret < 0)
goto xfr_err;
- }
- ret = msm_spi_set_state(dd, SPI_OP_STATE_RESET);
- if (ret < 0) {
- dev_err(dd->dev,
- "%s: Failed to set QUP state to reset",
- __func__);
+ prod_desc_cnt--;
+ }
+
+ if (dd->write_buf && (cons_desc_cnt > 0)) {
+ ret = msm_spi_bam_process_tx(dd, &tx_bytes_to_send,
+ cons_desc_cnt);
+ if (ret < 0)
goto xfr_err;
- }
- init_completion(&dd->transfer_complete);
+ cons_desc_cnt--;
}
+ byte_count += ret;
}
- return 0;
+ dd->tx_bytes_remaining -= min_t(u32, byte_count,
+ SPI_MAX_TRFR_BTWN_RESETS);
+ return 0;
xfr_err:
return ret;
}
+static int
+msm_spi_bam_next_transfer(struct msm_spi *dd)
+{
+ if (dd->mode != SPI_BAM_MODE)
+ return 0;
+
+ if (dd->tx_bytes_remaining > 0) {
+ init_completion(&dd->transfer_complete);
+ if (msm_spi_set_state(dd, SPI_OP_STATE_RESET))
+ return 0;
+ if ((msm_spi_bam_begin_transfer(dd)) < 0) {
+ dev_err(dd->dev, "%s: BAM transfer setup failed\n",
+ __func__);
+ return 0;
+ }
+ return 1;
+ }
+ return 0;
+}
+
static void msm_spi_setup_dm_transfer(struct msm_spi *dd)
{
dmov_box *box;
@@ -1096,6 +1203,16 @@ static int msm_spi_dm_send_next(struct msm_spi *dd)
return 0;
}
+static int msm_spi_dma_send_next(struct msm_spi *dd)
+{
+ int ret = 0;
+ if (dd->mode == SPI_DMOV_MODE)
+ ret = msm_spi_dm_send_next(dd);
+ if (dd->mode == SPI_BAM_MODE)
+ ret = msm_spi_bam_next_transfer(dd);
+ return ret;
+}
+
static inline void msm_spi_ack_transfer(struct msm_spi *dd)
{
writel_relaxed(SPI_OP_MAX_INPUT_DONE_FLAG |
@@ -1165,10 +1282,8 @@ static irqreturn_t msm_spi_input_irq(int irq, void *dev_id)
if ((!dd->read_buf || op & SPI_OP_MAX_INPUT_DONE_FLAG) &&
(!dd->write_buf || op & SPI_OP_MAX_OUTPUT_DONE_FLAG)) {
msm_spi_ack_transfer(dd);
- if (dd->rx_unaligned_len == 0) {
if (atomic_inc_return(&dd->rx_irq_called) == 1)
return IRQ_HANDLED;
- }
msm_spi_complete(dd);
return IRQ_HANDLED;
}
@@ -1297,14 +1412,14 @@ static irqreturn_t msm_spi_error_irq(int irq, void *dev_id)
}
/**
- * msm_spi_dma_map_buffers: prepares buffer for DMA transfer
+ * msm_spi_dmov_map_buffers: prepares buffer for DMA transfer
* @return zero on success or negative error code
*
* calls dma_map_single() on the read/write buffers, effectively invalidating
* their cash entries. for For WR-WR and WR-RD transfers, allocates temporary
* buffer and copy the data to/from the client buffers
*/
-static int msm_spi_dma_map_buffers(struct msm_spi *dd)
+static int msm_spi_dmov_map_buffers(struct msm_spi *dd)
{
struct device *dev;
struct spi_transfer *first_xfr;
@@ -1323,7 +1438,7 @@ static int msm_spi_dma_map_buffers(struct msm_spi *dd)
* For WR-WR and WR-RD transfers, we allocate our own temporary
* buffer and copy the data to/from the client buffers.
*/
- if (dd->multi_xfr) {
+ if (!dd->qup_ver && dd->multi_xfr) {
dd->temp_buf = kzalloc(dd->cur_msg_len,
GFP_KERNEL | __GFP_DMA);
if (!dd->temp_buf)
@@ -1384,6 +1499,70 @@ error:
return ret;
}
+static int msm_spi_bam_map_buffers(struct msm_spi *dd)
+{
+ int ret = -EINVAL;
+ struct device *dev;
+ struct spi_transfer *first_xfr;
+ struct spi_transfer *nxt_xfr;
+ void *tx_buf, *rx_buf;
+ u32 tx_len, rx_len;
+ int num_xfrs_grped = dd->num_xfrs_grped;
+
+ dev = &dd->cur_msg->spi->dev;
+ first_xfr = dd->cur_transfer;
+
+ do {
+ tx_buf = (void *)first_xfr->tx_buf;
+ rx_buf = first_xfr->rx_buf;
+ tx_len = rx_len = first_xfr->len;
+ if (tx_buf != NULL) {
+ first_xfr->tx_dma = dma_map_single(dev, tx_buf,
+ tx_len, DMA_TO_DEVICE);
+ if (dma_mapping_error(NULL, first_xfr->tx_dma)) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+
+ if (rx_buf != NULL) {
+ first_xfr->rx_dma = dma_map_single(dev, rx_buf, rx_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(NULL, first_xfr->rx_dma)) {
+ if (tx_buf != NULL)
+ dma_unmap_single(NULL,
+ first_xfr->tx_dma,
+ tx_len, DMA_TO_DEVICE);
+ ret = -ENOMEM;
+ goto error;
+ }
+ }
+
+ nxt_xfr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer, transfer_list);
+
+ if (nxt_xfr == NULL)
+ break;
+ num_xfrs_grped--;
+ first_xfr = nxt_xfr;
+ } while (num_xfrs_grped > 0);
+
+ return 0;
+error:
+ msm_spi_dma_unmap_buffers(dd);
+ return ret;
+}
+
+static int msm_spi_dma_map_buffers(struct msm_spi *dd)
+{
+ int ret = 0;
+ if (dd->mode == SPI_DMOV_MODE)
+ ret = msm_spi_dmov_map_buffers(dd);
+ else if (dd->mode == SPI_BAM_MODE)
+ ret = msm_spi_bam_map_buffers(dd);
+ return ret;
+}
+
static void msm_spi_dmov_unmap_buffers(struct msm_spi *dd)
{
struct device *dev;
@@ -1455,21 +1634,39 @@ unmap_end:
static void msm_spi_bam_unmap_buffers(struct msm_spi *dd)
{
struct device *dev;
+ int num_xfrs_grped = dd->num_xfrs_grped;
+ struct spi_transfer *first_xfr;
+ struct spi_transfer *nxt_xfr;
+ void *tx_buf, *rx_buf;
+ u32 tx_len, rx_len;
+
+ dev = &dd->cur_msg->spi->dev;
+ first_xfr = dd->cur_transfer;
/* mapped by client */
if (dd->cur_msg->is_dma_mapped)
return;
- dev = &dd->cur_msg->spi->dev;
- if (dd->cur_transfer->rx_buf)
- dma_unmap_single(dev, dd->cur_transfer->rx_dma,
- dd->cur_transfer->len,
- DMA_FROM_DEVICE);
+ do {
+ tx_buf = (void *)first_xfr->tx_buf;
+ rx_buf = first_xfr->rx_buf;
+ tx_len = rx_len = first_xfr->len;
+ if (tx_buf != NULL)
+ dma_unmap_single(dev, first_xfr->tx_dma,
+ tx_len, DMA_TO_DEVICE);
- if (dd->cur_transfer->tx_buf)
- dma_unmap_single(dev, dd->cur_transfer->tx_dma,
- dd->cur_transfer->len,
- DMA_TO_DEVICE);
+ if (rx_buf != NULL)
+ dma_unmap_single(dev, first_xfr->rx_dma,
+ rx_len, DMA_FROM_DEVICE);
+
+ nxt_xfr = list_entry(first_xfr->transfer_list.next,
+ struct spi_transfer, transfer_list);
+
+ if (nxt_xfr == NULL)
+ break;
+ num_xfrs_grped--;
+ first_xfr = nxt_xfr;
+ } while (num_xfrs_grped > 0);
}
static inline void msm_spi_dma_unmap_buffers(struct msm_spi *dd)
@@ -1506,7 +1703,8 @@ msm_spi_use_dma(struct msm_spi *dd, struct spi_transfer *tr, u8 bpw)
if (dd->cur_msg_len < 3*dd->input_block_size)
return false;
- if (dd->multi_xfr && !dd->read_len && !dd->write_len)
+ if ((dd->qup_ver != SPI_QUP_VERSION_BFAM) &&
+ dd->multi_xfr && !dd->read_len && !dd->write_len)
return false;
if (dd->qup_ver == SPI_QUP_VERSION_NONE) {
@@ -1688,11 +1886,17 @@ static void msm_spi_process_transfer(struct msm_spi *dd)
msm_spi_set_transfer_mode(dd, bpw, read_count);
msm_spi_set_mx_counts(dd, read_count);
- if ((dd->mode == SPI_BAM_MODE) || (dd->mode == SPI_DMOV_MODE))
+ if (dd->mode == SPI_DMOV_MODE) {
if (msm_spi_dma_map_buffers(dd) < 0) {
pr_err("Mapping DMA buffers\n");
return;
+ }
+ } else if (dd->mode == SPI_BAM_MODE) {
+ if (msm_spi_dma_map_buffers(dd) < 0) {
+ pr_err("Mapping DMA buffers\n");
+ return;
}
+ }
msm_spi_set_qup_io_modes(dd);
msm_spi_set_spi_config(dd, bpw);
msm_spi_set_qup_config(dd, bpw);
@@ -1712,7 +1916,7 @@ static void msm_spi_process_transfer(struct msm_spi *dd)
goto transfer_end;
msm_spi_start_write(dd, read_count);
} else if (dd->mode == SPI_BAM_MODE) {
- if ((msm_spi_bam_begin_transfer(dd, timeout, bpw)) < 0)
+ if ((msm_spi_bam_begin_transfer(dd)) < 0)
dev_err(dd->dev, "%s: BAM transfer setup failed\n",
__func__);
}
@@ -1749,9 +1953,10 @@ static void msm_spi_process_transfer(struct msm_spi *dd)
msm_spi_bam_flush(dd);
break;
}
- } while (msm_spi_dm_send_next(dd));
+ } while (msm_spi_dma_send_next(dd));
+
+ msm_spi_udelay(dd->xfrs_delay_usec);
- msm_spi_udelay(dd->cur_transfer->delay_usecs);
transfer_end:
msm_spi_dma_unmap_buffers(dd);
dd->mode = SPI_MODE_NONE;
@@ -1803,155 +2008,137 @@ static void get_transfer_length(struct msm_spi *dd)
dd->multi_xfr = 1;
}
+static inline void write_force_cs(struct msm_spi *dd, bool set_flag)
+{
+ u32 spi_ioc;
+ u32 spi_ioc_orig;
+
+ spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ if (set_flag)
+ spi_ioc |= SPI_IO_C_FORCE_CS;
+ else
+ spi_ioc &= ~SPI_IO_C_FORCE_CS;
+
+ if (spi_ioc != spi_ioc_orig)
+ writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+}
+
static inline int combine_transfers(struct msm_spi *dd)
{
struct spi_transfer *t = dd->cur_transfer;
struct spi_transfer *nxt;
int xfrs_grped = 1;
+ dd->xfrs_delay_usec = 0;
+
+ dd->bam.bam_rx_len = dd->bam.bam_tx_len = 0;
dd->cur_msg_len = dd->cur_transfer->len;
+
+ if (dd->cur_transfer->tx_buf)
+ dd->bam.bam_tx_len += dd->cur_transfer->len;
+ if (dd->cur_transfer->rx_buf)
+ dd->bam.bam_rx_len += dd->cur_transfer->len;
+
while (t->transfer_list.next != &dd->cur_msg->transfers) {
nxt = list_entry(t->transfer_list.next,
struct spi_transfer,
transfer_list);
if (t->cs_change != nxt->cs_change)
return xfrs_grped;
+ if (t->delay_usecs) {
+ dd->xfrs_delay_usec = t->delay_usecs;
+ dev_info(dd->dev, "SPI slave requests delay per txn :%d usecs",
+ t->delay_usecs);
+ return xfrs_grped;
+ }
+ if (nxt->tx_buf)
+ dd->bam.bam_tx_len += nxt->len;
+ if (nxt->rx_buf)
+ dd->bam.bam_rx_len += nxt->len;
+
dd->cur_msg_len += nxt->len;
xfrs_grped++;
t = nxt;
}
- return xfrs_grped;
-}
-static inline void write_force_cs(struct msm_spi *dd, bool set_flag)
-{
- u32 spi_ioc;
- u32 spi_ioc_orig;
-
- spi_ioc = readl_relaxed(dd->base + SPI_IO_CONTROL);
- spi_ioc_orig = spi_ioc;
- if (set_flag)
- spi_ioc |= SPI_IO_C_FORCE_CS;
- else
- spi_ioc &= ~SPI_IO_C_FORCE_CS;
+ if (1 == xfrs_grped)
+ dd->xfrs_delay_usec = dd->cur_transfer->delay_usecs;
- if (spi_ioc != spi_ioc_orig)
- writel_relaxed(spi_ioc, dd->base + SPI_IO_CONTROL);
+ return xfrs_grped;
}
static void msm_spi_process_message(struct msm_spi *dd)
{
int xfrs_grped = 0;
- int cs_num;
int rc;
- bool xfer_delay = false;
- struct spi_transfer *tr;
+ dd->num_xfrs_grped = 0;
+ dd->bam.curr_rx_bytes_recvd = dd->bam.curr_tx_bytes_sent = 0;
dd->write_xfr_cnt = dd->read_xfr_cnt = 0;
- cs_num = dd->cur_msg->spi->chip_select;
- if ((!(dd->cur_msg->spi->mode & SPI_LOOP)) &&
- (!(dd->cs_gpios[cs_num].valid)) &&
- (dd->cs_gpios[cs_num].gpio_num >= 0)) {
- rc = gpio_request(dd->cs_gpios[cs_num].gpio_num,
- spi_cs_rsrcs[cs_num]);
- if (rc) {
- dev_err(dd->dev, "gpio_request for pin %d failed with "
- "error %d\n", dd->cs_gpios[cs_num].gpio_num,
- rc);
- return;
- }
- dd->cs_gpios[cs_num].valid = 1;
- }
-
- list_for_each_entry(tr,
- &dd->cur_msg->transfers,
- transfer_list) {
- if (tr->delay_usecs) {
- dev_info(dd->dev, "SPI slave requests delay per txn :%d",
- tr->delay_usecs);
- xfer_delay = true;
- break;
- }
- }
-
- /* Don't combine xfers if delay is needed after every xfer */
- if (dd->qup_ver || xfer_delay) {
- if (dd->qup_ver)
- write_force_cs(dd, 0);
- list_for_each_entry(dd->cur_transfer,
- &dd->cur_msg->transfers,
- transfer_list) {
- struct spi_transfer *t = dd->cur_transfer;
- struct spi_transfer *nxt;
+ rc = msm_spi_request_cs_gpio(dd);
+ if (rc)
+ return;
- if (t->transfer_list.next != &dd->cur_msg->transfers) {
- nxt = list_entry(t->transfer_list.next,
+ dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
struct spi_transfer,
transfer_list);
- if (dd->qup_ver &&
- t->cs_change == nxt->cs_change)
- write_force_cs(dd, 1);
- else if (dd->qup_ver)
- write_force_cs(dd, 0);
- }
+ get_transfer_length(dd);
+ if (dd->qup_ver || (dd->multi_xfr && !dd->read_len && !dd->write_len)) {
- dd->cur_msg_len = dd->cur_transfer->len;
- msm_spi_process_transfer(dd);
- }
- } else {
- dd->cur_transfer = list_first_entry(&dd->cur_msg->transfers,
- struct spi_transfer,
- transfer_list);
- get_transfer_length(dd);
- if (dd->multi_xfr && !dd->read_len && !dd->write_len) {
- /*
- * Handling of multi-transfers.
- * FIFO mode is used by default
- */
- list_for_each_entry(dd->cur_transfer,
- &dd->cur_msg->transfers,
- transfer_list) {
- if (!dd->cur_transfer->len)
- goto error;
- if (xfrs_grped) {
- xfrs_grped--;
- continue;
- } else {
- dd->read_len = dd->write_len = 0;
- xfrs_grped = combine_transfers(dd);
- }
+ if (dd->qup_ver)
+ write_force_cs(dd, 0);
- dd->cur_tx_transfer = dd->cur_transfer;
- dd->cur_rx_transfer = dd->cur_transfer;
- msm_spi_process_transfer(dd);
+ /*
+ * Handling of multi-transfers.
+ * FIFO mode is used by default
+ */
+ list_for_each_entry(dd->cur_transfer,
+ &dd->cur_msg->transfers,
+ transfer_list) {
+ if (!dd->cur_transfer->len)
+ goto error;
+ if (xfrs_grped) {
xfrs_grped--;
- }
- } else {
- /* Handling of a single transfer or
- * WR-WR or WR-RD transfers
- */
- if ((!dd->cur_msg->is_dma_mapped) &&
- (msm_spi_use_dma(dd, dd->cur_transfer,
- dd->cur_transfer->bits_per_word))) {
- /* Mapping of DMA buffers */
- int ret = msm_spi_dma_map_buffers(dd);
- if (ret < 0) {
- dd->cur_msg->status = ret;
- goto error;
- }
+ continue;
+ } else {
+ dd->read_len = dd->write_len = 0;
+ xfrs_grped = combine_transfers(dd);
+ dd->num_xfrs_grped = xfrs_grped;
+ if (dd->qup_ver)
+ write_force_cs(dd, 1);
}
dd->cur_tx_transfer = dd->cur_transfer;
dd->cur_rx_transfer = dd->cur_transfer;
msm_spi_process_transfer(dd);
+ if (dd->qup_ver && !dd->xfrs_delay_usec)
+ write_force_cs(dd, 0);
+ xfrs_grped--;
}
+ } else {
+ /* Handling of a single transfer or
+ * WR-WR or WR-RD transfers
+ */
+ if ((!dd->cur_msg->is_dma_mapped) &&
+ (msm_spi_use_dma(dd, dd->cur_transfer,
+ dd->cur_transfer->bits_per_word))) {
+ /* Mapping of DMA buffers */
+ int ret = msm_spi_dma_map_buffers(dd);
+ if (ret < 0) {
+ dd->cur_msg->status = ret;
+ goto error;
+ }
+ }
+
+ dd->cur_tx_transfer = dd->cur_transfer;
+ dd->cur_rx_transfer = dd->cur_transfer;
+ dd->num_xfrs_grped = 1;
+ msm_spi_process_transfer(dd);
}
error:
- if (dd->cs_gpios[cs_num].valid) {
- gpio_free(dd->cs_gpios[cs_num].gpio_num);
- dd->cs_gpios[cs_num].valid = 0;
- }
+ msm_spi_free_cs_gpio(dd);
return;
}
diff --git a/drivers/spi/spi_qsd.h b/drivers/spi/spi_qsd.h
index 3fd8a90991ca..8d272a1c28e3 100644
--- a/drivers/spi/spi_qsd.h
+++ b/drivers/spi/spi_qsd.h
@@ -289,6 +289,10 @@ struct msm_spi_bam {
struct msm_spi_bam_pipe prod;
struct msm_spi_bam_pipe cons;
bool deregister_required;
+ u32 curr_rx_bytes_recvd;
+ u32 curr_tx_bytes_sent;
+ u32 bam_rx_len;
+ u32 bam_tx_len;
};
struct msm_spi {
@@ -382,6 +386,8 @@ struct msm_spi {
struct spi_cs_gpio cs_gpios[ARRAY_SIZE(spi_cs_rsrcs)];
enum msm_spi_qup_version qup_ver;
int max_trfr_len;
+ int num_xfrs_grped;
+ u16 xfrs_delay_usec;
};
/* Forward declaration */
diff --git a/drivers/spmi/qpnp-int.c b/drivers/spmi/qpnp-int.c
index 9022cef35782..6379528f963d 100644
--- a/drivers/spmi/qpnp-int.c
+++ b/drivers/spmi/qpnp-int.c
@@ -27,10 +27,9 @@
#include <linux/slab.h>
#include <linux/printk.h>
#include <linux/ratelimit.h>
+#include <linux/irqchip/qpnp-int.h>
#include <asm/irq.h>
-#include <asm/mach/irq.h>
-#include <mach/qpnp-int.h>
/* 16 slave_ids, 256 per_ids per slave, and 8 ints per per_id */
#define QPNPINT_NR_IRQS (16 * 256 * 8)
diff --git a/drivers/spmi/spmi-dbgfs.h b/drivers/spmi/spmi-dbgfs.h
index a419002830a2..255377b5beef 100644
--- a/drivers/spmi/spmi-dbgfs.h
+++ b/drivers/spmi/spmi-dbgfs.h
@@ -17,6 +17,9 @@
#ifdef CONFIG_DEBUG_FS
int spmi_dfs_add_controller(struct spmi_controller *ctrl);
int spmi_dfs_del_controller(struct spmi_controller *ctrl);
+struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl,
+ const char *name, void *data,
+ const struct file_operations *fops);
#else
static inline int spmi_dfs_add_controller(struct spmi_controller *ctrl)
{
@@ -26,10 +29,13 @@ static inline int spmi_dfs_del_controller(struct spmi_controller *ctrl)
{
return 0;
}
-#endif
-struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl,
+static inline struct dentry *spmi_dfs_create_file(struct spmi_controller *ctrl,
const char *name, void *data,
- const struct file_operations *fops);
+ const struct file_operations *fops)
+{
+ return 0;
+}
+#endif
#endif /* _SPMI_DBGFS_H */
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 31b34d28bbe7..e4f13f19a5a5 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -25,7 +25,7 @@
#include <linux/module.h>
#include <linux/seq_file.h>
#include <linux/syscore_ops.h>
-#include <mach/qpnp-int.h>
+#include <linux/irqchip/qpnp-int.h>
#include "spmi-dbgfs.h"
#define SPMI_PMIC_ARB_NAME "spmi_pmic_arb"
@@ -33,6 +33,9 @@
/* PMIC Arbiter configuration registers */
#define PMIC_ARB_VERSION 0x0000
#define PMIC_ARB_INT_EN 0x0004
+#define PMIC_ARB_GENI_CTRL 0x0024
+#define PMIC_ARB_GENI_STATUS 0x0028
+#define PMIC_ARB_PROTOCOL_IRQ_STATUS (0x700 + 0x820)
/* PMIC Arbiter channel registers */
#define PMIC_ARB_CMD(N) (0x0800 + (0x80 * (N)))
@@ -209,6 +212,17 @@ pa_write_data(struct spmi_pmic_arb_dev *dev, u8 *buf, u32 reg, u8 bc)
pmic_arb_write(dev, reg, data);
}
+static void pmic_arb_dbg_dump_regs(struct spmi_pmic_arb_dev *pmic_arb, int ret,
+ const char *msg)
+{
+ u32 irq = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_PROTOCOL_IRQ_STATUS);
+ u32 geni_stat = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_GENI_STATUS);
+ u32 geni_ctrl = readl_relaxed(pmic_arb->cnfg + PMIC_ARB_GENI_CTRL);
+ dev_err(pmic_arb->dev,
+ "err:%d on %s PROTOCOL_IRQ_STATUS:0x%x GENI_STATUS:0x%x GENI_CTRL:0x%x\n",
+ ret, msg, irq, geni_stat, geni_ctrl);
+}
+
/* Non-data command */
static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
{
@@ -230,6 +244,8 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid)
rc = pmic_arb_wait_for_done(pmic_arb);
spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ if (rc)
+ pmic_arb_dbg_dump_regs(pmic_arb, rc, "cmd");
return rc;
}
@@ -277,6 +293,8 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl,
done:
spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ if (rc)
+ pmic_arb_dbg_dump_regs(pmic_arb, rc, "read_cmd");
return rc;
}
@@ -323,6 +341,8 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl,
rc = pmic_arb_wait_for_done(pmic_arb);
spin_unlock_irqrestore(&pmic_arb->lock, flags);
+ if (rc)
+ pmic_arb_dbg_dump_regs(pmic_arb, rc, "write_cmd");
return rc;
}
diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c
index 6dc27dac679d..eb632161d0c9 100644
--- a/drivers/staging/android/alarm-dev.c
+++ b/drivers/staging/android/alarm-dev.c
@@ -40,7 +40,8 @@ do { \
#define ANDROID_ALARM_WAKEUP_MASK ( \
ANDROID_ALARM_RTC_WAKEUP_MASK | \
- ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK)
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK | \
+ ANDROID_ALARM_RTC_POWEROFF_WAKEUP_MASK)
static int alarm_opened;
static DEFINE_SPINLOCK(alarm_slock);
@@ -64,7 +65,8 @@ static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
static int is_wakeup(enum android_alarm_type type)
{
return (type == ANDROID_ALARM_RTC_WAKEUP ||
- type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
+ type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP ||
+ type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP);
}
@@ -92,7 +94,7 @@ static void devalarm_cancel(struct devalarm *alrm)
hrtimer_cancel(&alrm->u.hrt);
}
-static void alarm_clear(enum android_alarm_type alarm_type)
+static void alarm_clear(enum android_alarm_type alarm_type, struct timespec *ts)
{
uint32_t alarm_type_mask = 1U << alarm_type;
unsigned long flags;
@@ -108,6 +110,8 @@ static void alarm_clear(enum android_alarm_type alarm_type)
alarm_enabled &= ~alarm_type_mask;
spin_unlock_irqrestore(&alarm_slock, flags);
+ if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP)
+ set_power_on_alarm(ts->tv_sec, 0);
}
static void alarm_set(enum android_alarm_type alarm_type,
@@ -122,6 +126,9 @@ static void alarm_set(enum android_alarm_type alarm_type,
alarm_enabled |= alarm_type_mask;
devalarm_start(&alarms[alarm_type], timespec_to_ktime(*ts));
spin_unlock_irqrestore(&alarm_slock, flags);
+
+ if (alarm_type == ANDROID_ALARM_RTC_POWEROFF_WAKEUP)
+ set_power_on_alarm(ts->tv_sec, 1);
}
static int alarm_wait(void)
@@ -181,6 +188,7 @@ static int alarm_get_time(enum android_alarm_type alarm_type,
switch (alarm_type) {
case ANDROID_ALARM_RTC_WAKEUP:
case ANDROID_ALARM_RTC:
+ case ANDROID_ALARM_RTC_POWEROFF_WAKEUP:
getnstimeofday(ts);
break;
case ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP:
@@ -224,7 +232,7 @@ static long alarm_do_ioctl(struct file *file, unsigned int cmd,
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_CLEAR(0):
- alarm_clear(alarm_type);
+ alarm_clear(alarm_type, ts);
break;
case ANDROID_ALARM_SET(0):
alarm_set(alarm_type, ts);
@@ -258,6 +266,7 @@ static long alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case ANDROID_ALARM_SET_AND_WAIT(0):
case ANDROID_ALARM_SET(0):
case ANDROID_ALARM_SET_RTC:
+ case ANDROID_ALARM_CLEAR(0):
if (copy_from_user(&ts, (void __user *)arg, sizeof(ts)))
return -EFAULT;
break;
@@ -421,6 +430,8 @@ static int __init alarm_dev_init(void)
CLOCK_BOOTTIME, HRTIMER_MODE_ABS);
hrtimer_init(&alarms[ANDROID_ALARM_SYSTEMTIME].u.hrt,
CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+ alarm_init(&alarms[ANDROID_ALARM_RTC_POWEROFF_WAKEUP].u.alrm,
+ ALARM_REALTIME, devalarm_alarmhandler);
for (i = 0; i < ANDROID_ALARM_TYPE_COUNT; i++) {
alarms[i].type = i;
diff --git a/drivers/staging/android/android_alarm.h b/drivers/staging/android/android_alarm.h
index 4fd32f337f9c..f11ff2952465 100644
--- a/drivers/staging/android/android_alarm.h
+++ b/drivers/staging/android/android_alarm.h
@@ -26,6 +26,7 @@ enum android_alarm_type {
ANDROID_ALARM_RTC,
ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
ANDROID_ALARM_ELAPSED_REALTIME,
+ ANDROID_ALARM_RTC_POWEROFF_WAKEUP,
ANDROID_ALARM_SYSTEMTIME,
ANDROID_ALARM_TYPE_COUNT,
@@ -41,6 +42,8 @@ enum android_alarm_return_flags {
1U << ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
ANDROID_ALARM_ELAPSED_REALTIME_MASK =
1U << ANDROID_ALARM_ELAPSED_REALTIME,
+ ANDROID_ALARM_RTC_POWEROFF_WAKEUP_MASK =
+ 1U << ANDROID_ALARM_RTC_POWEROFF_WAKEUP,
ANDROID_ALARM_SYSTEMTIME_MASK = 1U << ANDROID_ALARM_SYSTEMTIME,
ANDROID_ALARM_TIME_CHANGE_MASK = 1U << 16
};
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index e6e5b1cf527b..bfb131fcd279 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -69,7 +69,6 @@ config THERMAL_GOV_USER_SPACE
config CPU_THERMAL
bool "generic cpu cooling support"
depends on CPU_FREQ
- select CPU_FREQ_TABLE
help
This implements the generic cpu cooling mechanism through frequency
reduction. An ACPI version of this already exists
diff --git a/drivers/thermal/msm8974-tsens.c b/drivers/thermal/msm8974-tsens.c
index 695803ccc3b8..a3d0f64d80fb 100644
--- a/drivers/thermal/msm8974-tsens.c
+++ b/drivers/thermal/msm8974-tsens.c
@@ -1803,19 +1803,19 @@ static int tsens_calib_sensors(void)
}
static struct of_device_id tsens_match[] = {
- { .compatible = "qti,msm-tsens",
+ { .compatible = "qcom,msm-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_8974,
},
- { .compatible = "qti,msm8x26-tsens",
+ { .compatible = "qcom,msm8x26-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_8X26,
},
- { .compatible = "qti,msm8x10-tsens",
+ { .compatible = "qcom,msm8x10-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_8X10,
},
- { .compatible = "qti,fsm9900-tsens",
+ { .compatible = "qcom,fsm9900-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_9900,
},
- { .compatible = "qti,msmkrypton-tsens",
+ { .compatible = "qcom,msmkrypton-tsens",
.data = (void *)TSENS_CALIB_FUSE_MAP_KRYPTON,
},
{}
@@ -1830,7 +1830,7 @@ static int get_device_tree_data(struct platform_device *pdev)
const struct of_device_id *id;
rc = of_property_read_u32(of_node,
- "qti,sensors", &tsens_num_sensors);
+ "qcom,sensors", &tsens_num_sensors);
if (rc) {
dev_err(&pdev->dev, "missing sensor number\n");
return -ENODEV;
@@ -1844,7 +1844,7 @@ static int get_device_tree_data(struct platform_device *pdev)
}
rc = of_property_read_u32_array(of_node,
- "qti,slope", tsens_slope_data, tsens_num_sensors);
+ "qcom,slope", tsens_slope_data, tsens_num_sensors);
if (rc) {
dev_err(&pdev->dev, "invalid or missing property: tsens-slope\n");
return rc;
@@ -1871,9 +1871,9 @@ static int get_device_tree_data(struct platform_device *pdev)
tmdev->tsens_factor = TSENS_SLOPE_FACTOR;
tmdev->tsens_num_sensor = tsens_num_sensors;
tmdev->calibration_less_mode = of_property_read_bool(of_node,
- "qti,calibration-less-mode");
+ "qcom,calibration-less-mode");
tmdev->tsens_local_init = of_property_read_bool(of_node,
- "qti,tsens-local-init");
+ "qcom,tsens-local-init");
tmdev->calib_mode = (u32) id->data;
sensor_id = devm_kzalloc(&pdev->dev,
@@ -1884,7 +1884,7 @@ static int get_device_tree_data(struct platform_device *pdev)
}
rc = of_property_read_u32_array(of_node,
- "qti,sensor-id", sensor_id, tsens_num_sensors);
+ "qcom,sensor-id", sensor_id, tsens_num_sensors);
if (rc) {
pr_debug("Default sensor id mapping\n");
for (i = 0; i < tsens_num_sensors; i++) {
@@ -1899,7 +1899,7 @@ static int get_device_tree_data(struct platform_device *pdev)
}
}
- if (!strcmp(id->compatible, "qti,msmkrypton-tsens"))
+ if (!strcmp(id->compatible, "qcom,msmkrypton-tsens"))
tmdev->tsens_type = TSENS_TYPE2;
else
tmdev->tsens_type = TSENS_TYPE0;
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index 1ca54dc62d7e..510ad88303ff 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -32,7 +32,7 @@
#include <linux/types.h>
#include <linux/thermal.h>
#include <mach/rpm-regulator.h>
-#include <mach/rpm-regulator-smd.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/regulator/consumer.h>
#include <linux/msm_thermal_ioctl.h>
@@ -1163,7 +1163,8 @@ static __ref int do_freq_mitigation(void *data)
cpus[cpu].limited_min_freq = min_freq_req;
update_cpu_freq(cpu);
reset_threshold:
- if (cpus[cpu].freq_thresh_clear) {
+ if (freq_mitigation_enabled &&
+ cpus[cpu].freq_thresh_clear) {
set_threshold(cpus[cpu].sensor_id,
&cpus[cpu].threshold[FREQ_THRESHOLD_HIGH]);
@@ -1225,8 +1226,10 @@ static void freq_mitigation_init(void)
uint32_t cpu = 0;
struct sensor_threshold *hi_thresh = NULL, *low_thresh = NULL;
- if (!freq_mitigation_enabled || freq_mitigation_task)
+ if (freq_mitigation_task)
return;
+ if (!freq_mitigation_enabled)
+ goto init_freq_thread;
for_each_possible_cpu(cpu) {
if (!(msm_thermal_info.freq_mitig_control_mask & BIT(cpu)))
@@ -1245,7 +1248,7 @@ static void freq_mitigation_init(void)
set_threshold(cpus[cpu].sensor_id, hi_thresh);
}
-
+init_freq_thread:
init_completion(&freq_mitigation_complete);
freq_mitigation_task = kthread_run(do_freq_mitigation, NULL,
"msm_thermal:freq_mitig");
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index dc04b81918ec..44874f8755e1 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -1277,7 +1277,7 @@ unsigned int msm_hs_tx_empty(struct uart_port *uport)
struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport);
msm_hs_clock_vote(msm_uport);
- data = msm_hs_read(uport, UARTDM_SR_ADDR);
+ data = msm_hs_read(uport, UART_DM_SR);
msm_hs_clock_unvote(msm_uport);
if (data & UARTDM_SR_TXEMT_BMSK)
@@ -2255,7 +2255,7 @@ void msm_hs_request_clock_off(struct uart_port *uport) {
msm_uport->clk_state = MSM_HS_CLK_REQUEST_OFF;
msm_uport->clk_req_off_state = CLK_REQ_OFF_START;
msm_uport->imr_reg |= UARTDM_ISR_TXLEV_BMSK;
- msm_hs_write(uport, UARTDM_IMR, msm_uport->imr_reg);
+ msm_hs_write(uport, UART_DM_IMR, msm_uport->imr_reg);
/*
* Complete device write before retuning back.
* Hence mb() requires here.
diff --git a/drivers/usb/class/Kconfig b/drivers/usb/class/Kconfig
index bb8b73682a70..4e8a4d9fc331 100644
--- a/drivers/usb/class/Kconfig
+++ b/drivers/usb/class/Kconfig
@@ -46,3 +46,15 @@ config USB_TMC
To compile this driver as a module, choose M here: the
module will be called usbtmc.
+
+config USB_CCID_BRIDGE
+ tristate "USB Smart Card Class (CCID) support"
+ help
+ Say Y here if you want to connect a USB Smart Card device that
+ follows the USB.org specification for Integrated Circuit(s) Cards
+ Interface Devices to your computer's USB port. This module
+ provides a character device interface to exchange the messages.
+ Ioctls facilitate control transfers and interrupt transfers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ccid_bridge.
diff --git a/drivers/usb/class/Makefile b/drivers/usb/class/Makefile
index 32e85277b5cf..c2ee6f3bc0cf 100644
--- a/drivers/usb/class/Makefile
+++ b/drivers/usb/class/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_USB_ACM) += cdc-acm.o
obj-$(CONFIG_USB_PRINTER) += usblp.o
obj-$(CONFIG_USB_WDM) += cdc-wdm.o
obj-$(CONFIG_USB_TMC) += usbtmc.o
+obj-$(CONFIG_USB_CCID_BRIDGE) += ccid_bridge.o
diff --git a/drivers/usb/class/ccid_bridge.c b/drivers/usb/class/ccid_bridge.c
new file mode 100644
index 000000000000..a3e100ab3fee
--- /dev/null
+++ b/drivers/usb/class/ccid_bridge.c
@@ -0,0 +1,885 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt "\n", __func__
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/wait.h>
+#include <linux/cdev.h>
+
+#include <linux/usb/ccid_bridge.h>
+
+#define CCID_CLASS_DECRIPTOR_TYPE 0x21
+#define CCID_NOTIFY_SLOT_CHANGE 0x50
+#define CCID_NOTIFY_HARDWARE_ERROR 0x51
+#define CCID_ABORT_REQ 0x1
+#define CCID_GET_CLK_FREQ_REQ 0x2
+#define CCID_GET_DATA_RATES 0x3
+
+#define CCID_BRIDGE_MSG_SZ 512
+#define CCID_BRIDGE_OPEN_TIMEOUT 500 /* msec */
+#define CCID_CONTROL_TIMEOUT 500 /* msec */
+#define CCID_BRIDGE_MSG_TIMEOUT 500 /* msec */
+
+struct ccid_bridge {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ unsigned int in_pipe;
+ unsigned int out_pipe;
+ unsigned int int_pipe;
+ struct urb *inturb;
+ struct urb *readurb;
+ struct urb *writeurb;
+
+ bool opened;
+ bool events_supported;
+ bool is_suspended;
+ struct mutex open_mutex;
+ struct mutex write_mutex;
+ struct mutex read_mutex;
+ struct mutex event_mutex;
+ int write_result;
+ int read_result;
+ int event_result;
+ wait_queue_head_t open_wq;
+ wait_queue_head_t write_wq;
+ wait_queue_head_t read_wq;
+ wait_queue_head_t event_wq;
+ struct usb_ccid_event cur_event;
+ void *intbuf;
+
+ dev_t chrdev;
+ struct cdev cdev;
+ struct class *class;
+ struct device *device;
+};
+
+static struct ccid_bridge *__ccid_bridge_dev;
+
+static void ccid_bridge_out_cb(struct urb *urb)
+{
+ struct ccid_bridge *ccid = urb->context;
+
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ ccid->write_result = -ENODEV;
+ else
+ ccid->write_result = urb->status ? : urb->actual_length;
+
+ pr_debug("write result = %d", ccid->write_result);
+ wake_up(&ccid->write_wq);
+}
+
+static void ccid_bridge_in_cb(struct urb *urb)
+{
+ struct ccid_bridge *ccid = urb->context;
+
+ if (urb->dev->state == USB_STATE_NOTATTACHED)
+ ccid->read_result = -ENODEV;
+ else
+ ccid->read_result = urb->status ? : urb->actual_length;
+
+ pr_debug("read result = %d", ccid->read_result);
+ wake_up(&ccid->read_wq);
+}
+
+static void ccid_bridge_int_cb(struct urb *urb)
+{
+ struct ccid_bridge *ccid = urb->context;
+ u8 *msg_type;
+ bool wakeup = true;
+
+ if (urb->dev->state == USB_STATE_NOTATTACHED || (urb->status &&
+ urb->status != -ENOENT)) {
+ ccid->event_result = -ENODEV;
+ wakeup = true;
+ goto out;
+ }
+
+ /*
+ * Don't wakeup the event ioctl process during suspend.
+ * The suspend state is not visible to user space.
+ * we wake up the process after resume to send RESUME
+ * event if the device supports remote wakeup.
+ */
+ if (urb->status == -ENOENT && !urb->actual_length) {
+ ccid->event_result = -ENOENT;
+ wakeup = false;
+ goto out;
+ }
+
+ ccid->event_result = 0;
+ msg_type = urb->transfer_buffer;
+ switch (*msg_type) {
+ case CCID_NOTIFY_SLOT_CHANGE:
+ pr_debug("NOTIFY_SLOT_CHANGE event arrived");
+ ccid->cur_event.event = USB_CCID_NOTIFY_SLOT_CHANGE_EVENT;
+ ccid->cur_event.u.notify.slot_icc_state = *(++msg_type);
+ break;
+ case CCID_NOTIFY_HARDWARE_ERROR:
+ pr_debug("NOTIFY_HARDWARE_ERROR event arrived");
+ ccid->cur_event.event = USB_CCID_HARDWARE_ERROR_EVENT;
+ ccid->cur_event.u.error.slot = *(++msg_type);
+ ccid->cur_event.u.error.seq = *(++msg_type);
+ ccid->cur_event.u.error.error_code = *(++msg_type);
+ break;
+ default:
+ pr_err("UNKNOWN event arrived\n");
+ ccid->event_result = -EINVAL;
+ }
+
+out:
+ pr_debug("returning %d", ccid->event_result);
+ if (wakeup)
+ wake_up(&ccid->event_wq);
+}
+
+static int ccid_bridge_submit_inturb(struct ccid_bridge *ccid)
+{
+ int ret = 0;
+
+ /*
+ * Don't resume the bus to submit an interrupt URB.
+ * We submit the URB in resume path. This is important.
+ * Because the device will be in suspend state during
+ * multiple system suspend/resume cycles. The user space
+ * process comes here during system resume after it is
+ * unfrozen.
+ */
+ if (!ccid->int_pipe || ccid->is_suspended)
+ goto out;
+
+ ret = usb_autopm_get_interface(ccid->intf);
+ if (ret < 0) {
+ pr_debug("fail to get autopm with %d\n", ret);
+ goto out;
+ }
+ ret = usb_submit_urb(ccid->inturb, GFP_KERNEL);
+ if (ret < 0)
+ pr_err("fail to submit int urb with %d\n", ret);
+ usb_autopm_put_interface(ccid->intf);
+
+out:
+ pr_debug("returning %d", ret);
+ return ret;
+}
+
+static int ccid_bridge_get_event(struct ccid_bridge *ccid)
+{
+ int ret = 0;
+
+ /*
+ * The first event returned after the device resume
+ * will be RESUME event. This event is set by
+ * the resume.
+ */
+ if (ccid->cur_event.event)
+ goto out;
+
+ ccid->event_result = -EINPROGRESS;
+
+ ret = ccid_bridge_submit_inturb(ccid);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Wait for the notification on interrupt endpoint
+ * or remote wakeup event from the resume. The
+ * int urb completion handler and resume callback
+ * take care of setting the current event.
+ */
+ mutex_unlock(&ccid->event_mutex);
+ ret = wait_event_interruptible(ccid->event_wq,
+ (ccid->event_result != -EINPROGRESS));
+ mutex_lock(&ccid->event_mutex);
+
+ if (ret == -ERESTARTSYS) /* interrupted */
+ usb_kill_urb(ccid->inturb);
+ else
+ ret = ccid->event_result;
+out:
+ pr_debug("returning %d", ret);
+ return ret;
+}
+
+static int ccid_bridge_open(struct inode *ip, struct file *fp)
+{
+ struct ccid_bridge *ccid = container_of(ip->i_cdev,
+ struct ccid_bridge, cdev);
+ int ret;
+
+ pr_debug("called");
+
+ mutex_lock(&ccid->open_mutex);
+ if (ccid->opened) {
+ ret = -EBUSY;
+ goto out;
+ }
+ mutex_unlock(&ccid->open_mutex);
+
+ ret = wait_event_interruptible_timeout(ccid->open_wq,
+ ccid->intf != NULL, msecs_to_jiffies(
+ CCID_BRIDGE_OPEN_TIMEOUT));
+
+ mutex_lock(&ccid->open_mutex);
+
+ if (ret != -ERESTARTSYS && ccid->intf) {
+ fp->private_data = ccid;
+ ccid->opened = true;
+ ret = 0;
+ } else if (!ret) { /* timed out */
+ ret = -ENODEV;
+ }
+out:
+ mutex_unlock(&ccid->open_mutex);
+ pr_debug("returning %d", ret);
+ return ret;
+}
+
+static ssize_t ccid_bridge_write(struct file *fp, const char __user *ubuf,
+ size_t count, loff_t *pos)
+{
+ struct ccid_bridge *ccid = fp->private_data;
+ int ret;
+ char *kbuf;
+
+ pr_debug("called with %d", count);
+
+ if (!ccid->intf) {
+ pr_debug("intf is not active");
+ return -ENODEV;
+ }
+
+ mutex_lock(&ccid->write_mutex);
+
+ if (!count || count > CCID_BRIDGE_MSG_SZ) {
+ pr_err("invalid count");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (!kbuf) {
+ pr_err("fail to allocate memory");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = copy_from_user(kbuf, ubuf, count);
+ if (ret) {
+ pr_err("fail to copy user buf");
+ ret = -EFAULT;
+ goto free_kbuf;
+ }
+
+ ret = usb_autopm_get_interface(ccid->intf);
+ if (ret) {
+ pr_err("fail to get autopm with %d", ret);
+ goto free_kbuf;
+ }
+
+ ccid->write_result = 0;
+
+ usb_fill_bulk_urb(ccid->writeurb, ccid->udev, ccid->out_pipe,
+ kbuf, count, ccid_bridge_out_cb, ccid);
+ ret = usb_submit_urb(ccid->writeurb, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("urb submit fail with %d", ret);
+ goto put_pm;
+ }
+
+ ret = wait_event_interruptible_timeout(ccid->write_wq,
+ ccid->write_result != 0,
+ msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT));
+ if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */
+ usb_kill_urb(ccid->writeurb);
+ if (!ret)
+ ret = -ETIMEDOUT;
+ } else {
+ ret = ccid->write_result;
+ }
+
+ pr_debug("returning %d", ret);
+
+put_pm:
+ if (ret != -ENODEV)
+ usb_autopm_put_interface(ccid->intf);
+free_kbuf:
+ kfree(kbuf);
+out:
+ mutex_unlock(&ccid->write_mutex);
+ return ret;
+
+}
+
+static ssize_t ccid_bridge_read(struct file *fp, char __user *ubuf,
+ size_t count, loff_t *pos)
+{
+ struct ccid_bridge *ccid = fp->private_data;
+ int ret;
+ char *kbuf;
+
+ pr_debug("called with %d", count);
+ if (!ccid->intf) {
+ pr_debug("intf is not active");
+ return -ENODEV;
+ }
+
+ mutex_lock(&ccid->read_mutex);
+
+ if (!count || count > CCID_BRIDGE_MSG_SZ) {
+ pr_err("invalid count");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (!kbuf) {
+ pr_err("fail to allocate memory");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = usb_autopm_get_interface(ccid->intf);
+ if (ret) {
+ pr_err("fail to get autopm with %d", ret);
+ goto free_kbuf;
+ }
+
+ ccid->read_result = 0;
+
+ usb_fill_bulk_urb(ccid->readurb, ccid->udev, ccid->in_pipe,
+ kbuf, count, ccid_bridge_in_cb, ccid);
+ ret = usb_submit_urb(ccid->readurb, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("urb submit fail with %d", ret);
+ if (ret != -ENODEV)
+ usb_autopm_put_interface(ccid->intf);
+ goto free_kbuf;
+ }
+
+
+ ret = wait_event_interruptible_timeout(ccid->read_wq,
+ ccid->read_result != 0,
+ msecs_to_jiffies(CCID_BRIDGE_MSG_TIMEOUT));
+ if (!ret || ret == -ERESTARTSYS) { /* timedout or interrupted */
+ usb_kill_urb(ccid->readurb);
+ if (!ret)
+ ret = -ETIMEDOUT;
+ } else {
+ ret = ccid->read_result;
+ }
+
+
+ if (ret > 0) {
+ if (copy_to_user(ubuf, kbuf, ret))
+ ret = -EFAULT;
+ }
+
+ usb_autopm_put_interface(ccid->intf);
+ pr_debug("returning %d", ret);
+
+free_kbuf:
+ kfree(kbuf);
+out:
+ mutex_unlock(&ccid->read_mutex);
+ return ret;
+}
+
+static long
+ccid_bridge_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+{
+ struct ccid_bridge *ccid = fp->private_data;
+ char *buf;
+ struct usb_ccid_data data;
+ struct usb_ccid_abort abort;
+ struct usb_descriptor_header *header;
+ int ret;
+ struct usb_device *udev = ccid->udev;
+ __u8 intf = ccid->intf->cur_altsetting->desc.bInterfaceNumber;
+ __u8 breq = 0;
+
+ if (!ccid->intf) {
+ pr_debug("intf is not active");
+ return -ENODEV;
+ }
+
+ mutex_lock(&ccid->event_mutex);
+ switch (cmd) {
+ case USB_CCID_GET_CLASS_DESC:
+ pr_debug("GET_CLASS_DESC ioctl called");
+ ret = copy_from_user(&data, (void __user *)arg, sizeof(data));
+ if (ret) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = __usb_get_extra_descriptor(udev->rawdescriptors[0],
+ le16_to_cpu(udev->config[0].desc.wTotalLength),
+ CCID_CLASS_DECRIPTOR_TYPE, (void **) &buf);
+ if (ret) {
+ ret = -ENOENT;
+ break;
+ }
+ header = (struct usb_descriptor_header *) buf;
+ if (data.length != header->bLength) {
+ ret = -EINVAL;
+ break;
+ }
+ ret = copy_to_user((void __user *)data.data, buf, data.length);
+ if (ret)
+ ret = -EFAULT;
+ break;
+ case USB_CCID_GET_CLOCK_FREQUENCIES:
+ pr_debug("GET_CLOCK_FREQUENCIES ioctl called");
+ breq = CCID_GET_CLK_FREQ_REQ;
+ /* fall through */
+ case USB_CCID_GET_DATA_RATES:
+ if (!breq) {
+ pr_debug("GET_DATA_RATES ioctl called");
+ breq = CCID_GET_DATA_RATES;
+ }
+ ret = copy_from_user(&data, (void __user *)arg, sizeof(data));
+ if (ret) {
+ ret = -EFAULT;
+ break;
+ }
+ buf = kmalloc(data.length, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ break;
+ }
+ ret = usb_autopm_get_interface(ccid->intf);
+ if (ret < 0) {
+ pr_debug("fail to get autopm with %d", ret);
+ break;
+ }
+ ret = usb_control_msg(ccid->udev,
+ usb_rcvctrlpipe(ccid->udev, 0),
+ breq, (USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE), 0, intf, buf,
+ data.length, CCID_CONTROL_TIMEOUT);
+ usb_autopm_put_interface(ccid->intf);
+ if (ret == data.length) {
+ ret = copy_to_user((void __user *)data.data, buf,
+ data.length);
+ if (ret)
+ ret = -EFAULT;
+ } else {
+ if (ret > 0)
+ ret = -EPIPE;
+ }
+ kfree(buf);
+ break;
+ case USB_CCID_ABORT:
+ pr_debug("ABORT ioctl called");
+ breq = CCID_ABORT_REQ;
+ ret = copy_from_user(&abort, (void __user *)arg, sizeof(abort));
+ if (ret) {
+ ret = -EFAULT;
+ break;
+ }
+ ret = usb_autopm_get_interface(ccid->intf);
+ if (ret < 0) {
+ pr_debug("fail to get autopm with %d", ret);
+ break;
+ }
+ ret = usb_control_msg(ccid->udev,
+ usb_sndctrlpipe(ccid->udev, 0),
+ breq, (USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE),
+ (abort.seq << 8) | abort.slot, intf, NULL,
+ 0, CCID_CONTROL_TIMEOUT);
+ if (ret < 0)
+ pr_err("abort request failed with err %d\n", ret);
+ usb_autopm_put_interface(ccid->intf);
+ break;
+ case USB_CCID_GET_EVENT:
+ pr_debug("GET_EVENT ioctl called");
+ if (!ccid->events_supported) {
+ ret = -ENOENT;
+ break;
+ }
+ ret = ccid_bridge_get_event(ccid);
+ if (ret == 0) {
+ ret = copy_to_user((void __user *)arg, &ccid->cur_event,
+ sizeof(ccid->cur_event));
+ if (ret)
+ ret = -EFAULT;
+ }
+ ccid->cur_event.event = 0;
+ break;
+ default:
+ pr_err("UNKNOWN ioctl called");
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&ccid->event_mutex);
+ pr_debug("returning %d", ret);
+ return ret;
+}
+
+static int ccid_bridge_release(struct inode *ip, struct file *fp)
+{
+ struct ccid_bridge *ccid = fp->private_data;
+
+ pr_debug("called");
+
+ usb_kill_urb(ccid->writeurb);
+ usb_kill_urb(ccid->readurb);
+ if (ccid->int_pipe)
+ usb_kill_urb(ccid->inturb);
+
+ ccid->event_result = -EIO;
+ wake_up(&ccid->event_wq);
+
+ mutex_lock(&ccid->open_mutex);
+ ccid->opened = false;
+ mutex_unlock(&ccid->open_mutex);
+ return 0;
+}
+
+static const struct file_operations ccid_bridge_fops = {
+ .owner = THIS_MODULE,
+ .open = ccid_bridge_open,
+ .write = ccid_bridge_write,
+ .read = ccid_bridge_read,
+ .unlocked_ioctl = ccid_bridge_ioctl,
+ .release = ccid_bridge_release,
+};
+
+static int ccid_bridge_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct ccid_bridge *ccid = usb_get_intfdata(intf);
+ int ret = 0;
+
+ pr_debug("called");
+
+ if (!ccid->opened)
+ goto out;
+
+ mutex_lock(&ccid->event_mutex);
+ if (ccid->int_pipe) {
+ usb_kill_urb(ccid->inturb);
+ if (ccid->event_result != -ENOENT) {
+ ret = -EBUSY;
+ goto rel_mutex;
+ }
+ }
+
+ ccid->is_suspended = true;
+rel_mutex:
+ mutex_unlock(&ccid->event_mutex);
+out:
+ pr_debug("returning %d", ret);
+ return ret;
+}
+
+static int ccid_bridge_resume(struct usb_interface *intf)
+{
+ struct ccid_bridge *ccid = usb_get_intfdata(intf);
+ int ret;
+
+ pr_debug("called");
+
+ if (!ccid->opened)
+ goto out;
+
+ mutex_lock(&ccid->event_mutex);
+
+ ccid->is_suspended = false;
+
+ if (device_can_wakeup(&ccid->udev->dev)) {
+ ccid->event_result = 0;
+ ccid->cur_event.event = USB_CCID_RESUME_EVENT;
+ wake_up(&ccid->event_wq);
+ } else if (ccid->int_pipe) {
+ ccid->event_result = -EINPROGRESS;
+ ret = usb_submit_urb(ccid->inturb, GFP_KERNEL);
+ if (ret < 0)
+ pr_debug("fail to submit inturb with %d\n", ret);
+ }
+
+ mutex_unlock(&ccid->event_mutex);
+out:
+ return 0;
+}
+
+static int
+ccid_bridge_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct ccid_bridge *ccid = __ccid_bridge_dev;
+ struct usb_host_interface *intf_desc;
+ struct usb_endpoint_descriptor *ep_desc;
+ struct usb_host_endpoint *ep;
+ __u8 epin_addr = 0, epout_addr = 0, epint_addr = 0;
+ int i, ret;
+
+ intf_desc = intf->cur_altsetting;
+
+ if (intf_desc->desc.bNumEndpoints > 3)
+ return -ENODEV;
+
+ for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) {
+ ep_desc = &intf_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(ep_desc))
+ epin_addr = ep_desc->bEndpointAddress;
+ else if (usb_endpoint_is_bulk_out(ep_desc))
+ epout_addr = ep_desc->bEndpointAddress;
+ else if (usb_endpoint_is_int_in(ep_desc))
+ epint_addr = ep_desc->bEndpointAddress;
+ else
+ return -ENODEV;
+ }
+
+ if (!epin_addr || !epout_addr)
+ return -ENODEV;
+
+ ccid->udev = usb_get_dev(interface_to_usbdev(intf));
+ ccid->in_pipe = usb_rcvbulkpipe(ccid->udev, epin_addr);
+ ccid->out_pipe = usb_sndbulkpipe(ccid->udev, epout_addr);
+ if (epint_addr)
+ ccid->int_pipe = usb_rcvbulkpipe(ccid->udev, epint_addr);
+
+ ccid->writeurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ccid->writeurb) {
+ pr_err("fail to allocate write urb");
+ ret = -ENOMEM;
+ goto put_udev;
+ }
+ ccid->readurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ccid->readurb) {
+ pr_err("fail to allocate read urb");
+ ret = -ENOMEM;
+ goto free_writeurb;
+ }
+
+ if (ccid->int_pipe) {
+ pr_debug("interrupt endpoint is present");
+ ep = usb_pipe_endpoint(ccid->udev, ccid->int_pipe);
+ ccid->inturb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ccid->inturb) {
+ pr_err("fail to allocate int urb");
+ ret = -ENOMEM;
+ goto free_readurb;
+ }
+ ccid->intbuf = kmalloc(usb_endpoint_maxp(&ep->desc),
+ GFP_KERNEL);
+ if (!ccid->intbuf) {
+ pr_err("fail to allocated int buf");
+ ret = -ENOMEM;
+ goto free_inturb;
+ }
+ usb_fill_int_urb(ccid->inturb, ccid->udev,
+ usb_rcvintpipe(ccid->udev, epint_addr),
+ ccid->intbuf, usb_endpoint_maxp(&ep->desc),
+ ccid_bridge_int_cb, ccid,
+ ep->desc.bInterval);
+ }
+
+ if (ccid->int_pipe || device_can_wakeup(&ccid->udev->dev)) {
+ pr_debug("event support is present");
+ ccid->events_supported = true;
+ }
+
+ usb_set_intfdata(intf, ccid);
+
+ mutex_lock(&ccid->open_mutex);
+ ccid->intf = intf;
+ wake_up(&ccid->open_wq);
+ mutex_unlock(&ccid->open_mutex);
+
+ pr_info("success");
+ return 0;
+
+free_inturb:
+ if (ccid->int_pipe)
+ usb_free_urb(ccid->inturb);
+free_readurb:
+ usb_free_urb(ccid->readurb);
+free_writeurb:
+ usb_free_urb(ccid->writeurb);
+put_udev:
+ usb_put_dev(ccid->udev);
+ return ret;
+}
+
+static void ccid_bridge_disconnect(struct usb_interface *intf)
+{
+ struct ccid_bridge *ccid = usb_get_intfdata(intf);
+
+ pr_debug("called");
+
+ usb_kill_urb(ccid->writeurb);
+ usb_kill_urb(ccid->readurb);
+ if (ccid->int_pipe)
+ usb_kill_urb(ccid->inturb);
+
+ ccid->event_result = -ENODEV;
+ wake_up(&ccid->event_wq);
+
+ /*
+ * This would synchronize any ongoing read/write/ioctl.
+ * After acquiring the mutex, we can safely set
+ * intf to NULL.
+ */
+ mutex_lock(&ccid->open_mutex);
+ mutex_lock(&ccid->write_mutex);
+ mutex_lock(&ccid->read_mutex);
+ mutex_lock(&ccid->event_mutex);
+
+ usb_free_urb(ccid->writeurb);
+ usb_free_urb(ccid->readurb);
+ if (ccid->int_pipe) {
+ usb_free_urb(ccid->inturb);
+ kfree(ccid->intbuf);
+ ccid->int_pipe = 0;
+ }
+
+ ccid->intf = NULL;
+
+ mutex_unlock(&ccid->event_mutex);
+ mutex_unlock(&ccid->read_mutex);
+ mutex_unlock(&ccid->write_mutex);
+ mutex_unlock(&ccid->open_mutex);
+
+}
+
+static const struct usb_device_id ccid_bridge_ids[] = {
+ { USB_INTERFACE_INFO(USB_CLASS_CSCID, 0, 0) },
+
+ {} /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, ccid_bridge_ids);
+
+static struct usb_driver ccid_bridge_driver = {
+ .name = "ccid_bridge",
+ .probe = ccid_bridge_probe,
+ .disconnect = ccid_bridge_disconnect,
+ .suspend = ccid_bridge_suspend,
+ .resume = ccid_bridge_resume,
+ .id_table = ccid_bridge_ids,
+ .supports_autosuspend = 1,
+};
+
+static int __init ccid_bridge_init(void)
+{
+ int ret;
+ struct ccid_bridge *ccid;
+
+ ccid = kzalloc(sizeof(*ccid), GFP_KERNEL);
+ if (!ccid) {
+ pr_err("Fail to allocate ccid");
+ ret = -ENOMEM;
+ goto out;
+ }
+ __ccid_bridge_dev = ccid;
+
+ mutex_init(&ccid->open_mutex);
+ mutex_init(&ccid->write_mutex);
+ mutex_init(&ccid->read_mutex);
+ mutex_init(&ccid->event_mutex);
+
+ init_waitqueue_head(&ccid->open_wq);
+ init_waitqueue_head(&ccid->write_wq);
+ init_waitqueue_head(&ccid->read_wq);
+ init_waitqueue_head(&ccid->event_wq);
+
+ ret = usb_register(&ccid_bridge_driver);
+ if (ret < 0) {
+ pr_err("Fail to register ccid usb driver with %d", ret);
+ goto free_ccid;
+ }
+
+ ret = alloc_chrdev_region(&ccid->chrdev, 0, 1, "ccid_bridge");
+ if (ret < 0) {
+ pr_err("Fail to allocate ccid char dev region with %d", ret);
+ goto unreg_driver;
+ }
+ ccid->class = class_create(THIS_MODULE, "ccid_bridge");
+ if (IS_ERR(ccid->class)) {
+ ret = PTR_ERR(ccid->class);
+ pr_err("Fail to create ccid class with %d", ret);
+ goto unreg_chrdev;
+ }
+ cdev_init(&ccid->cdev, &ccid_bridge_fops);
+ ccid->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&ccid->cdev, ccid->chrdev, 1);
+ if (ret < 0) {
+ pr_err("Fail to add ccid cdev with %d", ret);
+ goto destroy_class;
+ }
+ ccid->device = device_create(ccid->class,
+ NULL, ccid->chrdev, NULL,
+ "ccid_bridge");
+ if (IS_ERR(ccid->device)) {
+ ret = PTR_ERR(ccid->device);
+ pr_err("Fail to create ccid device with %d", ret);
+ goto del_cdev;
+ }
+
+ pr_info("success");
+
+ return 0;
+
+del_cdev:
+ cdev_del(&ccid->cdev);
+destroy_class:
+ class_destroy(ccid->class);
+unreg_chrdev:
+ unregister_chrdev_region(ccid->chrdev, 1);
+unreg_driver:
+ usb_deregister(&ccid_bridge_driver);
+free_ccid:
+ mutex_destroy(&ccid->open_mutex);
+ mutex_destroy(&ccid->write_mutex);
+ mutex_destroy(&ccid->read_mutex);
+ mutex_destroy(&ccid->event_mutex);
+ kfree(ccid);
+ __ccid_bridge_dev = NULL;
+out:
+ return ret;
+}
+
+static void __exit ccid_bridge_exit(void)
+{
+ struct ccid_bridge *ccid = __ccid_bridge_dev;
+
+ pr_debug("called");
+ device_destroy(ccid->class, ccid->chrdev);
+ cdev_del(&ccid->cdev);
+ class_destroy(ccid->class);
+ unregister_chrdev_region(ccid->chrdev, 1);
+
+ usb_deregister(&ccid_bridge_driver);
+
+ mutex_destroy(&ccid->open_mutex);
+ mutex_destroy(&ccid->write_mutex);
+ mutex_destroy(&ccid->read_mutex);
+ mutex_destroy(&ccid->event_mutex);
+
+ kfree(ccid);
+ __ccid_bridge_dev = NULL;
+}
+
+module_init(ccid_bridge_init);
+module_exit(ccid_bridge_exit);
+
+MODULE_DESCRIPTION("USB CCID bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 78e89d7b314a..1b13c6aabeef 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -648,6 +648,7 @@ struct dwc3_request {
u8 epnum;
struct dwc3_trb *trb;
+ struct dwc3_trb *ztrb;
dma_addr_t trb_dma;
unsigned direction:1;
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index aa6366a8831b..d2b5c86fd815 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -36,6 +36,7 @@
#include <linux/usb/msm_hsusb.h>
#include <linux/usb/msm_ext_chg.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/pm_wakeup.h>
#include <linux/power_supply.h>
#include <linux/qpnp/qpnp-adc.h>
@@ -44,7 +45,6 @@
#include <linux/clk/msm-clk.h>
#include <mach/rpm-regulator.h>
-#include <mach/rpm-regulator-smd.h>
#include <mach/msm_bus.h>
#include <mach/scm.h>
@@ -2505,7 +2505,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
ret = PTR_ERR(mdwc->ref_clk);
goto disable_utmi_clk;
}
- ret = of_property_read_u32(node, "qti,ref-clk-rate",
+ ret = of_property_read_u32(node, "qcom,ref-clk-rate",
(u32 *)&mdwc->ref_clk_rate);
if (ret)
mdwc->ref_clk_rate = 19200000;
@@ -2514,15 +2514,15 @@ static int dwc3_msm_probe(struct platform_device *pdev)
mdwc->id_state = mdwc->ext_xceiv.id = DWC3_ID_FLOAT;
mdwc->ext_xceiv.otg_capability = of_property_read_bool(node,
- "qti,otg-capability");
+ "qcom,otg-capability");
mdwc->charger.charging_disabled = of_property_read_bool(node,
- "qti,charging-disabled");
+ "qcom,charging-disabled");
mdwc->charger.skip_chg_detect = of_property_read_bool(node,
- "qti,skip-charger-detection");
+ "qcom,skip-charger-detection");
mdwc->suspend_resume_no_support = of_property_read_bool(node,
- "qti,no-suspend-resume");
+ "qcom,no-suspend-resume");
/*
* DWC3 has separate IRQ line for OTG events (ID/BSV) and for
* DP and DM linestate transitions during low power mode.
@@ -2623,7 +2623,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
mdwc->io_res = res; /* used to calculate chg block offset */
- if (of_property_read_u32(node, "qti,dwc-usb3-msm-dbm-eps",
+ if (of_property_read_u32(node, "qcom,dwc-usb3-msm-dbm-eps",
&mdwc->dbm_num_eps)) {
dev_err(&pdev->dev,
"unable to read platform data num of dbm eps\n");
@@ -2639,12 +2639,12 @@ static int dwc3_msm_probe(struct platform_device *pdev)
goto disable_ref_clk;
}
- if (of_property_read_u32(node, "qti,dwc-usb3-msm-tx-fifo-size",
+ if (of_property_read_u32(node, "qcom,dwc-usb3-msm-tx-fifo-size",
&mdwc->tx_fifo_size))
dev_err(&pdev->dev,
"unable to read platform data tx fifo size\n");
- if (of_property_read_u32(node, "qti,dwc-usb3-msm-qdss-tx-fifo-size",
+ if (of_property_read_u32(node, "qcom,dwc-usb3-msm-qdss-tx-fifo-size",
&mdwc->qdss_tx_fifo_size))
dev_err(&pdev->dev,
"unable to read platform data qdss tx fifo size\n");
@@ -2677,15 +2677,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
}
}
- if (node) {
- ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
- if (ret) {
- dev_err(&pdev->dev,
- "failed to add create dwc3 core\n");
- goto put_psupply;
- }
- }
-
/* Assumes dwc3 is the only DT child of dwc3-msm */
dwc3_node = of_get_next_available_child(node, NULL);
if (!dwc3_node) {
@@ -2694,12 +2685,37 @@ static int dwc3_msm_probe(struct platform_device *pdev)
}
host_mode = of_property_read_bool(dwc3_node, "host-only-mode");
+ if (host_mode && of_get_property(pdev->dev.of_node, "vbus_dwc3-supply",
+ NULL)) {
+ mdwc->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3");
+ if (IS_ERR(mdwc->vbus_otg)) {
+ dev_err(&pdev->dev, "Failed to get vbus regulator\n");
+ ret = PTR_ERR(mdwc->vbus_otg);
+ of_node_put(dwc3_node);
+ goto put_psupply;
+ }
+ ret = regulator_enable(mdwc->vbus_otg);
+ if (ret) {
+ mdwc->vbus_otg = 0;
+ dev_err(&pdev->dev, "Failed to enable vbus_otg\n");
+ of_node_put(dwc3_node);
+ goto put_psupply;
+ }
+ }
+
+ ret = of_platform_populate(node, NULL, NULL, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to add create dwc3 core\n");
+ of_node_put(dwc3_node);
+ goto disable_vbus;
+ }
mdwc->dwc3 = of_find_device_by_node(dwc3_node);
of_node_put(dwc3_node);
if (!mdwc->dwc3) {
dev_err(&pdev->dev, "failed to get dwc3 platform device\n");
- goto put_psupply;
+ goto put_dwc3;
}
mdwc->hs_phy = devm_usb_get_phy_by_phandle(&mdwc->dwc3->dev,
@@ -2762,17 +2778,6 @@ static int dwc3_msm_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "No OTG, DWC3 running in host only mode\n");
mdwc->scope = POWER_SUPPLY_SCOPE_SYSTEM;
mdwc->hs_phy->flags |= PHY_HOST_MODE;
- mdwc->vbus_otg = devm_regulator_get(&pdev->dev, "vbus_dwc3");
- if (IS_ERR(mdwc->vbus_otg)) {
- dev_dbg(&pdev->dev, "Failed to get vbus regulator\n");
- mdwc->vbus_otg = 0;
- } else {
- ret = regulator_enable(mdwc->vbus_otg);
- if (ret) {
- mdwc->vbus_otg = 0;
- dev_err(&pdev->dev, "Failed to enable vbus_otg\n");
- }
- }
} else {
dev_err(&pdev->dev, "DWC3 device-only mode not supported\n");
ret = -ENODEV;
@@ -2785,7 +2790,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Fail to setup dwc3 setup cdev\n");
}
- ret = of_property_read_u32(node, "qti,restore-sec-cfg-for-scm-dev-id",
+ ret = of_property_read_u32(node, "qcom,restore-sec-cfg-for-scm-dev-id",
&mdwc->scm_dev_id);
if (ret && ret != -ENODATA)
dev_dbg(&pdev->dev, "unable to read scm device id\n");
@@ -2797,7 +2802,7 @@ static int dwc3_msm_probe(struct platform_device *pdev)
pm_runtime_set_active(mdwc->dev);
pm_runtime_enable(mdwc->dev);
- if (of_property_read_bool(node, "qti,reset_hsphy_sleep_clk_on_init")) {
+ if (of_property_read_bool(node, "qcom,reset_hsphy_sleep_clk_on_init")) {
ret = clk_reset(mdwc->hsphy_sleep_clk, CLK_RESET_ASSERT);
if (ret) {
dev_err(&pdev->dev,
@@ -2815,10 +2820,15 @@ static int dwc3_msm_probe(struct platform_device *pdev)
}
}
+ msm_bam_set_usb_dev(mdwc->dev);
+
return 0;
put_dwc3:
platform_device_put(mdwc->dwc3);
+disable_vbus:
+ if (!IS_ERR_OR_NULL(mdwc->vbus_otg))
+ regulator_disable(mdwc->vbus_otg);
put_psupply:
if (mdwc->usb_psy.dev)
power_supply_unregister(&mdwc->usb_psy);
@@ -2865,7 +2875,7 @@ static int dwc3_msm_remove(struct platform_device *pdev)
dwc3_start_chg_det(&mdwc->charger, false);
if (mdwc->usb_psy.dev)
power_supply_unregister(&mdwc->usb_psy);
- if (mdwc->vbus_otg)
+ if (!IS_ERR_OR_NULL(mdwc->vbus_otg))
regulator_disable(mdwc->vbus_otg);
pm_runtime_disable(mdwc->dev);
@@ -2987,7 +2997,7 @@ static const struct dev_pm_ops dwc3_msm_dev_pm_ops = {
static const struct of_device_id of_dwc3_matach[] = {
{
- .compatible = "qti,dwc-usb3-msm",
+ .compatible = "qcom,dwc-usb3-msm",
},
{ },
};
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 4adb44b429b3..ebaf9d162c17 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -294,6 +294,15 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
dep->busy_slot++;
} while(++i < req->request.num_mapped_sgs);
req->queued = false;
+
+ if (req->request.zero && req->ztrb) {
+ dep->busy_slot++;
+ req->ztrb = NULL;
+ if (((dep->busy_slot & DWC3_TRB_MASK) ==
+ DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dep->busy_slot++;
+ }
}
list_del(&req->list);
req->trb = NULL;
@@ -863,6 +872,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
dep->free_slot++;
+update_trb:
trb->size = DWC3_TRB_SIZE_LENGTH(length);
trb->bpl = lower_32_bits(dma);
trb->bph = upper_32_bits(dma);
@@ -897,8 +907,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
trb->ctrl |= DWC3_TRB_CTRL_CSP;
- } else if (last) {
- trb->ctrl |= DWC3_TRB_CTRL_LST;
}
if (chain)
@@ -908,6 +916,25 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(req->request.stream_id);
trb->ctrl |= DWC3_TRB_CTRL_HWO;
+
+ if (req->request.zero && length &&
+ (length % usb_endpoint_maxp(dep->endpoint.desc) == 0)) {
+ /* Skip the LINK-TRB on ISOC */
+ if (((dep->free_slot & DWC3_TRB_MASK) == DWC3_TRB_NUM - 1) &&
+ usb_endpoint_xfer_isoc(dep->endpoint.desc))
+ dep->free_slot++;
+
+ trb = &dep->trb_pool[dep->free_slot & DWC3_TRB_MASK];
+ dep->free_slot++;
+
+ req->ztrb = trb;
+ length = 0;
+
+ goto update_trb;
+ }
+
+ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && last)
+ trb->ctrl |= DWC3_TRB_CTRL_LST;
}
/*
@@ -1011,12 +1038,25 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting)
}
dbg_queue(dep->number, &req->request, 0);
} else {
+ struct dwc3_request *req1;
+ int maxpkt_size = usb_endpoint_maxp(dep->endpoint.desc);
+
dma = req->request.dma;
length = req->request.length;
trbs_left--;
- if (!trbs_left)
+ if (req->request.zero && length &&
+ (length % maxpkt_size == 0))
+ trbs_left--;
+
+ if (!trbs_left) {
last_one = 1;
+ } else if (dep->direction && (trbs_left <= 1)) {
+ req1 = next_request(&req->list);
+ if (req1->request.zero && req1->request.length
+ && (req1->request.length % maxpkt_size == 0))
+ last_one = 1;
+ }
/* Is this the last request? */
if (list_is_last(&req->list, &dep->request_list))
@@ -2201,6 +2241,17 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
break;
}while (++i < req->request.num_mapped_sgs);
+ if (req->ztrb) {
+ trb = req->ztrb;
+ if ((event->status & DEPEVT_STATUS_LST) &&
+ (trb->ctrl & (DWC3_TRB_CTRL_LST |
+ DWC3_TRB_CTRL_HWO)))
+ ret = 1;
+
+ if ((event->status & DEPEVT_STATUS_IOC) &&
+ (trb->ctrl & DWC3_TRB_CTRL_IOC))
+ ret = 1;
+ }
dwc3_gadget_giveback(dep, req, status);
if (ret)
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 5a0987daa451..f8d9ab973f95 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -444,12 +444,23 @@ static void ffs_function_enable(struct android_usb_function *f)
{
struct android_dev *dev = f->android_dev;
struct functionfs_config *config = f->config;
+ int ret = 0;
config->enabled = true;
/* Disable the gadget until the function is ready */
- if (!config->opened)
+ if (!config->opened) {
android_disable(dev);
+ } else {
+ /*
+ * Call functionfs_bind to handle the case where userspace
+ * passed descriptors before updating enabled functions list
+ */
+ ret = functionfs_bind(config->data, dev->cdev);
+ if (ret)
+ pr_err("%s: functionfs_bind failed (%d)\n", __func__,
+ ret);
+ }
}
static void ffs_function_disable(struct android_usb_function *f)
@@ -3106,6 +3117,7 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
struct android_configuration *conf;
int value = -EOPNOTSUPP;
unsigned long flags;
+ bool do_work = false;
req->zero = 0;
req->length = 0;
@@ -3135,13 +3147,14 @@ android_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *c)
spin_lock_irqsave(&cdev->lock, flags);
if (!dev->connected) {
dev->connected = 1;
- schedule_work(&dev->work);
+ do_work = true;
} else if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
cdev->config) {
- schedule_work(&dev->work);
+ do_work = true;
}
spin_unlock_irqrestore(&cdev->lock, flags);
-
+ if (do_work)
+ schedule_work(&dev->work);
return value;
}
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index c36cbb788c57..d3c2a9f89afa 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -354,28 +354,6 @@ static void free_reqs(struct diag_context *ctxt)
}
/**
- * usb_diag_free_req() - Free USB requests
- * @ch: Channel handler
- *
- * This function free read and write USB requests for the interface
- * associated with this channel.
- *
- */
-void usb_diag_free_req(struct usb_diag_ch *ch)
-{
- struct diag_context *ctxt = ch->priv_usb;
- unsigned long flags;
-
- if (ctxt) {
- spin_lock_irqsave(&ctxt->lock, flags);
- free_reqs(ctxt);
- spin_unlock_irqrestore(&ctxt->lock, flags);
- }
-
-}
-EXPORT_SYMBOL(usb_diag_free_req);
-
-/**
* usb_diag_alloc_req() - Allocate USB requests
* @ch: Channel handler
* @n_write: Number of requests for Tx
diff --git a/drivers/usb/gadget/f_mbim.c b/drivers/usb/gadget/f_mbim.c
index 4219bfa6d5ce..a93e392beb93 100644
--- a/drivers/usb/gadget/f_mbim.c
+++ b/drivers/usb/gadget/f_mbim.c
@@ -696,9 +696,9 @@ static int mbim_bam_connect(struct f_mbim *dev)
pr_info("dev:%p portno:%d\n", dev, dev->port_num);
src_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
- USB_TO_PEER_PERIPHERAL, dev->port_num);
+ USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, dev->port_num);
dst_connection_idx = usb_bam_get_connection_idx(gadget->name, bam_name,
- PEER_PERIPHERAL_TO_USB, dev->port_num);
+ PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, dev->port_num);
if (src_connection_idx < 0 || dst_connection_idx < 0) {
pr_err("%s: usb_bam_get_connection_idx failed\n", __func__);
return ret;
@@ -1695,8 +1695,10 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
return -EIO;
}
+ spin_lock(&dev->lock);
while (list_empty(&dev->cpkt_req_q)) {
pr_debug("Requests list is empty. Wait.\n");
+ spin_unlock(&dev->lock);
ret = wait_event_interruptible(dev->read_wq,
!list_empty(&dev->cpkt_req_q));
if (ret < 0) {
@@ -1705,11 +1707,13 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
return -ERESTARTSYS;
}
pr_debug("Received request packet\n");
+ spin_lock(&dev->lock);
}
cpkt = list_first_entry(&dev->cpkt_req_q, struct ctrl_pkt,
list);
if (cpkt->len > count) {
+ spin_unlock(&dev->lock);
mbim_unlock(&dev->read_excl);
pr_err("cpkt size too big:%d > buf size:%d\n",
cpkt->len, count);
@@ -1719,6 +1723,7 @@ mbim_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
pr_debug("cpkt size:%d\n", cpkt->len);
list_del(&cpkt->list);
+ spin_unlock(&dev->lock);
mbim_unlock(&dev->read_excl);
ret = copy_to_user(buf, cpkt->buf, cpkt->len);
diff --git a/drivers/usb/gadget/f_mtp.c b/drivers/usb/gadget/f_mtp.c
index 8008cf35c43d..75968f54db14 100644
--- a/drivers/usb/gadget/f_mtp.c
+++ b/drivers/usb/gadget/f_mtp.c
@@ -899,7 +899,10 @@ static void receive_file_work(struct work_struct *data)
dev->rx_done || dev->state != STATE_BUSY);
if (dev->state == STATE_CANCELED
|| dev->state == STATE_OFFLINE) {
- r = -ECANCELED;
+ if (dev->state == STATE_OFFLINE)
+ r = -EIO;
+ else
+ r = -ECANCELED;
if (!dev->rx_done)
usb_ep_dequeue(dev->ep_out, read_req);
break;
diff --git a/drivers/usb/gadget/f_qc_ecm.c b/drivers/usb/gadget/f_qc_ecm.c
index 4169a2ada57e..bd6a48b15a7a 100644
--- a/drivers/usb/gadget/f_qc_ecm.c
+++ b/drivers/usb/gadget/f_qc_ecm.c
@@ -395,9 +395,9 @@ static int ecm_qc_bam_connect(struct f_ecm_qc *dev)
/* currently we use the first connection */
src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
- USB_TO_PEER_PERIPHERAL, 0);
+ USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, 0);
dst_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
- PEER_PERIPHERAL_TO_USB, 0);
+ PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);
if (src_connection_idx < 0 || dst_connection_idx < 0) {
pr_err("usb_bam_get_connection_idx failed\n");
return ret;
@@ -662,6 +662,12 @@ static void ecm_qc_disable(struct usb_function *f)
gether_qc_disconnect_name(&ecm->port, "ecm0");
}
+ if (ecm->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
+ gadget_is_dwc3(cdev->gadget)) {
+ msm_ep_unconfig(ecm->port.out_ep);
+ msm_ep_unconfig(ecm->port.in_ep);
+ }
+
if (ecm->notify->driver_data) {
usb_ep_disable(ecm->notify);
ecm->notify->driver_data = NULL;
diff --git a/drivers/usb/gadget/f_qc_rndis.c b/drivers/usb/gadget/f_qc_rndis.c
index 15a6619a0a18..72aa1a686d43 100644
--- a/drivers/usb/gadget/f_qc_rndis.c
+++ b/drivers/usb/gadget/f_qc_rndis.c
@@ -440,9 +440,9 @@ static int rndis_qc_bam_connect(struct f_rndis_qc *dev)
/* currently we use the first connection */
src_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
- USB_TO_PEER_PERIPHERAL, 0);
+ USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE, 0);
dst_connection_idx = usb_bam_get_connection_idx(gadget->name, peer_bam,
- PEER_PERIPHERAL_TO_USB, 0);
+ PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);
if (src_connection_idx < 0 || dst_connection_idx < 0) {
pr_err("%s: usb_bam_get_connection_idx failed\n", __func__);
return ret;
@@ -781,6 +781,7 @@ fail:
static void rndis_qc_disable(struct usb_function *f)
{
struct f_rndis_qc *rndis = func_to_rndis_qc(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
if (!rndis->notify->driver_data)
return;
@@ -794,6 +795,11 @@ static void rndis_qc_disable(struct usb_function *f)
else
rndis_ipa_supported = false;
+ if (rndis->xport == USB_GADGET_XPORT_BAM2BAM_IPA &&
+ gadget_is_dwc3(cdev->gadget)) {
+ msm_ep_unconfig(rndis->port.out_ep);
+ msm_ep_unconfig(rndis->port.in_ep);
+ }
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
}
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 5eabe21a79b4..ef551b7bc714 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -388,7 +388,7 @@ static int rmnet_gport_setup(void)
return 0;
}
-static int gport_rmnet_connect(struct f_rmnet *dev)
+static int gport_rmnet_connect(struct f_rmnet *dev, unsigned intf)
{
int ret;
unsigned port_num;
@@ -413,7 +413,7 @@ static int gport_rmnet_connect(struct f_rmnet *dev)
}
break;
case USB_GADGET_XPORT_QTI:
- ret = gqti_ctrl_connect(&dev->port, port_num);
+ ret = gqti_ctrl_connect(&dev->port, port_num, intf);
if (ret) {
pr_err("%s: gqti_ctrl_connect failed: err:%d\n",
__func__, ret);
@@ -449,9 +449,11 @@ static int gport_rmnet_connect(struct f_rmnet *dev)
switch (dxport) {
case USB_GADGET_XPORT_BAM2BAM:
src_connection_idx = usb_bam_get_connection_idx(gadget->name,
- A2_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
+ A2_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ port_num);
dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
- A2_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
+ A2_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ port_num);
if (dst_connection_idx < 0 || src_connection_idx < 0) {
pr_err("%s: usb_bam_get_connection_idx failed\n",
__func__);
@@ -470,9 +472,11 @@ static int gport_rmnet_connect(struct f_rmnet *dev)
break;
case USB_GADGET_XPORT_BAM2BAM_IPA:
src_connection_idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, USB_TO_PEER_PERIPHERAL, port_num);
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL, USB_BAM_DEVICE,
+ port_num);
dst_connection_idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, PEER_PERIPHERAL_TO_USB, port_num);
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE,
+ port_num);
if (dst_connection_idx < 0 || src_connection_idx < 0) {
pr_err("%s: usb_bam_get_connection_idx failed\n",
__func__);
@@ -696,6 +700,8 @@ static void frmnet_resume(struct usb_function *f)
static void frmnet_disable(struct usb_function *f)
{
struct f_rmnet *dev = func_to_rmnet(f);
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
+ struct usb_composite_dev *cdev = dev->cdev;
pr_debug("%s: port#%d\n", __func__, dev->port_num);
@@ -706,6 +712,11 @@ static void frmnet_disable(struct usb_function *f)
frmnet_purge_responses(dev);
+ if (dxport == USB_GADGET_XPORT_BAM2BAM_IPA &&
+ gadget_is_dwc3(cdev->gadget)) {
+ msm_ep_unconfig(dev->port.out);
+ msm_ep_unconfig(dev->port.in);
+ }
gport_rmnet_disconnect(dev);
}
@@ -748,7 +759,7 @@ frmnet_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
dev->port.out->desc = NULL;
return -EINVAL;
}
- ret = gport_rmnet_connect(dev);
+ ret = gport_rmnet_connect(dev, intf);
}
if (dxport == USB_GADGET_XPORT_BAM2BAM_IPA &&
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index fa589331e0f9..68cafd520cb3 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -25,6 +25,7 @@
#include <mach/usb_gadget_xport.h>
#include <linux/usb/msm_hsusb.h>
+#include <linux/usb/rmnet_ctrl_qti.h>
#include <mach/usb_bam.h>
#include "u_rmnet.h"
@@ -862,7 +863,8 @@ static void gbam2bam_connect_work(struct work_struct *w)
struct u_bam_data_connect_info bam_info;
idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, USB_TO_PEER_PERIPHERAL, 0);
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
+ USB_BAM_DEVICE, 0);
if (idx < 0) {
pr_err("%s: get_connection_idx failed\n",
__func__);
@@ -894,7 +896,8 @@ static void gbam2bam_connect_work(struct work_struct *w)
struct u_bam_data_connect_info bam_info;
idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, PEER_PERIPHERAL_TO_USB, 0);
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
+ USB_BAM_DEVICE, 0);
if (idx < 0) {
pr_err("%s: get_connection_idx failed\n",
__func__);
@@ -912,6 +915,10 @@ static void gbam2bam_connect_work(struct work_struct *w)
bam_info.usb_bam_pipe_idx);
}
+ gqti_ctrl_update_ipa_pipes(port->port_usb, port->port_num,
+ d->ipa_params.ipa_prod_ep_idx ,
+ d->ipa_params.ipa_cons_ep_idx);
+
connect_params.ipa_usb_pipe_hdl = d->ipa_params.prod_clnt_hdl;
connect_params.usb_ipa_pipe_hdl = d->ipa_params.cons_clnt_hdl;
connect_params.tethering_mode = TETH_TETHERING_MODE_RMNET;
diff --git a/drivers/usb/gadget/u_bam_data.c b/drivers/usb/gadget/u_bam_data.c
index 45bf145f9a9c..ce4a167b50a2 100644
--- a/drivers/usb/gadget/u_bam_data.c
+++ b/drivers/usb/gadget/u_bam_data.c
@@ -271,7 +271,8 @@ static void bam2bam_data_connect_work(struct work_struct *w)
struct usb_bam_data_connect_info bam_info;
idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, USB_TO_PEER_PERIPHERAL, 0);
+ IPA_P_BAM, USB_TO_PEER_PERIPHERAL,
+ USB_BAM_DEVICE, 0);
if (idx < 0) {
pr_err("%s: get_connection_idx failed\n",
__func__);
@@ -310,7 +311,8 @@ static void bam2bam_data_connect_work(struct work_struct *w)
struct usb_bam_data_connect_info bam_info;
idx = usb_bam_get_connection_idx(gadget->name,
- IPA_P_BAM, PEER_PERIPHERAL_TO_USB, 0);
+ IPA_P_BAM, PEER_PERIPHERAL_TO_USB,
+ USB_BAM_DEVICE, 0);
if (idx < 0) {
pr_err("%s: get_connection_idx failed\n",
__func__);
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 65d4aea4fe66..67095d97eab2 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -866,9 +866,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
req->length = length;
- /* throttle highspeed IRQ rate back slightly */
+ /* throttle high/super speed IRQ rate back slightly */
if (gadget_is_dualspeed(dev->gadget) &&
- (dev->gadget->speed == USB_SPEED_HIGH)) {
+ (dev->gadget->speed == USB_SPEED_HIGH ||
+ dev->gadget->speed == USB_SPEED_SUPER)) {
dev->tx_qlen++;
if (dev->tx_qlen == (qmult/2)) {
req->no_interrupt = 0;
diff --git a/drivers/usb/gadget/u_qdss.c b/drivers/usb/gadget/u_qdss.c
index c6952d59c466..dea25ce8a78e 100644
--- a/drivers/usb/gadget/u_qdss.c
+++ b/drivers/usb/gadget/u_qdss.c
@@ -69,7 +69,7 @@ static int set_qdss_data_connection(struct usb_gadget *gadget,
/* There is only one qdss pipe, so the pipe number can be set to 0 */
idx = usb_bam_get_connection_idx(gadget->name, QDSS_P_BAM,
- PEER_PERIPHERAL_TO_USB, 0);
+ PEER_PERIPHERAL_TO_USB, USB_BAM_DEVICE, 0);
if (idx < 0) {
pr_err("%s: usb_bam_get_connection_idx failed\n", __func__);
return idx;
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 549bba30da2b..86fa4e5761bc 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -69,7 +69,9 @@ int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
int gsmd_ctrl_setup(enum ctrl_client client_num, unsigned int count,
u8 *first_port_idx);
-int gqti_ctrl_connect(struct grmnet *gr, u8 port_num);
+int gqti_ctrl_connect(struct grmnet *gr, u8 port_num, unsigned intf);
void gqti_ctrl_disconnect(struct grmnet *gr, u8 port_num);
+void gqti_ctrl_update_ipa_pipes(struct grmnet *gr, u8 port_num,
+ u32 ipa_prod, u32 ipa_cons);
#endif /* __U_RMNET_H*/
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_qti.c b/drivers/usb/gadget/u_rmnet_ctrl_qti.c
index 7eb49722f5e1..f1ea710fee34 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_qti.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_qti.c
@@ -23,6 +23,9 @@ struct rmnet_ctrl_qti_port {
bool is_open;
int index;
+ unsigned intf;
+ int ipa_prod_idx;
+ int ipa_cons_idx;
atomic_t connected;
atomic_t line_state;
@@ -152,7 +155,7 @@ gqti_ctrl_notify_modem(void *gptr, u8 portno, int val)
rmnet_ctrl_queue_notify(port);
}
-int gqti_ctrl_connect(struct grmnet *gr, u8 port_num)
+int gqti_ctrl_connect(struct grmnet *gr, u8 port_num, unsigned intf)
{
struct rmnet_ctrl_qti_port *port;
unsigned long flags;
@@ -177,6 +180,7 @@ int gqti_ctrl_connect(struct grmnet *gr, u8 port_num)
spin_lock_irqsave(&port->lock, flags);
port->port_usb = gr;
+ port->intf = intf;
gr->send_encap_cmd = grmnet_ctrl_qti_send_cpkt_tomodem;
gr->notify_modem = gqti_ctrl_notify_modem;
spin_unlock_irqrestore(&port->lock, flags);
@@ -233,6 +237,24 @@ void gqti_ctrl_disconnect(struct grmnet *gr, u8 port_num)
rmnet_ctrl_queue_notify(port);
}
+void gqti_ctrl_update_ipa_pipes(struct grmnet *gr, u8 port_num, u32 ipa_prod,
+ u32 ipa_cons)
+{
+ struct rmnet_ctrl_qti_port *port;
+
+ if (port_num >= NR_QTI_PORTS) {
+ pr_err("%s: Invalid QTI port %d\n", __func__, port_num);
+ return;
+ }
+
+ port = ctrl_port[port_num];
+
+ port->ipa_prod_idx = ipa_prod;
+ port->ipa_cons_idx = ipa_cons;
+
+}
+
+
static int rmnet_ctrl_open(struct inode *ip, struct file *fp)
{
unsigned long flags;
@@ -423,6 +445,7 @@ rmnet_ctrl_write(struct file *fp, const char __user *buf, size_t count,
static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
{
struct rmnet_ctrl_qti_port *port = fp->private_data;
+ struct ep_info info;
int val, ret = 0;
pr_debug("%s: Received command %d", __func__, cmd);
@@ -441,6 +464,33 @@ static long rmnet_ctrl_ioctl(struct file *fp, unsigned cmd, unsigned long arg)
pr_debug("%s: Sent line_state: %d", __func__,
atomic_read(&port->line_state));
break;
+ case FRMNET_CTRL_EP_LOOKUP:
+ val = atomic_read(&port->connected);
+ if (!val) {
+ pr_err("EP_LOOKUP failed - not connected");
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (port->ipa_prod_idx == -1 || port->ipa_cons_idx == -1) {
+ pr_err("EP_LOOKUP failed - ipa pipes were not updated");
+ ret = -EAGAIN;
+ break;
+
+ }
+
+ info.ph_ep_info.ep_type = DATA_EP_TYPE_HSUSB;
+ info.ph_ep_info.peripheral_iface_id = port->intf;
+ info.ipa_ep_pair.cons_pipe_num = port->ipa_cons_idx;
+ info.ipa_ep_pair.prod_pipe_num = port->ipa_prod_idx;
+
+ ret = copy_to_user((void __user *)arg, &info,
+ sizeof(info));
+ if (ret) {
+ pr_err("copying to user space failed");
+ ret = -EFAULT;
+ }
+ break;
default:
pr_err("wrong parameter");
ret = -EINVAL;
@@ -527,6 +577,8 @@ static int __init gqti_ctrl_init(void)
ctrl_port[i] = port;
port->index = i;
+ port->ipa_prod_idx = -1;
+ port->ipa_cons_idx = -1;
ret = misc_register(&rmnet_device[i]);
if (ret) {
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
index 837bedbffd98..f6c1e411a3c5 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/u_serial.c
@@ -705,18 +705,20 @@ static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
}
static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
- int num, int size, void (*fn)(struct usb_ep *, struct usb_request *),
+ int queue_size, int req_size,
+ void (*fn)(struct usb_ep *, struct usb_request *),
int *allocated)
{
int i;
struct usb_request *req;
+ int n = allocated ? queue_size - *allocated : queue_size;
/* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
* do quite that many this time, don't fail ... we just won't
* be as speedy as we might otherwise be.
*/
- for (i = 0; i < num; i++) {
- req = gs_alloc_req(ep, size, GFP_ATOMIC);
+ for (i = 0; i < n; i++) {
+ req = gs_alloc_req(ep, req_size, GFP_ATOMIC);
if (!req)
return list_empty(head) ? -ENOMEM : 0;
req->complete = fn;
@@ -963,22 +965,6 @@ static void gs_close(struct tty_struct *tty, struct file *file)
port->port_num, tty, file);
wake_up(&port->port.close_wait);
-
- /*
- * Freeing the previously queued requests as they are
- * allocated again as a part of gs_open()
- */
- if (port->port_usb) {
- spin_unlock_irq(&port->port_lock);
- usb_ep_fifo_flush(gser->out);
- usb_ep_fifo_flush(gser->in);
- spin_lock_irq(&port->port_lock);
- gs_free_requests(gser->out, &port->read_queue, NULL);
- gs_free_requests(gser->out, &port->read_pool, NULL);
- gs_free_requests(gser->in, &port->write_pool, NULL);
- }
- port->read_allocated = port->read_started =
- port->write_allocated = port->write_started = 0;
exit:
spin_unlock_irq(&port->port_lock);
}
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index a88a34ee0f60..8e0d8f887c5d 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -881,19 +881,14 @@ static int ehci_hub_control (
) {
struct ehci_hcd *ehci = hcd_to_ehci (hcd);
int ports = HCS_N_PORTS (ehci->hcs_params);
- u32 __iomem *status_reg;
- u32 __iomem *hostpc_reg;
+ u32 __iomem *status_reg = &ehci->regs->port_status[
+ (wIndex & 0xff) - 1];
+ u32 __iomem *hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];
u32 temp, temp1, status;
unsigned long flags;
int retval = 0;
unsigned selector;
- if ((wIndex & 0xff) == 0x0)
- return -EINVAL;
-
- status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
- hostpc_reg = &ehci->regs->hostpc[(wIndex & 0xff) - 1];
-
/*
* FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR.
* HCS_INDICATOR may say we can change LEDs to off/amber/green.
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index dab70e89586f..269fc80f70f7 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -891,7 +891,7 @@ static int msm_hsic_resume(struct msm_hsic_hcd *mehci)
if (pdata->consider_ipa_handshake) {
dev_dbg(mehci->dev, "%s:Wait for producer resource\n",
__func__);
- msm_bam_wait_for_hsic_prod_granted();
+ msm_bam_wait_for_hsic_host_prod_granted();
dev_dbg(mehci->dev, "%s:Producer resource obtained\n",
__func__);
}
@@ -986,7 +986,7 @@ skip_phy_resume:
if (pdata->consider_ipa_handshake) {
dev_dbg(mehci->dev, "%s:Notify usb bam on resume complete\n",
__func__);
- msm_bam_hsic_notify_on_resume();
+ msm_bam_hsic_host_notify_on_resume();
}
return 0;
@@ -1523,7 +1523,7 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
/*core_clk is required for LINK protocol engine
*clock rate appropriately set by target specific clock driver */
- mehci->core_clk = clk_get(mehci->dev, "core_clk");
+ mehci->core_clk = devm_clk_get(mehci->dev, "core_clk");
if (IS_ERR(mehci->core_clk)) {
ret = PTR_ERR(mehci->core_clk);
mehci->core_clk = NULL;
@@ -1532,48 +1532,35 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
return ret;
}
- /* alt_core_clk is for LINK to be used during PHY RESET in
- * targets on which link does NOT use asynchronous reset methodology.
- * clock rate appropriately set by target specific clock driver */
- mehci->alt_core_clk = clk_get(mehci->dev, "alt_core_clk");
- if (IS_ERR(mehci->alt_core_clk)) {
- ret = PTR_ERR(mehci->alt_core_clk);
- mehci->alt_core_clk = NULL;
- if (ret != -EPROBE_DEFER)
- dev_dbg(mehci->dev, "failed to get alt_core_clk\n");
- else
- goto put_core_clk;
- }
-
/* phy_clk is required for HSIC PHY operation
* clock rate appropriately set by target specific clock driver */
- mehci->phy_clk = clk_get(mehci->dev, "phy_clk");
+ mehci->phy_clk = devm_clk_get(mehci->dev, "phy_clk");
if (IS_ERR(mehci->phy_clk)) {
ret = PTR_ERR(mehci->phy_clk);
mehci->phy_clk = NULL;
if (ret != -EPROBE_DEFER)
dev_err(mehci->dev, "failed to get phy_clk\n");
- goto put_alt_core_clk;
+ return ret;
}
/* 10MHz cal_clk is required for calibration of I/O pads */
- mehci->cal_clk = clk_get(mehci->dev, "cal_clk");
+ mehci->cal_clk = devm_clk_get(mehci->dev, "cal_clk");
if (IS_ERR(mehci->cal_clk)) {
ret = PTR_ERR(mehci->cal_clk);
mehci->cal_clk = NULL;
if (ret != -EPROBE_DEFER)
dev_err(mehci->dev, "failed to get cal_clk\n");
- goto put_phy_clk;
+ return ret;
}
/* ahb_clk is required for data transfers */
- mehci->ahb_clk = clk_get(mehci->dev, "iface_clk");
+ mehci->ahb_clk = devm_clk_get(mehci->dev, "iface_clk");
if (IS_ERR(mehci->ahb_clk)) {
ret = PTR_ERR(mehci->ahb_clk);
mehci->ahb_clk = NULL;
if (ret != -EPROBE_DEFER)
dev_err(mehci->dev, "failed to get iface_clk\n");
- goto put_cal_clk;
+ return ret;
}
/*
@@ -1581,10 +1568,19 @@ static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
* This clock is not compulsory and is defined in clock lookup
* only for targets that need to use the inactivity timer feature.
*/
- mehci->inactivity_clk = clk_get(mehci->dev, "inactivity_clk");
+ mehci->inactivity_clk = devm_clk_get(mehci->dev, "inactivity_clk");
if (IS_ERR(mehci->inactivity_clk))
dev_dbg(mehci->dev, "failed to get inactivity_clk\n");
+ /*
+ * alt_core_clk is for LINK to be used during PHY RESET in
+ * targets on which link does NOT use asynchronous reset methodology.
+ * clock rate appropriately set by target specific clock driver
+ */
+ mehci->alt_core_clk = devm_clk_get(mehci->dev, "alt_core_clk");
+ if (IS_ERR(mehci->alt_core_clk))
+ dev_dbg(mehci->dev, "failed to get alt_core_clk\n");
+
clk_prepare_enable(mehci->core_clk);
clk_prepare_enable(mehci->phy_clk);
clk_prepare_enable(mehci->cal_clk);
@@ -1603,21 +1599,10 @@ put_clocks:
if (!IS_ERR(mehci->inactivity_clk))
clk_disable_unprepare(mehci->inactivity_clk);
}
- if (!IS_ERR(mehci->inactivity_clk))
- clk_put(mehci->inactivity_clk);
- clk_put(mehci->ahb_clk);
-put_cal_clk:
- clk_put(mehci->cal_clk);
-put_phy_clk:
- clk_put(mehci->phy_clk);
-put_alt_core_clk:
- if (mehci->alt_core_clk)
- clk_put(mehci->alt_core_clk);
-put_core_clk:
- clk_put(mehci->core_clk);
- return ret;
+ return 0;
}
+
static irqreturn_t hsic_peripheral_status_change(int irq, void *dev_id)
{
struct msm_hsic_hcd *mehci = dev_id;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 655cf342400a..d63b5ba0d80a 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -150,7 +150,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
pm_runtime_enable(&pdev->dev);
- /* FIXME: need to call usb_add_hcd() here? */
+ msm_bam_set_usb_host_dev(&pdev->dev);
return 0;
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index a875b04d6b98..c61de84b28f3 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -172,11 +172,11 @@ static int msm_ehci_init_vddcx(struct msm_hcd *mhcd, int init)
if (mhcd->dev->of_node) {
of_get_property(mhcd->dev->of_node,
- "qti,vdd-voltage-level",
+ "qcom,vdd-voltage-level",
&len);
if (len == sizeof(tmp)) {
of_property_read_u32_array(mhcd->dev->of_node,
- "qti,vdd-voltage-level",
+ "qcom,vdd-voltage-level",
tmp, len/sizeof(*tmp));
hsusb_vdd_val[mhcd->vdd_type][VDD_MIN_NONE] = tmp[0];
hsusb_vdd_val[mhcd->vdd_type][VDD_MIN_P50] = tmp[1];
@@ -1005,6 +1005,10 @@ static int msm_ehci_reset(struct usb_hcd *hcd)
writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16),
USB_PHY_CTRL2);
+ /* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */
+ writel_relaxed(readl_relaxed(USB_GENCONFIG2) & ~(1<<19),
+ USB_GENCONFIG2);
+
return 0;
}
@@ -1311,15 +1315,15 @@ struct msm_usb_host_platform_data *ehci_msm2_dt_to_pdata(
}
pdata->use_sec_phy = of_property_read_bool(node,
- "qti,usb2-enable-hsphy2");
- of_property_read_u32(node, "qti,usb2-power-budget",
+ "qcom,usb2-enable-hsphy2");
+ of_property_read_u32(node, "qcom,usb2-power-budget",
&pdata->power_budget);
pdata->no_selective_suspend = of_property_read_bool(node,
- "qti,no-selective-suspend");
- pdata->resume_gpio = of_get_named_gpio(node, "qti,resume-gpio", 0);
+ "qcom,no-selective-suspend");
+ pdata->resume_gpio = of_get_named_gpio(node, "qcom,resume-gpio", 0);
pdata->ext_hub_reset_gpio = of_get_named_gpio(node,
- "qti,ext-hub-reset-gpio", 0);
+ "qcom,ext-hub-reset-gpio", 0);
return pdata;
}
@@ -1746,7 +1750,7 @@ static const struct dev_pm_ops ehci_msm2_dev_pm_ops = {
#endif
static const struct of_device_id ehci_msm2_dt_match[] = {
- { .compatible = "qti,ehci-host",
+ { .compatible = "qcom,ehci-host",
},
{}
};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index ae18ad64aa01..ecf0a4c87569 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -683,7 +683,7 @@ qh_urb_transaction (
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
token ^= QTD_TOGGLE;
- if (likely(sg && (this_sg_len <= 0))) {
+ if (likely(this_sg_len <= 0)) {
if (--i <= 0 || len <= 0)
break;
sg = sg_next(sg);
diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c
index 5b9f55eb3d00..2f8c5362a77e 100644
--- a/drivers/usb/host/xhci-dbg.c
+++ b/drivers/usb/host/xhci-dbg.c
@@ -613,11 +613,11 @@ static char *get_timestamp(char *tbuf)
return tbuf;
}
-static int check_log_mask(struct dbg_data *d, int ep_addr)
+static int check_log_mask(struct dbg_data *d, int ep_addr, struct urb *urb)
{
int dir, num;
- dir = ep_addr & USB_DIR_IN ? USB_DIR_IN : USB_DIR_OUT;
+ dir = usb_urb_dir_in(urb) ? USB_DIR_IN : USB_DIR_OUT;
num = ep_addr & ~USB_DIR_IN;
num = 1 << num;
@@ -629,9 +629,9 @@ static int check_log_mask(struct dbg_data *d, int ep_addr)
return 0;
}
-static char *get_hex_data(char *dbuf, struct urb *urb, int event, int status)
+static char *
+get_hex_data(char *dbuf, struct urb *urb, int event, int status, size_t max_len)
{
- int ep_addr = urb->ep->desc.bEndpointAddress;
char *ubuf = urb->transfer_buffer;
size_t len =
event ? urb->actual_length : urb->transfer_buffer_length;
@@ -640,11 +640,10 @@ static char *get_hex_data(char *dbuf, struct urb *urb, int event, int status)
status = 0;
/*Only dump ep in completions and epout submissions*/
- if (len && !status &&
- (((ep_addr & USB_DIR_IN) && event) ||
- (!(ep_addr & USB_DIR_IN) && !event))) {
- if (len >= 32)
- len = 32;
+ if (len && !status && ((usb_urb_dir_in(urb) && event) ||
+ (usb_urb_dir_in(urb) && !event))) {
+ if (len >= max_len)
+ len = max_len;
hex_dump_to_buffer(ubuf, len, 32, 4, dbuf, HEX_DUMP_LEN, 0);
} else {
dbuf = "";
@@ -675,7 +674,7 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event,
}
ep_addr = urb->ep->desc.bEndpointAddress;
- if (!check_log_mask(d, ep_addr))
+ if (!check_log_mask(d, ep_addr, urb))
return;
if ((ep_addr & 0x0f) == 0x0) {
@@ -684,9 +683,9 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event,
write_lock_irqsave(&d->ctrl_lck, flags);
scnprintf(d->ctrl_buf[d->ctrl_idx],
DBG_MSG_LEN, "%s: [%s : %p]:[%s] "
- "%02x %02x %04x %04x %04x %u %d",
+ "%02x %02x %04x %04x %04x %u %d %s",
get_timestamp(tbuf), event, urb,
- (ep_addr & USB_DIR_IN) ? "in" : "out",
+ usb_urb_dir_in(urb) ? "in" : "out",
urb->setup_packet[0], urb->setup_packet[1],
(urb->setup_packet[3] << 8) |
urb->setup_packet[2],
@@ -694,17 +693,21 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event,
urb->setup_packet[4],
(urb->setup_packet[7] << 8) |
urb->setup_packet[6],
- urb->transfer_buffer_length, extra);
+ urb->transfer_buffer_length, extra,
+ d->log_payload ? get_hex_data(dbuf, urb,
+ xhci_str_to_event(event), extra, 16) : "");
dbg_inc(&d->ctrl_idx);
write_unlock_irqrestore(&d->ctrl_lck, flags);
} else {
write_lock_irqsave(&d->ctrl_lck, flags);
scnprintf(d->ctrl_buf[d->ctrl_idx],
- DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d",
+ DBG_MSG_LEN, "%s: [%s : %p]:[%s] %u %d %s",
get_timestamp(tbuf), event, urb,
- (ep_addr & USB_DIR_IN) ? "in" : "out",
- urb->actual_length, extra);
+ usb_urb_dir_in(urb) ? "in" : "out",
+ urb->actual_length, extra,
+ d->log_payload ? get_hex_data(dbuf, urb,
+ xhci_str_to_event(event), extra, 16) : "");
dbg_inc(&d->ctrl_idx);
write_unlock_irqrestore(&d->ctrl_lck, flags);
@@ -714,11 +717,11 @@ xhci_dbg_log_event(struct dbg_data *d, struct urb *urb, char *event,
scnprintf(d->data_buf[d->data_idx], DBG_MSG_LEN,
"%s: [%s : %p]:ep%d[%s] %u %d %s",
get_timestamp(tbuf), event, urb, ep_addr & 0x0f,
- (ep_addr & USB_DIR_IN) ? "in" : "out",
+ usb_urb_dir_in(urb) ? "in" : "out",
xhci_str_to_event(event) ? urb->actual_length :
urb->transfer_buffer_length, extra,
d->log_payload ? get_hex_data(dbuf, urb,
- xhci_str_to_event(event), extra) : "");
+ xhci_str_to_event(event), extra, 32) : "");
dbg_inc(&d->data_idx);
write_unlock_irqrestore(&d->data_lck, flags);
diff --git a/drivers/usb/host/xhci-msm-hsic.c b/drivers/usb/host/xhci-msm-hsic.c
index 07f12f0abdbb..ef6e88b6f8f5 100644
--- a/drivers/usb/host/xhci-msm-hsic.c
+++ b/drivers/usb/host/xhci-msm-hsic.c
@@ -554,7 +554,9 @@ static int mxhci_hsic_bus_suspend(struct usb_hcd *hcd)
/* don't miss connect bus state from peripheral for USB 2.0 root hub */
if (usb_hcd_is_primary_hcd(hcd) &&
!(readl_relaxed(MSM_HSIC_PORTSC) & PORT_PE)) {
- dev_err(mxhci->dev, "%s: port is not enabled; skip suspend\n",
+ xhci_dbg_log_event(&dbg_hsic, NULL,
+ "port is not enabled; skip suspend", 0);
+ dev_dbg(mxhci->dev, "%s: port is not enabled; skip suspend\n",
__func__);
return -EAGAIN;
}
@@ -599,6 +601,10 @@ static int mxhci_hsic_suspend(struct mxhci_hsic_hcd *mxhci)
init_completion(&mxhci->phy_in_lpm);
+ /* Don't poll the roothubs after bus suspend. */
+ clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ del_timer_sync(&hcd->rh_timer);
+
clk_disable_unprepare(mxhci->core_clk);
clk_disable_unprepare(mxhci->utmi_clk);
clk_disable_unprepare(mxhci->hsic_clk);
@@ -684,6 +690,10 @@ static int mxhci_hsic_resume(struct mxhci_hsic_hcd *mxhci)
clk_prepare_enable(mxhci->utmi_clk);
clk_prepare_enable(mxhci->core_clk);
+ /* Re-enable port polling. */
+ set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
+ usb_hcd_poll_rh_status(hcd);
+
if (mxhci->wakeup_irq)
usb_hcd_resume_root_hub(hcd);
@@ -943,7 +953,7 @@ static int mxhci_hsic_probe(struct platform_device *pdev)
reg |= CTRLREG_PLL_CTRL_SLEEP | CTRLREG_PLL_CTRL_SUSP;
writel_relaxed(reg, MSM_HSIC_CTRL_REG);
- if (of_property_read_bool(node, "qti,disable-hw-clk-gating")) {
+ if (of_property_read_bool(node, "qcom,disable-hw-clk-gating")) {
reg = readl_relaxed(MSM_HSIC_GCTL);
writel_relaxed((reg | GCTL_DSBLCLKGTNG), MSM_HSIC_GCTL);
}
diff --git a/drivers/usb/phy/phy-msm-hsusb.c b/drivers/usb/phy/phy-msm-hsusb.c
index 0359d8894e43..52e8fe3f60e9 100644
--- a/drivers/usb/phy/phy-msm-hsusb.c
+++ b/drivers/usb/phy/phy-msm-hsusb.c
@@ -519,21 +519,21 @@ static int msm_hsphy_probe(struct platform_device *pdev)
phy->tcsr);
}
- if (of_get_property(dev->of_node, "qti,primary-phy", NULL)) {
+ if (of_get_property(dev->of_node, "qcom,primary-phy", NULL)) {
dev_dbg(dev, "secondary HSPHY\n");
phy->phy.flags |= ENABLE_SECONDARY_PHY;
}
- ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level",
+ ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
(u32 *) phy->vdd_levels,
ARRAY_SIZE(phy->vdd_levels));
if (ret) {
- dev_err(dev, "error reading qti,vdd-voltage-level property\n");
+ dev_err(dev, "error reading qcom,vdd-voltage-level property\n");
goto err_ret;
}
phy->ext_vbus_id = of_property_read_bool(dev->of_node,
- "qti,ext-vbus-id");
+ "qcom,ext-vbus-id");
phy->phy.dev = dev;
phy->vdd = devm_regulator_get(dev, "vdd");
@@ -575,18 +575,18 @@ static int msm_hsphy_probe(struct platform_device *pdev)
goto disable_hs_vdd;
}
- if (of_property_read_u32(dev->of_node, "qti,hsphy-init",
+ if (of_property_read_u32(dev->of_node, "qcom,hsphy-init",
&phy->hsphy_init_seq))
dev_dbg(dev, "unable to read hsphy init seq\n");
else if (!phy->hsphy_init_seq)
dev_warn(dev, "hsphy init seq cannot be 0. Using POR value\n");
phy->set_pllbtune = of_property_read_bool(dev->of_node,
- "qti,set-pllbtune");
+ "qcom,set-pllbtune");
platform_set_drvdata(pdev, phy);
- if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override"))
+ if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE;
phy->phy.init = msm_hsphy_init;
@@ -630,7 +630,7 @@ static int msm_hsphy_remove(struct platform_device *pdev)
static const struct of_device_id msm_usb_id_table[] = {
{
- .compatible = "qti,usb-hsphy",
+ .compatible = "qcom,usb-hsphy",
},
{ },
};
diff --git a/drivers/usb/phy/phy-msm-ssusb-qmp.c b/drivers/usb/phy/phy-msm-ssusb-qmp.c
index b80481566e4f..30cc23385dfe 100644
--- a/drivers/usb/phy/phy-msm-ssusb-qmp.c
+++ b/drivers/usb/phy/phy-msm-ssusb-qmp.c
@@ -392,11 +392,11 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
return -ENODEV;
}
- ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level",
+ ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
(u32 *) phy->vdd_levels,
ARRAY_SIZE(phy->vdd_levels));
if (ret) {
- dev_err(dev, "error reading qti,vdd-voltage-level property\n");
+ dev_err(dev, "error reading qcom,vdd-voltage-level property\n");
return ret;
}
@@ -432,7 +432,7 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
- if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override"))
+ if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE;
phy->phy.dev = dev;
@@ -485,7 +485,7 @@ static int msm_ssphy_qmp_remove(struct platform_device *pdev)
static const struct of_device_id msm_usb_id_table[] = {
{
- .compatible = "qti,usb-ssphy-qmp",
+ .compatible = "qcom,usb-ssphy-qmp",
},
{ },
};
diff --git a/drivers/usb/phy/phy-msm-ssusb.c b/drivers/usb/phy/phy-msm-ssusb.c
index feea5e0dcd8e..0259da87b5db 100644
--- a/drivers/usb/phy/phy-msm-ssusb.c
+++ b/drivers/usb/phy/phy-msm-ssusb.c
@@ -16,6 +16,8 @@
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
@@ -53,6 +55,8 @@ MODULE_PARM_DESC(ss_phy_override_deemphasis, "Override SSPHY demphasis value");
struct msm_ssphy {
struct usb_phy phy;
void __iomem *base;
+ struct clk *com_reset_clk; /* PHY common block reset */
+ struct clk *reset_clk; /* SS PHY reset */
struct regulator *vdd;
struct regulator *vdda18;
bool suspended;
@@ -214,19 +218,24 @@ static int msm_ssphy_init(struct usb_phy *uphy)
/* read initial value */
val = readl_relaxed(phy->base + SS_PHY_CTRL_REG);
+ /* Use clk reset, if available; otherwise use SS_PHY_RESET bit */
+ if (phy->com_reset_clk) {
+ clk_reset(phy->com_reset_clk, CLK_RESET_ASSERT);
+ clk_reset(phy->reset_clk, CLK_RESET_ASSERT);
+ udelay(10); /* 10us required before de-asserting */
+ clk_reset(phy->com_reset_clk, CLK_RESET_DEASSERT);
+ clk_reset(phy->reset_clk, CLK_RESET_DEASSERT);
+ } else {
+ writel_relaxed(val | SS_PHY_RESET, phy->base + SS_PHY_CTRL_REG);
+ udelay(10); /* 10us required before de-asserting */
+ writel_relaxed(val, phy->base + SS_PHY_CTRL_REG);
+ }
+
/* Use ref_clk from pads and set its parameters */
val |= REF_USE_PAD;
writel_relaxed(val, phy->base + SS_PHY_CTRL_REG);
msleep(30);
- /* Assert SSPHY reset */
- writel_relaxed(val | SS_PHY_RESET, phy->base + SS_PHY_CTRL_REG);
- usleep_range(2000, 2200);
-
- /* De-assert SSPHY reset - power and ref_clock must be ON */
- writel_relaxed(val, phy->base + SS_PHY_CTRL_REG);
- usleep_range(2000, 2200);
-
/* Ref clock must be stable now, enable ref clock for HS mode */
val |= LANE0_PWR_PRESENT | REF_SS_PHY_EN;
writel_relaxed(val, phy->base + SS_PHY_CTRL_REG);
@@ -332,6 +341,13 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend)
/* Set TEST_POWERDOWN (enables PHY retention) */
msm_usb_write_readback(base, SS_PHY_CTRL_REG, TEST_POWERDOWN,
TEST_POWERDOWN);
+ if (phy->com_reset_clk &&
+ !(phy->phy.flags & ENABLE_SECONDARY_PHY)) {
+ /* leave these asserted until resuming */
+ clk_reset(phy->com_reset_clk, CLK_RESET_ASSERT);
+ clk_reset(phy->reset_clk, CLK_RESET_ASSERT);
+ }
+
msm_ssusb_ldo_enable(phy, 0);
msm_ssusb_config_vdd(phy, 0);
} else {
@@ -343,9 +359,15 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend)
goto done;
}
- /* Assert SS PHY RESET */
- msm_usb_write_readback(base, SS_PHY_CTRL_REG, SS_PHY_RESET,
- SS_PHY_RESET);
+ if (phy->com_reset_clk) {
+ clk_reset(phy->com_reset_clk, CLK_RESET_DEASSERT);
+ clk_reset(phy->reset_clk, CLK_RESET_DEASSERT);
+ } else {
+ /* Assert SS PHY RESET */
+ msm_usb_write_readback(base, SS_PHY_CTRL_REG,
+ SS_PHY_RESET, SS_PHY_RESET);
+ }
+
/* Set REF_USE_PAD */
msm_usb_write_readback(base, SS_PHY_CTRL_REG, REF_USE_PAD,
REF_USE_PAD);
@@ -355,9 +377,11 @@ static int msm_ssphy_set_suspend(struct usb_phy *uphy, int suspend)
/* Clear TEST_POWERDOWN */
msm_usb_write_readback(base, SS_PHY_CTRL_REG, TEST_POWERDOWN,
0);
- /* 10usec delay required before de-asserting SS PHY RESET */
- udelay(10);
- msm_usb_write_readback(base, SS_PHY_CTRL_REG, SS_PHY_RESET, 0);
+ if (!phy->com_reset_clk) {
+ udelay(10); /* 10us required before de-asserting */
+ msm_usb_write_readback(base, SS_PHY_CTRL_REG,
+ SS_PHY_RESET, 0);
+ }
/*
* Reinitialize SSPHY parameters as SS_PHY RESET will reset
@@ -426,16 +450,28 @@ static int msm_ssphy_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (of_get_property(dev->of_node, "qti,primary-phy", NULL)) {
+ phy->com_reset_clk = devm_clk_get(dev, "com_reset_clk");
+ if (IS_ERR(phy->com_reset_clk)) {
+ dev_dbg(dev, "com_reset_clk unavailable\n");
+ phy->com_reset_clk = NULL;
+ }
+
+ phy->reset_clk = devm_clk_get(dev, "reset_clk");
+ if (IS_ERR(phy->reset_clk)) {
+ dev_dbg(dev, "reset_clk unavailable\n");
+ phy->reset_clk = NULL;
+ }
+
+ if (of_get_property(dev->of_node, "qcom,primary-phy", NULL)) {
dev_dbg(dev, "secondary HSPHY\n");
phy->phy.flags |= ENABLE_SECONDARY_PHY;
}
- ret = of_property_read_u32_array(dev->of_node, "qti,vdd-voltage-level",
+ ret = of_property_read_u32_array(dev->of_node, "qcom,vdd-voltage-level",
(u32 *) phy->vdd_levels,
ARRAY_SIZE(phy->vdd_levels));
if (ret) {
- dev_err(dev, "error reading qti,vdd-voltage-level property\n");
+ dev_err(dev, "error reading qcom,vdd-voltage-level property\n");
return ret;
}
@@ -472,10 +508,10 @@ static int msm_ssphy_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, phy);
- if (of_property_read_bool(dev->of_node, "qti,vbus-valid-override"))
+ if (of_property_read_bool(dev->of_node, "qcom,vbus-valid-override"))
phy->phy.flags |= PHY_VBUS_VALID_OVERRIDE;
- if (of_property_read_u32(dev->of_node, "qti,deemphasis-value",
+ if (of_property_read_u32(dev->of_node, "qcom,deemphasis-value",
&phy->deemphasis_val))
dev_dbg(dev, "unable to read ssphy deemphasis value\n");
@@ -521,7 +557,7 @@ static int msm_ssphy_remove(struct platform_device *pdev)
static const struct of_device_id msm_usb_id_table[] = {
{
- .compatible = "qti,usb-ssphy",
+ .compatible = "qcom,usb-ssphy",
},
{ },
};
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index e3b574c7ceb0..37ed14d4b9c0 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -30,6 +30,7 @@
#include <linux/of.h>
#include <linux/dma-mapping.h>
#include <linux/clk/msm-clk.h>
+#include <linux/irqchip/msm-mpm-irq.h>
#include <linux/usb.h>
#include <linux/usb/otg.h>
@@ -46,7 +47,6 @@
#include <linux/mhl_8334.h>
#include <mach/scm.h>
-#include <mach/mpm.h>
#include <mach/msm_xo.h>
#include <mach/msm_bus.h>
#include <mach/rpm-regulator.h>
@@ -900,7 +900,8 @@ static int msm_otg_suspend(struct msm_otg *motg)
if (atomic_read(&motg->in_lpm))
return 0;
- if (motg->pdata->delay_lpm_hndshk_on_disconnect && !msm_bam_lpm_ok())
+ if (motg->pdata->delay_lpm_hndshk_on_disconnect &&
+ !msm_bam_usb_lpm_ok())
return -EBUSY;
motg->ui_enabled = 0;
@@ -4323,7 +4324,7 @@ struct msm_otg_platform_data *msm_otg_dt_to_pdata(struct platform_device *pdev)
pdata->enable_ahb2ahb_bypass = of_property_read_bool(node,
"qcom,ahb-async-bridge-bypass");
pdata->disable_retention_with_vdd_min = of_property_read_bool(node,
- "qti,disable-retention-with-vdd-min");
+ "qcom,disable-retention-with-vdd-min");
res_gpio = of_get_named_gpio(node, "qcom,hsusb-otg-vddmin-gpio", 0);
if (res_gpio < 0)
@@ -4345,6 +4346,7 @@ static int __init msm_otg_probe(struct platform_device *pdev)
struct msm_otg *motg;
struct usb_phy *phy;
struct msm_otg_platform_data *pdata;
+ void __iomem *tcsr;
dev_info(&pdev->dev, "msm_otg probe\n");
@@ -4394,28 +4396,12 @@ static int __init msm_otg_probe(struct platform_device *pdev)
goto put_core_clk;
}
- /*
- * Targets on which link uses asynchronous reset methodology,
- * free running clock is not required during the reset.
- */
- motg->clk = clk_get(&pdev->dev, "alt_core_clk");
- if (IS_ERR(motg->clk)) {
- ret = PTR_ERR(motg->clk);
- motg->clk = NULL;
- if (ret != -EPROBE_DEFER)
- dev_dbg(&pdev->dev, "alt_core_clk is not present\n");
- else
- goto put_pclk;
- } else {
- clk_set_rate(motg->clk, 60000000);
- }
-
motg->xo_clk = clk_get(&pdev->dev, "xo");
if (IS_ERR(motg->xo_clk)) {
ret = PTR_ERR(motg->xo_clk);
motg->xo_clk = NULL;
if (ret == -EPROBE_DEFER)
- goto put_clk;
+ goto put_pclk;
}
/*
@@ -4439,6 +4425,18 @@ static int __init msm_otg_probe(struct platform_device *pdev)
}
}
+ /*
+ * Targets on which link uses asynchronous reset methodology,
+ * free running clock is not required during the reset.
+ */
+ motg->clk = clk_get(&pdev->dev, "alt_core_clk");
+ if (IS_ERR(motg->clk)) {
+ motg->clk = NULL;
+ dev_dbg(&pdev->dev, "alt_core_clk is not present\n");
+ } else {
+ clk_set_rate(motg->clk, 60000000);
+ }
+
if (pdev->dev.of_node) {
dev_dbg(&pdev->dev, "device tree enabled\n");
pdata = msm_otg_dt_to_pdata(pdev);
@@ -4516,6 +4514,27 @@ static int __init msm_otg_probe(struct platform_device *pdev)
}
dev_info(&pdev->dev, "OTG regs = %p\n", motg->regs);
+ if (pdata->enable_sec_phy) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_dbg(&pdev->dev, "missing TCSR memory resource\n");
+ } else {
+ tcsr = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
+ if (!tcsr) {
+ dev_dbg(&pdev->dev, "tcsr ioremap failed\n");
+ } else {
+ /* Enable USB2 on secondary HSPHY. */
+ writel_relaxed(0x1, tcsr);
+ /*
+ * Ensure that TCSR write is completed before
+ * USB registers initialization.
+ */
+ mb();
+ }
+ }
+ }
+
if (pdata->enable_sec_phy)
motg->usb_phy_ctrl_reg = USB_PHY_CTRL2;
else
@@ -4851,9 +4870,6 @@ put_sleep_clk:
put_xo_clk:
if (motg->xo_clk)
clk_put(motg->xo_clk);
-put_clk:
- if (motg->clk)
- clk_put(motg->clk);
put_pclk:
if (motg->pclk)
clk_put(motg->pclk);
diff --git a/drivers/video/msm/mdss/dsi_host_v2.c b/drivers/video/msm/mdss/dsi_host_v2.c
index 4d9e60fffe5b..1e09239fadcd 100644
--- a/drivers/video/msm/mdss/dsi_host_v2.c
+++ b/drivers/video/msm/mdss/dsi_host_v2.c
@@ -958,14 +958,18 @@ int msm_dsi_cmdlist_commit(struct mdss_dsi_ctrl_pdata *ctrl, int from_mdp)
}
msm_dsi_clk_ctrl(&ctrl->panel_data, 1);
- dsi_set_tx_power_mode(0);
+
+ if (0 == (req->flags & CMD_REQ_LP_MODE))
+ dsi_set_tx_power_mode(0);
if (req->flags & CMD_REQ_RX)
msm_dsi_cmdlist_rx(ctrl, req);
else
msm_dsi_cmdlist_tx(ctrl, req);
- dsi_set_tx_power_mode(1);
+ if (0 == (req->flags & CMD_REQ_LP_MODE))
+ dsi_set_tx_power_mode(1);
+
msm_dsi_clk_ctrl(&ctrl->panel_data, 0);
mutex_unlock(&ctrl->cmd_mutex);
@@ -1307,6 +1311,21 @@ static int dsi_get_panel_cfg(char *panel_cfg)
return rc;
}
+static struct device_node *dsi_pref_prim_panel(
+ struct platform_device *pdev)
+{
+ struct device_node *dsi_pan_node = NULL;
+
+ pr_debug("%s:%d: Select primary panel from dt\n",
+ __func__, __LINE__);
+ dsi_pan_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,dsi-pref-prim-pan", 0);
+ if (!dsi_pan_node)
+ pr_err("%s:can't find panel phandle\n", __func__);
+
+ return dsi_pan_node;
+}
+
/**
* dsi_find_panel_of_node(): find device node of dsi panel
* @pdev: platform_device of the dsi ctrl node
@@ -1336,14 +1355,7 @@ static struct device_node *dsi_find_panel_of_node(
/* no panel cfg chg, parse dt */
pr_debug("%s:%d: no cmd line cfg present\n",
__func__, __LINE__);
- dsi_pan_node = of_parse_phandle(
- pdev->dev.of_node,
- "qcom,dsi-pref-prim-pan", 0);
- if (!dsi_pan_node) {
- pr_err("%s:can't find panel phandle\n",
- __func__);
- return NULL;
- }
+ dsi_pan_node = dsi_pref_prim_panel(pdev);
} else {
if (panel_cfg[0] != '0') {
pr_err("%s:%d:ctrl id=[%d] not supported\n",
@@ -1371,7 +1383,7 @@ static struct device_node *dsi_find_panel_of_node(
if (!dsi_pan_node) {
pr_err("%s: invalid pan node\n",
__func__);
- return NULL;
+ dsi_pan_node = dsi_pref_prim_panel(pdev);
}
}
return dsi_pan_node;
diff --git a/drivers/video/msm/mdss/mdp3.c b/drivers/video/msm/mdss/mdp3.c
index 4dc7cc513d70..171d56ecb488 100644
--- a/drivers/video/msm/mdss/mdp3.c
+++ b/drivers/video/msm/mdss/mdp3.c
@@ -186,6 +186,7 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
int i = 0;
struct mdp3_hw_resource *mdata = (struct mdp3_hw_resource *)ptr;
u32 mdp_interrupt = 0;
+ u32 mdp_status = 0;
spin_lock(&mdata->irq_lock);
if (!mdata->irq_mask)
@@ -194,8 +195,8 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
clk_enable(mdp3_res->clocks[MDP3_CLK_AHB]);
clk_enable(mdp3_res->clocks[MDP3_CLK_CORE]);
- mdp_interrupt = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
- MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_interrupt);
+ mdp_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS);
+ mdp_interrupt = mdp_status;
pr_debug("mdp3_irq_handler irq=%d\n", mdp_interrupt);
mdp_interrupt &= mdata->irq_mask;
@@ -206,6 +207,7 @@ static irqreturn_t mdp3_irq_handler(int irq, void *ptr)
mdp_interrupt = mdp_interrupt >> 1;
i++;
}
+ MDP3_REG_WRITE(MDP3_REG_INTR_CLEAR, mdp_status);
clk_disable(mdp3_res->clocks[MDP3_CLK_AHB]);
clk_disable(mdp3_res->clocks[MDP3_CLK_CORE]);
@@ -949,7 +951,7 @@ static int mdp3_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
{
char *t = NULL;
char pan_intf_str[MDSS_MAX_PANEL_LEN];
- int rc, i;
+ int rc, i, panel_len;
char pan_name[MDSS_MAX_PANEL_LEN];
if (!pan_cfg)
@@ -986,6 +988,14 @@ static int mdp3_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg));
pr_debug("%s:%d: t=[%s] panel name=[%s]\n", __func__, __LINE__,
t, pan_cfg->arg_cfg);
+
+ panel_len = strlen(pan_cfg->arg_cfg);
+ if (!panel_len) {
+ pr_err("%s: Panel name is invalid\n", __func__);
+ pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
+ return -EINVAL;
+ }
+
rc = mdp3_get_pan_intf(pan_intf_str);
pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc;
return 0;
@@ -1069,10 +1079,10 @@ static int mdp3_parse_bootarg(struct platform_device *pdev)
of_node_put(chosen_node);
rc = mdp3_get_pan_cfg(pan_cfg);
- if (!rc)
+ if (!rc) {
pan_cfg->init_done = true;
-
- return rc;
+ return rc;
+ }
get_dt_pan:
rc = mdp3_parse_dt_pan_intf(pdev);
@@ -1444,7 +1454,9 @@ int mdp3_self_map_iommu(struct ion_client *client, struct ion_handle *handle,
ret = 0;
} else {
ret = PTR_ERR(iommu_meta);
- goto out_unlock;
+ mutex_unlock(&mdp3_res->iommu_lock);
+ pr_err("%s: meta_create failed err=%d", __func__, ret);
+ return ret;
}
} else {
if (iommu_meta->flags != iommu_flags) {
@@ -1729,7 +1741,7 @@ int mdp3_parse_dt_splash(struct msm_fb_data_type *mfd)
u32 offsets[2];
rc = of_property_read_u32_array(pdev->dev.of_node,
- "qti,memblock-reserve", offsets, 2);
+ "qcom,memblock-reserve", offsets, 2);
if (rc) {
pr_err("fail to get memblock-reserve property\n");
diff --git a/drivers/video/msm/mdss/mdp3.h b/drivers/video/msm/mdss/mdp3.h
index 046593237045..8b4b1417f435 100644
--- a/drivers/video/msm/mdss/mdp3.h
+++ b/drivers/video/msm/mdss/mdp3.h
@@ -25,6 +25,7 @@
#include "mdss_fb.h"
#define MDP_VSYNC_CLK_RATE 19200000
+#define KOFF_TIMEOUT msecs_to_jiffies(84)
enum {
MDP3_CLK_AHB,
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.c b/drivers/video/msm/mdss/mdp3_ctrl.c
index eefca918b14d..ea37899f60fd 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.c
+++ b/drivers/video/msm/mdss/mdp3_ctrl.c
@@ -28,7 +28,10 @@
#define MDP_CORE_CLK_RATE 100000000
#define VSYNC_EXPIRE_TICK 4
-static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req,
+ int image_size,
+ int *pipe_ndx);
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
static int mdp3_histogram_stop(struct mdp3_session_data *session,
u32 block);
@@ -91,6 +94,36 @@ static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq)
return bufq->count;
}
+void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses,
+ struct notifier_block *notifier)
+{
+ blocking_notifier_chain_register(&ses->notifier_head, notifier);
+}
+
+void mdp3_ctrl_notifier_unregister(struct mdp3_session_data *ses,
+ struct notifier_block *notifier)
+{
+ blocking_notifier_chain_unregister(&ses->notifier_head, notifier);
+}
+
+int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event)
+{
+ return blocking_notifier_call_chain(&ses->notifier_head, event, ses);
+}
+
+static void mdp3_dispatch_dma_done(struct work_struct *work)
+{
+ struct mdp3_session_data *session;
+
+ pr_debug("%s\n", __func__);
+ session = container_of(work, struct mdp3_session_data,
+ dma_done_work);
+ if (!session)
+ return;
+
+ mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE);
+}
+
static void mdp3_dispatch_clk_off(struct work_struct *work)
{
struct mdp3_session_data *session;
@@ -121,6 +154,12 @@ void vsync_notify_handler(void *arg)
sysfs_notify_dirent(session->vsync_event_sd);
}
+void dma_done_notify_handler(void *arg)
+{
+ struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
+ schedule_work(&session->dma_done_work);
+}
+
void vsync_count_down(void *arg)
{
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
@@ -140,8 +179,8 @@ void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session,
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
{
struct mdp3_session_data *mdp3_session;
- struct mdp3_vsync_notification vsync_client;
- struct mdp3_vsync_notification *arg = NULL;
+ struct mdp3_notification vsync_client;
+ struct mdp3_notification *arg = NULL;
pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -464,6 +503,7 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
int frame_rate = mfd->panel_info->mipi.frame_rate;
int vbp, vfp, vspw;
int vtotal, vporch;
+ struct mdp3_notification dma_done_callback;
vbp = panel_info->lcdc.v_back_porch;
vfp = panel_info->lcdc.v_front_porch;
@@ -499,6 +539,13 @@ static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
rc = dma->dma_config(dma, &sourceConfig, &outputConfig);
else
rc = -EINVAL;
+
+ if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
+ dma_done_callback.handler = dma_done_notify_handler;
+ dma_done_callback.arg = mfd->mdp.private1;
+ dma->dma_done_notifier(dma, &dma_done_callback);
+ }
+
return rc;
}
@@ -527,6 +574,8 @@ static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
}
mdp3_batfet_ctrl(true);
+ mdp3_ctrl_notifier_register(mdp3_session,
+ &mdp3_session->mfd->mdp_sync_pt_data.notifier);
rc = mdp3_iommu_enable(MDP3_CLIENT_DMA_P);
if (rc) {
@@ -658,6 +707,8 @@ static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
if (rc)
pr_err("fail to dettach MDP DMA SMMU\n");
+ mdp3_ctrl_notifier_unregister(mdp3_session,
+ &mdp3_session->mfd->mdp_sync_pt_data.notifier);
mdp3_batfet_ctrl(false);
mdp3_session->vsync_enabled = 0;
atomic_set(&mdp3_session->vsync_countdown, 0);
@@ -677,7 +728,7 @@ static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd)
struct mdp3_session_data *mdp3_session;
struct mdp3_dma *mdp3_dma;
struct mdss_panel_data *panel;
- struct mdp3_vsync_notification vsync_client;
+ struct mdp3_notification vsync_client;
pr_debug("mdp3_ctrl_reset_cmd\n");
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -728,7 +779,7 @@ static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd)
struct mdp3_session_data *mdp3_session;
struct mdp3_dma *mdp3_dma;
struct mdss_panel_data *panel;
- struct mdp3_vsync_notification vsync_client;
+ struct mdp3_notification vsync_client;
pr_debug("mdp3_ctrl_reset\n");
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
@@ -971,17 +1022,31 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
return -EPERM;
}
+ mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
data = mdp3_bufq_pop(&mdp3_session->bufq_in);
if (data) {
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
mdp3_ctrl_clk_enable(mfd, 1);
- mdp3_session->dma->update(mdp3_session->dma,
+ rc = mdp3_session->dma->update(mdp3_session->dma,
(void *)(int)data->addr,
mdp3_session->intf);
+ /* This is for the previous frame */
+ if (rc < 0) {
+ mdp3_ctrl_notify(mdp3_session,
+ MDP_NOTIFY_FRAME_TIMEOUT);
+ } else {
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ mdp3_ctrl_notify(mdp3_session,
+ MDP_NOTIFY_FRAME_DONE);
+ }
+ }
+
+ mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
mdp3_bufq_push(&mdp3_session->bufq_out, data);
}
- if (mdp3_bufq_count(&mdp3_session->bufq_out) > 2) {
+ if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
if (data)
mdp3_put_img(data, MDP3_CLIENT_DMA_P);
@@ -1001,16 +1066,20 @@ static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
mdss_fb_update_notify_update(mfd);
- return rc;
+ return 0;
}
-static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
+static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req,
+ int image_size,
+ int *pipe_ndx)
{
struct fb_info *fbi;
struct mdp3_session_data *mdp3_session;
u32 offset;
int bpp;
struct mdss_panel_info *panel_info;
+ int rc;
pr_debug("mdp3_ctrl_pan_display\n");
if (!mfd || !mfd->mdp.private1)
@@ -1049,10 +1118,23 @@ static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
if (mfd->fbi->screen_base) {
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
+ mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
mdp3_ctrl_clk_enable(mfd, 1);
- mdp3_session->dma->update(mdp3_session->dma,
+ rc = mdp3_session->dma->update(mdp3_session->dma,
(void *)(int)(mfd->iova + offset),
mdp3_session->intf);
+ /* This is for the previous frame */
+ if (rc < 0) {
+ mdp3_ctrl_notify(mdp3_session,
+ MDP_NOTIFY_FRAME_TIMEOUT);
+ } else {
+ if (mdp3_ctrl_get_intf_type(mfd) ==
+ MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ mdp3_ctrl_notify(mdp3_session,
+ MDP_NOTIFY_FRAME_DONE);
+ }
+ }
+ mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
} else {
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
mdp3_clk_enable(1, 0);
@@ -1683,6 +1765,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
memset(mdp3_session, 0, sizeof(struct mdp3_session_data));
mutex_init(&mdp3_session->lock);
INIT_WORK(&mdp3_session->clk_off_work, mdp3_dispatch_clk_off);
+ INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done);
atomic_set(&mdp3_session->vsync_countdown, 0);
mutex_init(&mdp3_session->histo_lock);
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
@@ -1718,6 +1801,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
mdp3_bufq_init(&mdp3_session->bufq_out);
mdp3_session->histo_status = 0;
mdp3_session->lut_sel = 0;
+ BLOCKING_INIT_NOTIFIER_HEAD(&mdp3_session->notifier_head);
init_timer(&mdp3_session->vsync_timer);
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
@@ -1746,8 +1830,11 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
kobject_uevent(&dev->kobj, KOBJ_ADD);
pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
- if (mdp3_get_cont_spash_en())
+ if (mdp3_get_cont_spash_en()) {
mdp3_session->clk_on = 1;
+ mdp3_ctrl_notifier_register(mdp3_session,
+ &mdp3_session->mfd->mdp_sync_pt_data.notifier);
+ }
if (splash_mismatch) {
pr_err("splash memory mismatch, stop splash\n");
diff --git a/drivers/video/msm/mdss/mdp3_ctrl.h b/drivers/video/msm/mdss/mdp3_ctrl.h
index f2484ef951a4..cfad1d3c8f58 100644
--- a/drivers/video/msm/mdss/mdp3_ctrl.h
+++ b/drivers/video/msm/mdss/mdp3_ctrl.h
@@ -49,6 +49,7 @@ struct mdp3_session_data {
struct mdp3_buffer_queue bufq_in;
struct mdp3_buffer_queue bufq_out;
struct work_struct clk_off_work;
+ struct work_struct dma_done_work;
int histo_status;
struct mutex histo_lock;
int lut_sel;
@@ -56,6 +57,7 @@ struct mdp3_session_data {
bool vsync_before_commit;
bool first_commit;
int clk_on;
+ struct blocking_notifier_head notifier_head;
int vsync_enabled;
atomic_t vsync_countdown; /* Used to count down */
diff --git a/drivers/video/msm/mdss/mdp3_dma.c b/drivers/video/msm/mdss/mdp3_dma.c
index 4d9bceab8691..13a453e01bbf 100644
--- a/drivers/video/msm/mdss/mdp3_dma.c
+++ b/drivers/video/msm/mdss/mdp3_dma.c
@@ -27,26 +27,38 @@
static void mdp3_vsync_intr_handler(int type, void *arg)
{
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
- struct mdp3_vsync_notification vsync_client;
+ struct mdp3_notification vsync_client;
+ unsigned int wait_for_next_vs;
pr_debug("mdp3_vsync_intr_handler\n");
spin_lock(&dma->dma_lock);
vsync_client = dma->vsync_client;
- complete(&dma->vsync_comp);
+ wait_for_next_vs = !dma->vsync_status;
+ dma->vsync_status = 0;
+ if (wait_for_next_vs)
+ complete(&dma->vsync_comp);
spin_unlock(&dma->dma_lock);
- if (vsync_client.handler)
+ if (vsync_client.handler) {
vsync_client.handler(vsync_client.arg);
- else
- mdp3_irq_disable_nosync(type);
+ } else {
+ if (wait_for_next_vs)
+ mdp3_irq_disable_nosync(type);
+ }
}
static void mdp3_dma_done_intr_handler(int type, void *arg)
{
struct mdp3_dma *dma = (struct mdp3_dma *)arg;
+ struct mdp3_notification dma_client;
pr_debug("mdp3_dma_done_intr_handler\n");
+ spin_lock(&dma->dma_lock);
+ dma_client = dma->dma_notifier_client;
complete(&dma->dma_comp);
+ spin_unlock(&dma->dma_lock);
mdp3_irq_disable_nosync(type);
+ if (dma_client.handler)
+ dma_client.handler(dma_client.arg);
}
static void mdp3_hist_done_intr_handler(int type, void *arg)
@@ -189,7 +201,7 @@ static int mdp3_dma_callback_setup(struct mdp3_dma *dma)
}
static void mdp3_dma_vsync_enable(struct mdp3_dma *dma,
- struct mdp3_vsync_notification *vsync_client)
+ struct mdp3_notification *vsync_client)
{
unsigned long flag;
int updated = 0;
@@ -220,6 +232,21 @@ static void mdp3_dma_vsync_enable(struct mdp3_dma *dma,
}
}
+static void mdp3_dma_done_notifier(struct mdp3_dma *dma,
+ struct mdp3_notification *dma_client)
+{
+ unsigned long flag;
+
+ spin_lock_irqsave(&dma->dma_lock, flag);
+ if (dma_client) {
+ dma->dma_notifier_client = *dma_client;
+ } else {
+ dma->dma_notifier_client.handler = NULL;
+ dma->dma_notifier_client.arg = NULL;
+ }
+ spin_unlock_irqrestore(&dma->dma_lock, flag);
+}
+
static void mdp3_dma_clk_auto_gating(struct mdp3_dma *dma, int enable)
{
u32 cgc;
@@ -546,13 +573,20 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,
{
unsigned long flag;
int cb_type = MDP3_DMA_CALLBACK_TYPE_VSYNC;
+ int rc = 0;
pr_debug("mdp3_dmap_update\n");
if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
cb_type = MDP3_DMA_CALLBACK_TYPE_DMA_DONE;
- if (intf->active)
- wait_for_completion_killable(&dma->dma_comp);
+ if (intf->active) {
+ rc = wait_for_completion_timeout(&dma->dma_comp,
+ KOFF_TIMEOUT);
+ if (rc <= 0) {
+ WARN(1, "cmd kickoff timed out (%d)\n", rc);
+ rc = -1;
+ }
+ }
}
spin_lock_irqsave(&dma->dma_lock, flag);
MDP3_REG_WRITE(MDP3_REG_DMA_P_IBUF_ADDR, (u32)buf);
@@ -567,16 +601,22 @@ static int mdp3_dmap_update(struct mdp3_dma *dma, void *buf,
intf->start(intf);
}
- wmb();
+ mb();
+ dma->vsync_status = MDP3_REG_READ(MDP3_REG_INTR_STATUS) &
+ (1 << MDP3_INTR_LCDC_START_OF_FRAME);
init_completion(&dma->vsync_comp);
spin_unlock_irqrestore(&dma->dma_lock, flag);
mdp3_dma_callback_enable(dma, cb_type);
pr_debug("mdp3_dmap_update wait for vsync_comp in\n");
- if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
- wait_for_completion_killable(&dma->vsync_comp);
+ if (dma->output_config.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
+ rc = wait_for_completion_timeout(&dma->vsync_comp,
+ KOFF_TIMEOUT);
+ if (rc <= 0)
+ rc = -1;
+ }
pr_debug("mdp3_dmap_update wait for vsync_comp out\n");
- return 0;
+ return rc;
}
static int mdp3_dmas_update(struct mdp3_dma *dma, void *buf,
@@ -870,6 +910,7 @@ int mdp3_dma_init(struct mdp3_dma *dma)
dma->get_histo = mdp3_dmap_histo_get;
dma->histo_op = mdp3_dmap_histo_op;
dma->vsync_enable = mdp3_dma_vsync_enable;
+ dma->dma_done_notifier = mdp3_dma_done_notifier;
dma->start = mdp3_dma_start;
dma->stop = mdp3_dma_stop;
dma->config_stride = mdp3_dma_stride_config;
diff --git a/drivers/video/msm/mdss/mdp3_dma.h b/drivers/video/msm/mdss/mdp3_dma.h
index 935025f2ea45..d914c712ef72 100644
--- a/drivers/video/msm/mdss/mdp3_dma.h
+++ b/drivers/video/msm/mdss/mdp3_dma.h
@@ -14,6 +14,7 @@
#ifndef MDP3_DMA_H
#define MDP3_DMA_H
+#include <linux/notifier.h>
#include <linux/sched.h>
#define MDP_HISTOGRAM_BL_SCALE_MAX 1024
@@ -227,7 +228,7 @@ struct mdp3_dma_histogram_data {
u32 extra[2];
};
-struct mdp3_vsync_notification {
+struct mdp3_notification {
void (*handler)(void *arg);
void *arg;
};
@@ -245,7 +246,8 @@ struct mdp3_dma {
struct completion vsync_comp;
struct completion dma_comp;
struct completion histo_comp;
- struct mdp3_vsync_notification vsync_client;
+ struct mdp3_notification vsync_client;
+ struct mdp3_notification dma_notifier_client;
struct mdp3_dma_output_config output_config;
struct mdp3_dma_source source_config;
@@ -256,6 +258,7 @@ struct mdp3_dma {
struct mdp3_dma_histogram_config histogram_config;
int histo_state;
struct mdp3_dma_histogram_data histo_data;
+ unsigned int vsync_status;
int (*dma_config)(struct mdp3_dma *dma,
struct mdp3_dma_source *source_config,
@@ -290,7 +293,10 @@ struct mdp3_dma {
void (*config_stride)(struct mdp3_dma *dma, int stride);
void (*vsync_enable)(struct mdp3_dma *dma,
- struct mdp3_vsync_notification *vsync_client);
+ struct mdp3_notification *vsync_client);
+
+ void (*dma_done_notifier)(struct mdp3_dma *dma,
+ struct mdp3_notification *dma_client);
};
struct mdp3_video_intf_cfg {
diff --git a/drivers/video/msm/mdss/mdss_dsi.c b/drivers/video/msm/mdss/mdss_dsi.c
index 5a66b8ab3366..9eb8643563a9 100644
--- a/drivers/video/msm/mdss/mdss_dsi.c
+++ b/drivers/video/msm/mdss/mdss_dsi.c
@@ -783,6 +783,21 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata,
return rc;
}
+static struct device_node *mdss_dsi_pref_prim_panel(
+ struct platform_device *pdev)
+{
+ struct device_node *dsi_pan_node = NULL;
+
+ pr_debug("%s:%d: Select primary panel from dt\n",
+ __func__, __LINE__);
+ dsi_pan_node = of_parse_phandle(pdev->dev.of_node,
+ "qcom,dsi-pref-prim-pan", 0);
+ if (!dsi_pan_node)
+ pr_err("%s:can't find panel phandle\n", __func__);
+
+ return dsi_pan_node;
+}
+
/**
* mdss_dsi_find_panel_of_node(): find device node of dsi panel
* @pdev: platform_device of the dsi ctrl node
@@ -810,14 +825,7 @@ static struct device_node *mdss_dsi_find_panel_of_node(
/* no panel cfg chg, parse dt */
pr_debug("%s:%d: no cmd line cfg present\n",
__func__, __LINE__);
- dsi_pan_node = of_parse_phandle(
- pdev->dev.of_node,
- "qcom,dsi-pref-prim-pan", 0);
- if (!dsi_pan_node) {
- pr_err("%s:can't find panel phandle\n",
- __func__);
- return NULL;
- }
+ dsi_pan_node = mdss_dsi_pref_prim_panel(pdev);
} else {
if (panel_cfg[0] == '0') {
pr_debug("%s:%d: DSI ctrl 1\n", __func__, __LINE__);
@@ -850,11 +858,12 @@ static struct device_node *mdss_dsi_find_panel_of_node(
dsi_pan_node = of_find_node_by_name(mdss_node,
panel_name);
if (!dsi_pan_node) {
- pr_err("%s: invalid pan node\n",
+ pr_err("%s: invalid pan node, selecting prim panel\n",
__func__);
- return NULL;
+ dsi_pan_node = mdss_dsi_pref_prim_panel(pdev);
}
}
+
return dsi_pan_node;
}
diff --git a/drivers/video/msm/mdss/mdss_dsi_cmd.h b/drivers/video/msm/mdss/mdss_dsi_cmd.h
index c48075601310..f806e78e6aee 100644
--- a/drivers/video/msm/mdss/mdss_dsi_cmd.h
+++ b/drivers/video/msm/mdss/mdss_dsi_cmd.h
@@ -98,6 +98,7 @@ struct dsi_cmd_desc {
#define CMD_REQ_COMMIT 0x0002
#define CMD_CLK_CTRL 0x0004
#define CMD_REQ_NO_MAX_PKT_SIZE 0x0008
+#define CMD_REQ_LP_MODE 0x0010
struct dcs_cmd_req {
struct dsi_cmd_desc *cmds;
diff --git a/drivers/video/msm/mdss/mdss_dsi_panel.c b/drivers/video/msm/mdss/mdss_dsi_panel.c
index c9d6c4484818..fc63ce48b7be 100644
--- a/drivers/video/msm/mdss/mdss_dsi_panel.c
+++ b/drivers/video/msm/mdss/mdss_dsi_panel.c
@@ -19,7 +19,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/leds.h>
-#include <linux/pwm.h>
+#include <linux/qpnp/pwm.h>
#include <linux/err.h>
#include "mdss_dsi.h"
@@ -69,9 +69,9 @@ static void mdss_dsi_panel_bklt_pwm(struct mdss_dsi_ctrl_pdata *ctrl, int level)
ctrl->pwm_enabled = 0;
}
- ret = pwm_config(ctrl->pwm_bl, duty, ctrl->pwm_period);
+ ret = pwm_config_us(ctrl->pwm_bl, duty, ctrl->pwm_period);
if (ret) {
- pr_err("%s: pwm_config() failed err=%d.\n", __func__, ret);
+ pr_err("%s: pwm_config_us() failed err=%d.\n", __func__, ret);
return;
}
@@ -118,6 +118,11 @@ static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl,
cmdreq.cmds = pcmds->cmds;
cmdreq.cmds_cnt = pcmds->cmd_cnt;
cmdreq.flags = CMD_REQ_COMMIT;
+
+ /*Panel ON/Off commands should be sent in DSI Low Power Mode*/
+ if (pcmds->link_state == DSI_LP_MODE)
+ cmdreq.flags |= CMD_REQ_LP_MODE;
+
cmdreq.rlen = 0;
cmdreq.cb = NULL;
@@ -810,6 +815,8 @@ static int mdss_panel_parse_dt(struct device_node *np,
ctrl_pdata->bklt_ctrl = BL_DCS_CMD;
}
}
+ rc = of_property_read_u32(np, "qcom,mdss-brightness-max-level", &tmp);
+ pinfo->brightness_max = (!rc ? tmp : MDSS_MAX_BL_BRIGHTNESS);
rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-min-level", &tmp);
pinfo->bl_min = (!rc ? tmp : 0);
rc = of_property_read_u32(np, "qcom,mdss-dsi-bl-max-level", &tmp);
diff --git a/drivers/video/msm/mdss/mdss_edp.c b/drivers/video/msm/mdss/mdss_edp.c
index 19bc1b144321..1b3f98db4f89 100644
--- a/drivers/video/msm/mdss/mdss_edp.c
+++ b/drivers/video/msm/mdss/mdss_edp.c
@@ -23,7 +23,7 @@
#include <linux/gpio.h>
#include <linux/err.h>
#include <linux/regulator/consumer.h>
-#include <linux/pwm.h>
+#include <linux/qpnp/pwm.h>
#include <linux/clk.h>
#include <linux/spinlock_types.h>
#include <linux/kthread.h>
@@ -206,11 +206,11 @@ void mdss_edp_set_backlight(struct mdss_panel_data *pdata, u32 bl_level)
if (bl_level > bl_max)
bl_level = bl_max;
- ret = pwm_config(edp_drv->bl_pwm,
+ ret = pwm_config_us(edp_drv->bl_pwm,
bl_level * edp_drv->pwm_period / bl_max,
edp_drv->pwm_period);
if (ret) {
- pr_err("%s: pwm_config() failed err=%d.\n", __func__,
+ pr_err("%s: pwm_config_us() failed err=%d.\n", __func__,
ret);
return;
}
@@ -714,10 +714,16 @@ static int mdss_edp_remove(struct platform_device *pdev)
static int mdss_edp_device_register(struct mdss_edp_drv_pdata *edp_drv)
{
int ret;
+ u32 tmp;
mdss_edp_edid2pinfo(edp_drv);
edp_drv->panel_data.panel_info.bl_min = 1;
edp_drv->panel_data.panel_info.bl_max = 255;
+ ret = of_property_read_u32(edp_drv->pdev->dev.of_node,
+ "qcom,mdss-brightness-max-level", &tmp);
+ edp_drv->panel_data.panel_info.brightness_max =
+ (!ret ? tmp : MDSS_MAX_BL_BRIGHTNESS);
+
edp_drv->panel_data.panel_info.edp.frame_rate =
DEFAULT_FRAME_RATE;/* 60 fps */
diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c
index f7c7f99b0822..fe622f42d3e4 100644
--- a/drivers/video/msm/mdss/mdss_fb.c
+++ b/drivers/video/msm/mdss/mdss_fb.c
@@ -147,6 +147,47 @@ static int mdss_fb_notify_update(struct msm_fb_data_type *mfd,
return ret;
}
+static int mdss_fb_splash_thread(void *data)
+{
+ struct msm_fb_data_type *mfd = data;
+ int ret = -EINVAL;
+ struct fb_info *fbi = NULL;
+ int ov_index[2];
+
+ if (!mfd || !mfd->fbi || !mfd->mdp.splash_fnc) {
+ pr_err("Invalid input parameter\n");
+ goto end;
+ }
+
+ fbi = mfd->fbi;
+
+ ret = mdss_fb_open(fbi, current->tgid);
+ if (ret) {
+ pr_err("fb_open failed\n");
+ goto end;
+ }
+
+ mfd->bl_updated = true;
+ mdss_fb_set_backlight(mfd, mfd->panel_info->bl_max >> 1);
+
+ ret = mfd->mdp.splash_fnc(mfd, ov_index, MDP_CREATE_SPLASH_OV);
+ if (ret) {
+ pr_err("Splash image failed\n");
+ goto splash_err;
+ }
+
+ do {
+ schedule_timeout_interruptible(SPLASH_THREAD_WAIT_TIMEOUT * HZ);
+ } while (!kthread_should_stop());
+
+ mfd->mdp.splash_fnc(mfd, ov_index, MDP_REMOVE_SPLASH_OV);
+
+splash_err:
+ mdss_fb_release(fbi, current->tgid);
+end:
+ return ret;
+}
+
static int lcd_backlight_registered;
static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
@@ -155,13 +196,13 @@ static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev,
struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
int bl_lvl;
- if (value > MDSS_MAX_BL_BRIGHTNESS)
- value = MDSS_MAX_BL_BRIGHTNESS;
+ if (value > mfd->panel_info->brightness_max)
+ value = mfd->panel_info->brightness_max;
/* This maps android backlight level 0 to 255 into
driver backlight level 0 to bl_max with rounding */
MDSS_BRIGHT_TO_BL(bl_lvl, value, mfd->panel_info->bl_max,
- MDSS_MAX_BL_BRIGHTNESS);
+ mfd->panel_info->brightness_max);
if (!bl_lvl && value)
bl_lvl = 1;
@@ -376,6 +417,8 @@ static int mdss_fb_probe(struct platform_device *pdev)
/* android supports only one lcd-backlight/lcd for now */
if (!lcd_backlight_registered) {
+ backlight_led.brightness = mfd->panel_info->brightness_max;
+ backlight_led.max_brightness = mfd->panel_info->brightness_max;
if (led_classdev_register(&pdev->dev, &backlight_led))
pr_err("led_classdev_register failed\n");
else
@@ -405,6 +448,16 @@ static int mdss_fb_probe(struct platform_device *pdev)
else
mfd->mdp_sync_pt_data.threshold = 2;
+ if (mfd->index == 0) {
+ mfd->splash_thread = kthread_run(mdss_fb_splash_thread, mfd,
+ "mdss_fb_splash");
+ if (IS_ERR(mfd->splash_thread)) {
+ pr_err("unable to start splash thread %d\n",
+ mfd->index);
+ mfd->splash_thread = NULL;
+ }
+ }
+
return rc;
}
@@ -1212,6 +1265,12 @@ static int mdss_fb_open(struct fb_info *info, int user)
pinfo->ref_cnt++;
mfd->ref_cnt++;
+ /* Stop the splash thread once userspace open the fb node */
+ if (mfd->splash_thread && mfd->ref_cnt > 1) {
+ kthread_stop(mfd->splash_thread);
+ mfd->splash_thread = NULL;
+ }
+
return 0;
blank_error:
@@ -1591,7 +1650,7 @@ static int mdss_fb_pan_display_sub(struct fb_var_screeninfo *var,
(var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
if (mfd->mdp.dma_fnc)
- mfd->mdp.dma_fnc(mfd);
+ mfd->mdp.dma_fnc(mfd, NULL, 0, NULL);
else
pr_warn("dma function not set for panel type=%d\n",
mfd->panel.type);
@@ -2221,13 +2280,6 @@ static int mdss_fb_register_extra_panel(struct platform_device *pdev,
return -EEXIST;
}
- if ((fb_pdata->panel_info.type != MIPI_VIDEO_PANEL) ||
- (pdata->panel_info.type != MIPI_VIDEO_PANEL)) {
- pr_err("Split panel not supported for panel type %d\n",
- pdata->panel_info.type);
- return -EINVAL;
- }
-
fb_pdata->next = pdata;
return 0;
diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h
index fc6208f75a15..7a7ddfac1b17 100644
--- a/drivers/video/msm/mdss/mdss_fb.h
+++ b/drivers/video/msm/mdss/mdss_fb.h
@@ -36,6 +36,8 @@
#define WAIT_DISP_OP_TIMEOUT ((WAIT_FENCE_FIRST_TIMEOUT + \
WAIT_FENCE_FINAL_TIMEOUT) * MDP_MAX_FENCE_FD)
+#define SPLASH_THREAD_WAIT_TIMEOUT 3
+
#ifndef MAX
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
@@ -70,6 +72,11 @@ enum mdp_notify_event {
MDP_NOTIFY_FRAME_TIMEOUT,
};
+enum mdp_splash_event {
+ MDP_CREATE_SPLASH_OV = 0,
+ MDP_REMOVE_SPLASH_OV,
+};
+
struct disp_info_type_suspend {
int op_enable;
int panel_power_on;
@@ -113,7 +120,8 @@ struct msm_mdp_interface {
int (*kickoff_fnc)(struct msm_fb_data_type *mfd,
struct mdp_display_commit *data);
int (*ioctl_handler)(struct msm_fb_data_type *mfd, u32 cmd, void *arg);
- void (*dma_fnc)(struct msm_fb_data_type *mfd);
+ void (*dma_fnc)(struct msm_fb_data_type *mfd, struct mdp_overlay *req,
+ int image_len, int *pipe_ndx);
int (*cursor_update)(struct msm_fb_data_type *mfd,
struct fb_cursor *cursor);
int (*lut_update)(struct msm_fb_data_type *mfd, struct fb_cmap *cmap);
@@ -122,6 +130,7 @@ struct msm_mdp_interface {
int (*update_ad_input)(struct msm_fb_data_type *mfd);
int (*panel_register_done)(struct mdss_panel_data *pdata);
u32 (*fb_stride)(u32 fb_index, u32 xres, int bpp);
+ int (*splash_fnc) (struct msm_fb_data_type *mfd, int *index, int req);
struct msm_sync_pt_data *(*get_sync_fnc)(struct msm_fb_data_type *mfd,
const struct mdp_buf_sync *buf_sync);
void *private1;
@@ -204,6 +213,8 @@ struct msm_fb_data_type {
wait_queue_head_t idle_wait_q;
bool shutdown_pending;
+ struct task_struct *splash_thread;
+
struct msm_fb_backup_type msm_fb_backup;
struct completion power_set_comp;
u32 is_power_setting;
diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c
index 1036065a16b3..d83e95a97e53 100644
--- a/drivers/video/msm/mdss/mdss_mdp.c
+++ b/drivers/video/msm/mdss/mdss_mdp.c
@@ -30,6 +30,7 @@
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/sched.h>
@@ -47,7 +48,6 @@
#include <linux/msm_iommu_domains.h>
#include <mach/memory.h>
#include <mach/msm_memtypes.h>
-#include <mach/rpm-regulator-smd.h>
#include "mdss.h"
#include "mdss_fb.h"
@@ -536,6 +536,8 @@ static int mdss_mdp_clk_update(u32 clk_idx, u32 enable)
if (clk) {
pr_debug("clk=%d en=%d\n", clk_idx, enable);
if (enable) {
+ if (clk_idx == MDSS_CLK_MDP_VSYNC)
+ clk_set_rate(clk, 19200000);
ret = clk_prepare_enable(clk);
} else {
clk_disable_unprepare(clk);
@@ -1311,7 +1313,7 @@ static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
{
char *t = NULL;
char pan_intf_str[MDSS_MAX_PANEL_LEN];
- int rc, i;
+ int rc, i, panel_len;
char pan_name[MDSS_MAX_PANEL_LEN];
if (!pan_cfg)
@@ -1348,6 +1350,14 @@ static int mdss_mdp_get_pan_cfg(struct mdss_panel_cfg *pan_cfg)
strlcpy(&pan_cfg->arg_cfg[0], t, sizeof(pan_cfg->arg_cfg));
pr_debug("%s:%d: t=[%s] panel name=[%s]\n", __func__, __LINE__,
t, pan_cfg->arg_cfg);
+
+ panel_len = strlen(pan_cfg->arg_cfg);
+ if (!panel_len) {
+ pr_err("%s: Panel name is invalid\n", __func__);
+ pan_cfg->pan_intf = MDSS_PANEL_INTF_INVALID;
+ return -EINVAL;
+ }
+
rc = mdss_mdp_get_pan_intf(pan_intf_str);
pan_cfg->pan_intf = (rc < 0) ? MDSS_PANEL_INTF_INVALID : rc;
return 0;
@@ -1452,10 +1462,10 @@ static int mdss_mdp_parse_bootarg(struct platform_device *pdev)
of_node_put(chosen_node);
rc = mdss_mdp_get_pan_cfg(pan_cfg);
- if (!rc)
+ if (!rc) {
pan_cfg->init_done = true;
-
- return rc;
+ return rc;
+ }
get_dt_pan:
rc = mdss_mdp_parse_dt_pan_intf(pdev);
diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h
index 9e0e9cc4afc9..ae3f1a028733 100644
--- a/drivers/video/msm/mdss/mdss_mdp.h
+++ b/drivers/video/msm/mdss/mdss_mdp.h
@@ -154,6 +154,7 @@ struct mdss_mdp_ctl {
u32 flush_bits;
u32 flush_reg_data;
+ bool split_flush_en;
bool is_video_mode;
u32 play_cnt;
u32 vsync_cnt;
@@ -214,6 +215,8 @@ struct mdss_mdp_mixer {
u16 height;
struct mdss_mdp_img_rect roi;
u8 cursor_enabled;
+ u16 cursor_hotx;
+ u16 cursor_hoty;
u8 rotator_mode;
struct mdss_mdp_ctl *ctl;
@@ -270,11 +273,12 @@ struct pp_hist_col_info {
u32 hist_cnt_sent;
u32 hist_cnt_time;
u32 frame_cnt;
- u32 is_kick_ready;
struct completion comp;
u32 data[HIST_V_SIZE];
struct mutex hist_mutex;
spinlock_t hist_lock;
+ char __iomem *base;
+ u32 intr_shift;
};
struct mdss_mdp_ad {
@@ -285,6 +289,7 @@ struct mdss_mdp_ad {
struct mdss_ad_info {
u8 num;
u8 calc_hw_num;
+ u32 ops;
u32 sts;
u32 reg_sts;
u32 state;
@@ -364,7 +369,6 @@ struct mdss_mdp_pipe {
u8 overfetch_disable;
u32 transp;
u32 bg_color;
- u8 has_buf;
struct msm_fb_data_type *mfd;
struct mdss_mdp_mixer *mixer;
diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c
index 9a39573a3557..cf634775f4cc 100644
--- a/drivers/video/msm/mdss/mdss_mdp_ctl.c
+++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c
@@ -656,13 +656,19 @@ int mdss_mdp_ctl_splash_finish(struct mdss_mdp_ctl *ctl, bool handoff)
static inline int mdss_mdp_set_split_ctl(struct mdss_mdp_ctl *ctl,
struct mdss_mdp_ctl *split_ctl)
{
- if (!ctl || !split_ctl)
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+
+ if (!ctl || !split_ctl || !mdata)
return -ENODEV;
/* setup split ctl mixer as right mixer of original ctl so that
* original ctl can work the same way as dual pipe solution */
ctl->mixer_right = split_ctl->mixer_left;
+ if ((mdata->mdp_rev >= MDSS_MDP_HW_REV_103) &&
+ (ctl->opmode == MDSS_MDP_CTL_OP_VIDEO_MODE))
+ ctl->split_flush_en = true;
+
return 0;
}
@@ -1035,7 +1041,7 @@ static void mdss_mdp_ctl_split_display_enable(int enable,
MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_LOWER_PIPE_CTRL, lower);
MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SPLIT_DISPLAY_EN, enable);
- if (main_ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_103)
+ if (main_ctl->split_flush_en)
MDSS_MDP_REG_WRITE(MMSS_MDP_MDP_SSPP_SPARE_0,
enable ? 0x1 : 0x0);
}
@@ -1954,7 +1960,7 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg)
/* postprocessing setup, including dspp */
mdss_mdp_pp_setup_locked(ctl);
- if (sctl && ctl->mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
+ if (sctl && ctl->split_flush_en) {
ctl->flush_bits |= sctl->flush_bits;
sctl->flush_bits = 0;
}
diff --git a/drivers/video/msm/mdss/mdss_mdp_intf_video.c b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
index 84643de78181..a469dead830b 100644
--- a/drivers/video/msm/mdss/mdss_mdp_intf_video.c
+++ b/drivers/video/msm/mdss/mdss_mdp_intf_video.c
@@ -62,6 +62,7 @@ struct mdss_mdp_video_ctx {
atomic_t vsync_ref;
spinlock_t vsync_lock;
+ struct mutex vsync_mtx;
struct list_head vsync_handlers;
};
@@ -216,19 +217,23 @@ static inline void video_vsync_irq_enable(struct mdss_mdp_ctl *ctl, bool clear)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ mutex_lock(&ctx->vsync_mtx);
if (atomic_inc_return(&ctx->vsync_ref) == 1)
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
else if (clear)
mdss_mdp_irq_clear(ctl->mdata, MDSS_MDP_IRQ_INTF_VSYNC,
ctl->intf_num);
+ mutex_unlock(&ctx->vsync_mtx);
}
static inline void video_vsync_irq_disable(struct mdss_mdp_ctl *ctl)
{
struct mdss_mdp_video_ctx *ctx = ctl->priv_data;
+ mutex_lock(&ctx->vsync_mtx);
if (atomic_dec_return(&ctx->vsync_ref) == 0)
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num);
+ mutex_unlock(&ctx->vsync_mtx);
}
static int mdss_mdp_video_add_vsync_handler(struct mdss_mdp_ctl *ctl,
@@ -687,6 +692,7 @@ int mdss_mdp_video_start(struct mdss_mdp_ctl *ctl)
ctx->intf_type = ctl->intf_type;
init_completion(&ctx->vsync_comp);
spin_lock_init(&ctx->vsync_lock);
+ mutex_init(&ctx->vsync_mtx);
atomic_set(&ctx->vsync_ref, 0);
mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_INTF_VSYNC, ctl->intf_num,
diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c
index a6debba9d371..474a6398c556 100644
--- a/drivers/video/msm/mdss/mdss_mdp_overlay.c
+++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c
@@ -34,6 +34,8 @@
#include "mdss_mdp.h"
#include "mdss_mdp_rotator.h"
+#include "splash.h"
+
#define VSYNC_PERIOD 16
#define BORDERFILL_NDX 0x0BF000BF
#define CHECK_BOUNDS(offset, size, max_size) \
@@ -44,6 +46,8 @@
#define MEM_PROTECT_SD_CTRL 0xF
+#define INVALID_PIPE_INDEX 0xFFFF
+
struct sd_ctrl_req {
unsigned int enable;
} __attribute__ ((__packed__));
@@ -150,6 +154,9 @@ int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd,
pr_err("Invalid decimation factors horz=%d vert=%d\n",
req->horz_deci, req->vert_deci);
return -EINVAL;
+ } else if (req->flags & MDP_BWC_EN) {
+ pr_err("Decimation can't be enabled with BWC\n");
+ return -EINVAL;
}
}
@@ -266,7 +273,8 @@ static int __mdp_pipe_tune_perf(struct mdss_mdp_pipe *pipe)
* requirement by applying vertical decimation and reduce
* mdp clock requirement
*/
- if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION))
+ if (mdata->has_decimation && (pipe->vert_deci < MAX_DECIMATION)
+ && !pipe->bwc_mode)
pipe->vert_deci++;
else
return -EPERM;
@@ -608,7 +616,6 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
}
pipe->params_changed++;
- pipe->has_buf = 0;
req->vert_deci = pipe->vert_deci;
@@ -1257,6 +1264,9 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd,
pr_debug("ov queue pnum=%d\n", pipe->num);
+ if (pipe->flags & MDP_SOLID_FILL)
+ pr_warn("Unexpected buffer queue to a solid fill pipe\n");
+
flags = (pipe->flags & MDP_SECURE_OVERLAY_SESSION);
src_data = &pipe->back_buf;
@@ -1270,7 +1280,6 @@ static int mdss_mdp_overlay_queue(struct msm_fb_data_type *mfd,
if (IS_ERR_VALUE(ret)) {
pr_err("src_data pmem error\n");
}
- pipe->has_buf = 1;
mdss_mdp_pipe_unmap(pipe);
return ret;
@@ -1377,18 +1386,18 @@ static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd)
static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd,
struct mdss_mdp_pipe **ppipe,
- int mixer_mux)
+ int mixer_mux,
+ struct mdp_overlay *req_ov)
{
struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
struct mdss_mdp_pipe *pipe;
+ int ret;
pipe = mdss_mdp_mixer_stage_pipe(mdp5_data->ctl, mixer_mux,
MDSS_MDP_STAGE_BASE);
+
if (pipe == NULL) {
- struct mdp_overlay req;
- struct fb_info *fbi = mfd->fbi;
struct mdss_mdp_mixer *mixer;
- int ret, bpp;
mixer = mdss_mdp_mixer_get(mdp5_data->ctl,
MDSS_MDP_MIXER_MUX_LEFT);
@@ -1397,49 +1406,73 @@ static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd,
return -ENODEV;
}
- memset(&req, 0, sizeof(req));
+ if (req_ov == NULL) {
+ struct mdp_overlay req;
+ struct fb_info *fbi = mfd->fbi;
+ int bpp;
+
+ memset(&req, 0, sizeof(req));
+
+ bpp = fbi->var.bits_per_pixel / 8;
+ req.id = MSMFB_NEW_REQUEST;
+ req.src.format = mfd->fb_imgType;
+ req.src.height = fbi->var.yres;
+ req.src.width = fbi->fix.line_length / bpp;
+ if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
+ if (req.src.width <= mixer->width) {
+ pr_warn("right fb pipe not needed\n");
+ return -EINVAL;
+ }
- bpp = fbi->var.bits_per_pixel / 8;
- req.id = MSMFB_NEW_REQUEST;
- req.src.format = mfd->fb_imgType;
- req.src.height = fbi->var.yres;
- req.src.width = fbi->fix.line_length / bpp;
- if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
- if (req.src.width <= mixer->width) {
- pr_warn("right fb pipe not needed\n");
- return -EINVAL;
+ req.flags |= MDSS_MDP_RIGHT_MIXER;
+ req.src_rect.x = mixer->width;
+ req.src_rect.w = fbi->var.xres - mixer->width;
+ } else {
+ req.src_rect.x = 0;
+ req.src_rect.w = MIN(fbi->var.xres,
+ mixer->width);
}
- req.flags |= MDSS_MDP_RIGHT_MIXER;
- req.src_rect.x = mixer->width;
- req.src_rect.w = fbi->var.xres - mixer->width;
- } else {
- req.src_rect.x = 0;
- req.src_rect.w = MIN(fbi->var.xres, mixer->width);
- }
-
- req.src_rect.y = 0;
- req.src_rect.h = req.src.height;
- req.dst_rect.x = 0;
- req.dst_rect.y = 0;
- req.dst_rect.w = req.src_rect.w;
- req.dst_rect.h = req.src_rect.h;
- req.z_order = MDSS_MDP_STAGE_BASE;
+ req.src_rect.y = 0;
+ req.src_rect.h = req.src.height;
+ req.dst_rect.x = 0;
+ req.dst_rect.y = 0;
+ req.dst_rect.w = req.src_rect.w;
+ req.dst_rect.h = req.src_rect.h;
+ req.z_order = MDSS_MDP_STAGE_BASE;
- pr_debug("allocating base pipe mux=%d\n", mixer_mux);
+ pr_debug("allocating base pipe mux=%d\n", mixer_mux);
- ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe);
- if (ret)
- return ret;
+ ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe);
+ if (ret)
+ return ret;
+ } else {
+ if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) {
+ req_ov->id = MSMFB_NEW_REQUEST;
+ req_ov->flags |= MDSS_MDP_RIGHT_MIXER;
+ req_ov->src_rect.w = MIN(mixer->width,
+ req_ov->src_rect.w >> 1);
+ req_ov->dst_rect.w = req_ov->src_rect.w;
+ req_ov->src_rect.x = req_ov->src_rect.w;
+ req_ov->dst_rect.x = 0;
+ }
- pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num);
+ ret = mdss_mdp_overlay_pipe_setup(mfd, req_ov, &pipe);
+ if (ret)
+ return ret;
+ }
}
+ pr_debug("ctl=%d pnum=%d\n", mdp5_data->ctl->num, pipe->num);
+
*ppipe = pipe;
return 0;
}
-static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
+static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd,
+ struct mdp_overlay *req,
+ int image_size,
+ int *pipe_ndx)
{
struct mdss_mdp_data *buf;
struct mdss_mdp_pipe *pipe;
@@ -1487,8 +1520,8 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
goto pan_display_error;
}
-
- ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT);
+ ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
+ MDSS_MDP_MIXER_MUX_LEFT, req);
if (ret) {
pr_err("unable to allocate base pipe\n");
goto pan_display_error;
@@ -1498,12 +1531,14 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
pr_err("unable to map base pipe\n");
goto pan_display_error;
}
+ if (pipe_ndx)
+ pipe_ndx[0] = pipe->ndx;
buf = &pipe->back_buf;
if (is_mdss_iommu_attached()) {
if (!mfd->iova) {
pr_err("mfd iova is zero\n");
- goto pan_display_error;
+ goto attach_err;
}
buf->p[0].addr = mfd->iova;
} else {
@@ -1511,24 +1546,28 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
}
buf->p[0].addr += offset;
- buf->p[0].len = fbi->fix.smem_len - offset;
+ if (image_size)
+ buf->p[0].len = image_size;
+ else
+ buf->p[0].len = fbi->fix.smem_len - offset;
buf->num_planes = 1;
- pipe->has_buf = 1;
mdss_mdp_pipe_unmap(pipe);
if (fbi->var.xres > MAX_MIXER_WIDTH || mfd->split_display) {
ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe,
- MDSS_MDP_MIXER_MUX_RIGHT);
+ MDSS_MDP_MIXER_MUX_RIGHT, req);
if (ret) {
pr_err("unable to allocate right base pipe\n");
- goto pan_display_error;
+ goto attach_err;
}
if (mdss_mdp_pipe_map(pipe)) {
pr_err("unable to map right base pipe\n");
- goto pan_display_error;
+ goto attach_err;
}
+ if (pipe_ndx)
+ pipe_ndx[1] = pipe->ndx;
+
pipe->back_buf = *buf;
- pipe->has_buf = 1;
mdss_mdp_pipe_unmap(pipe);
}
mutex_unlock(&mdp5_data->ov_lock);
@@ -1539,6 +1578,12 @@ static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd)
return;
+attach_err:
+ mutex_unlock(&mdp5_data->ov_lock);
+ mdss_mdp_overlay_unset(mfd, pipe->ndx);
+ if (pipe_ndx)
+ pipe_ndx[0] = INVALID_PIPE_INDEX;
+ return;
pan_display_error:
mutex_unlock(&mdp5_data->ov_lock);
}
@@ -1798,6 +1843,19 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
struct fb_image *img = &cursor->image;
u32 blendcfg;
int ret = 0;
+ u32 xres = mfd->fbi->var.xres;
+ u32 yres = mfd->fbi->var.yres;
+ u32 start_x = img->dx;
+ u32 start_y = img->dy;
+ u32 roi_x = 0;
+ u32 roi_y = 0;
+ int roi_w = 0;
+ int roi_h = 0;
+ int roi_size = 0;
+
+ mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
+ if (!mixer)
+ return -ENODEV;
if (!mfd->cursor_buf && (cursor->set & FB_CUR_SETIMAGE)) {
mfd->cursor_buf = dma_alloc_coherent(NULL, MDSS_MDP_CURSOR_SIZE,
@@ -1820,15 +1878,14 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
ret);
return ret;
}
- }
- mixer = mdss_mdp_mixer_get(mdp5_data->ctl, MDSS_MDP_MIXER_MUX_DEFAULT);
- if (!mixer)
- return -ENODEV;
+ mixer->cursor_hotx = 0;
+ mixer->cursor_hoty = 0;
+ }
if ((img->width > MDSS_MDP_CURSOR_WIDTH) ||
(img->height > MDSS_MDP_CURSOR_HEIGHT) ||
- (img->depth != 32))
+ (img->depth != 32) || (start_x >= xres) || (start_y >= yres))
return -EINVAL;
pr_debug("mixer=%d enable=%x set=%x\n", mixer->num, cursor->enable,
@@ -1837,9 +1894,43 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
blendcfg = mdp_mixer_read(mixer, MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG);
- if (cursor->set & FB_CUR_SETPOS)
+ if (cursor->set & FB_CUR_SETHOT) {
+ if ((cursor->hot.x < img->width) &&
+ (cursor->hot.y < img->height)) {
+ mixer->cursor_hotx = cursor->hot.x;
+ mixer->cursor_hoty = cursor->hot.y;
+ /* Update cursor position */
+ cursor->set |= FB_CUR_SETPOS;
+ } else {
+ pr_err("Invalid cursor hotspot coordinates\n");
+ return -EINVAL;
+ }
+ }
+
+ if (start_x > mixer->cursor_hotx) {
+ start_x -= mixer->cursor_hotx;
+ } else {
+ roi_x = mixer->cursor_hotx - start_x;
+ start_x = 0;
+ }
+ if (start_y > mixer->cursor_hoty) {
+ start_y -= mixer->cursor_hoty;
+ } else {
+ roi_y = mixer->cursor_hoty - start_y;
+ start_y = 0;
+ }
+
+ roi_w = min(xres - start_x, img->width - roi_x);
+ roi_h = min(yres - start_y, img->height - roi_y);
+ roi_size = (roi_h << 16) | roi_w;
+
+ if (cursor->set & FB_CUR_SETPOS) {
+ mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_XY,
+ (roi_y << 16) | roi_x);
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_START_XY,
- (img->dy << 16) | img->dx);
+ (start_y << 16) | start_x);
+ mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, roi_size);
+ }
if (cursor->set & FB_CUR_SETIMAGE) {
int calpha_en, transp_en, alpha, size;
@@ -1877,7 +1968,7 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
size = (img->height << 16) | img->width;
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_IMG_SIZE, size);
- mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, size);
+ mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_SIZE, roi_size);
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_STRIDE,
img->width * 4);
mdp_mixer_write(mixer, MDSS_MDP_REG_LM_CURSOR_BASE_ADDR,
@@ -1910,6 +2001,9 @@ static int mdss_mdp_hw_cursor_update(struct msm_fb_data_type *mfd,
MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1,
((img->bg_color & 0xff0000) >> 16));
}
+
+ mixer->cursor_hotx = 0;
+ mixer->cursor_hoty = 0;
}
if (!cursor->enable != !(blendcfg & 0x1)) {
@@ -2617,6 +2711,63 @@ error:
return rc;
}
+static int mdss_mdp_overlay_splash_image(struct msm_fb_data_type *mfd,
+ int *pipe_ndx, int splash_event)
+{
+ struct mdp_overlay req;
+ int rc = 0;
+ struct fb_info *fbi = NULL;
+ int image_len = 0;
+
+ if (!mfd || !mfd->fbi || !mfd->fbi->screen_base || !pipe_ndx) {
+ pr_err("Invalid input parameter\n");
+ return -EINVAL;
+ }
+
+ fbi = mfd->fbi;
+ image_len = SPLASH_IMAGE_WIDTH * SPLASH_IMAGE_HEIGHT * SPLASH_IMAGE_BPP;
+
+ if (SPLASH_IMAGE_WIDTH > fbi->var.xres ||
+ SPLASH_IMAGE_HEIGHT > fbi->var.yres ||
+ SPLASH_IMAGE_BPP > fbi->var.bits_per_pixel / 8 ||
+ image_len > fbi->fix.smem_len) {
+ pr_err("Invalid splash parameter configuration\n");
+ return -EINVAL;
+ }
+
+ if (splash_event == MDP_CREATE_SPLASH_OV) {
+ pipe_ndx[0] = INVALID_PIPE_INDEX;
+ pipe_ndx[1] = INVALID_PIPE_INDEX;
+
+ memset(&req, 0, sizeof(struct mdp_overlay));
+ req.src.width = req.dst_rect.w = req.src_rect.w =
+ SPLASH_IMAGE_WIDTH;
+ req.src.height = req.dst_rect.h = req.src_rect.h =
+ SPLASH_IMAGE_HEIGHT;
+ req.src.format = SPLASH_IMAGE_FORMAT;
+ req.id = MSMFB_NEW_REQUEST;
+ req.z_order = MDSS_MDP_STAGE_0;
+ req.is_fg = 1;
+ req.alpha = 0xff;
+ req.transp_mask = MDP_TRANSP_NOP;
+ req.dst_rect.x =
+ (fbi->var.xres >> 1) - (SPLASH_IMAGE_WIDTH >> 1);
+ req.dst_rect.y =
+ (fbi->var.yres >> 1) - (SPLASH_IMAGE_HEIGHT >> 1);
+
+ memcpy(fbi->screen_base, splash_bgr888_image, image_len);
+ mdss_mdp_overlay_pan_display(mfd, &req, image_len, pipe_ndx);
+
+ } else if (splash_event == MDP_REMOVE_SPLASH_OV) {
+ if (pipe_ndx[0] != INVALID_PIPE_INDEX)
+ mdss_mdp_overlay_unset(mfd, pipe_ndx[0]);
+ if (pipe_ndx[1] != INVALID_PIPE_INDEX)
+ mdss_mdp_overlay_unset(mfd, pipe_ndx[1]);
+ }
+
+ return rc;
+}
+
int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
{
struct device *dev = mfd->fbi->dev;
@@ -2634,6 +2785,7 @@ int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd)
mdp5_interface->panel_register_done = mdss_panel_register_done;
mdp5_interface->kickoff_fnc = mdss_mdp_overlay_kickoff;
mdp5_interface->get_sync_fnc = mdss_mdp_rotator_sync_pt_get;
+ mdp5_interface->splash_fnc = mdss_mdp_overlay_splash_image;
mdp5_data = kmalloc(sizeof(struct mdss_overlay_private), GFP_KERNEL);
if (!mdp5_data) {
@@ -2728,7 +2880,7 @@ static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd)
int len = 0, rc = 0;
u32 offsets[2];
- of_find_property(pdev->dev.of_node, "qti,memblock-reserve", &len);
+ of_find_property(pdev->dev.of_node, "qcom,memblock-reserve", &len);
if (len < 1) {
pr_debug("mem reservation for splash screen fb not present\n");
@@ -2739,7 +2891,7 @@ static __ref int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd)
len = len/sizeof(u32);
rc = of_property_read_u32_array(pdev->dev.of_node,
- "qti,memblock-reserve", offsets, len);
+ "qcom,memblock-reserve", offsets, len);
if (rc) {
pr_debug("Error reading mem reserve settings for fb\n");
goto error;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c
index 74c7df38cbda..4cbd65ba250e 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pipe.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c
@@ -166,7 +166,7 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe)
struct mdss_mdp_plane_sizes ps;
int i;
int rc = 0, rot_mode = 0;
- u32 nlines, format;
+ u32 nlines, format, seg_w;
u16 width;
width = pipe->src.w >> pipe->horz_deci;
@@ -178,19 +178,17 @@ int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe)
return rc;
/*
* Override fetch strides with SMP buffer size for both the
- * planes
+ * planes. BWC line buffer needs to be divided into 16
+ * segments and every segment is aligned to format
+ * specific RAU size
*/
+ seg_w = DIV_ROUND_UP(pipe->src.w, 16);
if (pipe->src_fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) {
- /*
- * BWC line buffer needs to be divided into 16
- * segments and every segment is aligned to format
- * specific RAU size
- */
- ps.ystride[0] = ALIGN(pipe->src.w / 16 , 32) * 16 *
- ps.rau_h[0] * pipe->src_fmt->bpp;
+ ps.ystride[0] = ALIGN(seg_w, 32) * 16 * ps.rau_h[0] *
+ pipe->src_fmt->bpp;
ps.ystride[1] = 0;
} else {
- u32 bwc_width = ALIGN(pipe->src.w / 16, 64) * 16;
+ u32 bwc_width = ALIGN(seg_w, 64) * 16;
ps.ystride[0] = bwc_width * ps.rau_h[0];
ps.ystride[1] = bwc_width * ps.rau_h[1];
/*
@@ -1008,7 +1006,7 @@ int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe,
(pipe->mixer->type == MDSS_MDP_MIXER_TYPE_WRITEBACK)
&& (ctl->mdata->mixer_switched)) ||
ctl->roi_changed;
- if (src_data == NULL || !pipe->has_buf) {
+ if (src_data == NULL || (pipe->flags & MDP_SOLID_FILL)) {
pipe->params_changed = 0;
mdss_mdp_pipe_solidfill_setup(pipe);
goto update_nobuf;
diff --git a/drivers/video/msm/mdss/mdss_mdp_pp.c b/drivers/video/msm/mdss/mdss_mdp_pp.c
index cd65bbe33c61..5b5e2de99de0 100644
--- a/drivers/video/msm/mdss/mdss_mdp_pp.c
+++ b/drivers/video/msm/mdss/mdss_mdp_pp.c
@@ -214,6 +214,10 @@ static u32 igc_limited[IGC_LUT_ENTRIES] = {
#define PP_AD_BAD_HW_NUM 255
+#define MDSS_SIDE_NONE 0
+#define MDSS_SIDE_LEFT 1
+#define MDSS_SIDE_RIGHT 2
+
#define PP_AD_STATE_INIT 0x2
#define PP_AD_STATE_CFG 0x4
#define PP_AD_STATE_DATA 0x8
@@ -302,8 +306,8 @@ struct mdss_pp_res_type {
struct mdp_gamut_cfg_data gamut_disp_cfg[MDSS_BLOCK_DISP_NUM];
uint16_t gamut_tbl[MDSS_BLOCK_DISP_NUM][GAMUT_TOTAL_TABLE_SIZE];
u32 hist_data[MDSS_BLOCK_DISP_NUM][HIST_V_SIZE];
- /* physical info */
struct pp_sts_type pp_disp_sts[MDSS_BLOCK_DISP_NUM];
+ /* physical info */
struct pp_hist_col_info dspp_hist[MDSS_MDP_MAX_DSPP];
};
@@ -313,8 +317,7 @@ static struct mdss_pp_res_type *mdss_pp_res;
static u32 pp_hist_read(char __iomem *v_addr,
struct pp_hist_col_info *hist_info);
static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix);
-static int pp_hist_disable(struct pp_hist_col_info *hist_info,
- u32 done_bit, char __iomem *ctl_base);
+static int pp_hist_disable(struct pp_hist_col_info *hist_info);
static void pp_update_pcc_regs(char __iomem *addr,
struct mdp_pcc_cfg_data *cfg_ptr);
static void pp_update_igc_lut(struct mdp_igc_lut_data *cfg,
@@ -350,8 +353,9 @@ static void pp_enhist_config(unsigned long flags, char __iomem *addr,
static void pp_dither_config(char __iomem *addr,
struct pp_sts_type *pp_sts,
struct mdp_dither_cfg_data *dither_cfg);
-static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode,
- int mdp_rev);
+static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num,
+ struct pp_sts_type *pp_sts, int mdp_rev,
+ u32 *opmode);
static void pp_sharp_config(char __iomem *addr,
struct pp_sts_type *pp_sts,
struct mdp_sharp_cfg *sharp_config);
@@ -385,11 +389,15 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw,
struct mdss_ad_info *ad, struct mdss_mdp_ctl *ctl);
static void pp_ad_input_write(struct mdss_mdp_ad *ad_hw,
struct mdss_ad_info *ad);
-static void pp_ad_bypass_config(struct mdss_ad_info *ad, u32 *opmode);
static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd,
struct mdss_ad_info *ad);
+static void pp_ad_bypass_config(struct mdss_ad_info *ad,
+ struct mdss_mdp_ctl *ctl, u32 num, u32 *opmode);
static int mdss_mdp_ad_setup(struct msm_fb_data_type *mfd);
static void pp_ad_cfg_lut(char __iomem *addr, u32 *data);
+static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num);
+static inline bool pp_sts_is_enabled(u32 sts, int side);
+static inline void pp_sts_set_split_bits(u32 *sts, u32 bits);
static u32 last_sts, last_state;
@@ -528,6 +536,7 @@ static void pp_gamut_config(struct mdp_gamut_cfg_data *gamut_cfg,
pp_sts->gamut_sts &= ~PP_STS_ENABLE;
else if (gamut_cfg->flags & MDP_PP_OPS_ENABLE)
pp_sts->gamut_sts |= PP_STS_ENABLE;
+ pp_sts_set_split_bits(&pp_sts->gamut_sts, gamut_cfg->flags);
}
static void pp_pa_config(unsigned long flags, char __iomem *addr,
@@ -696,6 +705,7 @@ static void pp_update_pa_v2_sts(struct pp_sts_type *pp_sts,
if (pa_v2_config->flags & MDP_PP_PA_SIX_ZONE_VAL_MASK)
pp_sts->pa_sts |= PP_STS_PA_SIX_ZONE_VAL_MASK;
+ pp_sts_set_split_bits(&pp_sts->pa_sts, pa_v2_config->flags);
}
static void pp_pcc_config(unsigned long flags, char __iomem *addr,
@@ -710,6 +720,7 @@ static void pp_pcc_config(unsigned long flags, char __iomem *addr,
pp_sts->pcc_sts &= ~PP_STS_ENABLE;
else if (pcc_config->ops & MDP_PP_OPS_ENABLE)
pp_sts->pcc_sts |= PP_STS_ENABLE;
+ pp_sts_set_split_bits(&pp_sts->pcc_sts, pcc_config->ops);
}
}
@@ -737,6 +748,7 @@ static void pp_igc_config(unsigned long flags, char __iomem *addr,
pp_sts->igc_sts &= ~PP_STS_ENABLE;
else if (igc_config->ops & MDP_PP_OPS_ENABLE)
pp_sts->igc_sts |= PP_STS_ENABLE;
+ pp_sts_set_split_bits(&pp_sts->igc_sts, igc_config->ops);
}
}
@@ -1086,7 +1098,7 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe)
/*program pixel extn values for the SSPP*/
mdss_mdp_pipe_program_pixel_extn(pipe);
- } else {
+ } else if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) {
writel_relaxed(phasex_step, pipe->base +
MDSS_MDP_REG_SCALE_PHASE_STEP_X);
writel_relaxed(phasey_step, pipe->base +
@@ -1095,6 +1107,11 @@ static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe)
MDSS_MDP_REG_SCALE_INIT_PHASE_X);
writel_relaxed(init_phasey, pipe->base +
MDSS_MDP_REG_SCALE_INIT_PHASE_Y);
+ } else {
+ writel_relaxed(phasex_step, pipe->base +
+ MDSS_MDP_REG_SCALE_PHASE_STEP_X);
+ writel_relaxed(phasey_step, pipe->base +
+ MDSS_MDP_REG_SCALE_PHASE_STEP_Y);
}
writel_relaxed(scale_config, pipe->base +
@@ -1121,17 +1138,12 @@ int mdss_mdp_pipe_pp_setup(struct mdss_mdp_pipe *pipe, u32 *op)
void mdss_mdp_pipe_sspp_term(struct mdss_mdp_pipe *pipe)
{
- u32 done_bit;
struct pp_hist_col_info *hist_info;
- char __iomem *ctl_base;
if (pipe) {
if (pipe->pp_res.hist.col_en) {
- done_bit = 3 << (pipe->num * 4);
hist_info = &pipe->pp_res.hist;
- ctl_base = pipe->base +
- MDSS_MDP_REG_VIG_HIST_CTL_BASE;
- pp_hist_disable(hist_info, done_bit, ctl_base);
+ pp_hist_disable(hist_info);
}
memset(&pipe->pp_cfg, 0, sizeof(struct mdp_overlay_pp_params));
memset(&pipe->pp_res, 0, sizeof(struct mdss_pipe_pp_res));
@@ -1271,19 +1283,21 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
int ret = -EINVAL;
char __iomem *base;
u32 op_flags, kick_base, col_state;
- struct mdss_data_type *mdata;
struct mdss_mdp_pipe *pipe;
struct pp_hist_col_info *hist_info;
unsigned long flag;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v1 = !(mdata->mdp_rev >= MDSS_MDP_HW_REV_103);
if (mix && (PP_LOCAT(block) == MDSS_PP_DSPP_CFG)) {
/* HIST_EN & AUTO_CLEAR */
- op_flags = BIT(16) | BIT(17);
+ op_flags = BIT(16);
+ if (is_hist_v1)
+ op_flags |= BIT(17);
hist_info = &mdss_pp_res->dspp_hist[mix->num];
base = mdss_mdp_get_dspp_addr_off(PP_BLOCK(block));
kick_base = MDSS_MDP_REG_DSPP_HIST_CTL_BASE;
- } else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG) {
- mdata = mdss_mdp_get_mdata();
+ } else if (PP_LOCAT(block) == MDSS_PP_SSPP_CFG && is_hist_v1) {
pipe = mdss_mdp_pipe_get(mdata, BIT(PP_BLOCK(block)));
if (IS_ERR_OR_NULL(pipe)) {
pr_debug("pipe DNE (%d)", (u32) BIT(PP_BLOCK(block)));
@@ -1297,7 +1311,6 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
kick_base = MDSS_MDP_REG_VIG_HIST_CTL_BASE;
mdss_mdp_pipe_unmap(pipe);
} else {
- pr_warn("invalid histogram location (%d)", block);
goto error;
}
@@ -1306,12 +1319,10 @@ static int pp_hist_setup(u32 *op, u32 block, struct mdss_mdp_mixer *mix)
mutex_lock(&hist_info->hist_mutex);
spin_lock_irqsave(&hist_info->hist_lock, flag);
col_state = hist_info->col_state;
- if (hist_info->is_kick_ready &&
- ((col_state == HIST_IDLE) ||
- ((false == hist_info->read_request) &&
- col_state == HIST_READY))) {
+ if (col_state == HIST_IDLE) {
/* Kick off collection */
- writel_relaxed(1, base + kick_base);
+ if (is_hist_v1)
+ writel_relaxed(1, base + kick_base);
hist_info->col_state = HIST_START;
}
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
@@ -1348,12 +1359,20 @@ static void pp_dither_config(char __iomem *addr,
pp_sts->dither_sts &= ~PP_STS_ENABLE;
else if (dither_cfg->flags & MDP_PP_OPS_ENABLE)
pp_sts->dither_sts |= PP_STS_ENABLE;
+ pp_sts_set_split_bits(&pp_sts->dither_sts, dither_cfg->flags);
}
-static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode,
- int mdp_rev)
+static void pp_dspp_opmode_config(struct mdss_mdp_ctl *ctl, u32 num,
+ struct pp_sts_type *pp_sts, int mdp_rev,
+ u32 *opmode)
{
- if (pp_sts->pa_sts & PP_STS_ENABLE)
+ int side;
+ side = pp_num_to_side(ctl, num);
+
+ if (side < 0)
+ return;
+
+ if (pp_sts_is_enabled(pp_sts->pa_sts, side))
*opmode |= MDSS_MDP_DSPP_OP_PA_EN; /* PA_EN */
if (mdp_rev >= MDSS_MDP_HW_REV_103) {
if (pp_sts->pa_sts & PP_STS_PA_HUE_MASK)
@@ -1381,10 +1400,10 @@ static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode,
if (pp_sts->pa_sts & PP_STS_PA_SIX_ZONE_VAL_MASK)
*opmode |= MDSS_MDP_DSPP_OP_PA_SIX_ZONE_VAL_MASK;
}
- if (pp_sts->pcc_sts & PP_STS_ENABLE)
+ if (pp_sts_is_enabled(pp_sts->pcc_sts, side))
*opmode |= MDSS_MDP_DSPP_OP_PCC_EN; /* PCC_EN */
- if (pp_sts->igc_sts & PP_STS_ENABLE) {
+ if (pp_sts_is_enabled(pp_sts->igc_sts, side)) {
*opmode |= MDSS_MDP_DSPP_OP_IGC_LUT_EN | /* IGC_LUT_EN */
(pp_sts->igc_tbl_idx << 1);
}
@@ -1392,14 +1411,14 @@ static void pp_dspp_opmode_config(struct pp_sts_type *pp_sts, u32 *opmode,
*opmode |= MDSS_MDP_DSPP_OP_HIST_LUTV_EN | /* HIST_LUT_EN */
MDSS_MDP_DSPP_OP_PA_EN; /* PA_EN */
}
- if (pp_sts->dither_sts & PP_STS_ENABLE)
+ if (pp_sts_is_enabled(pp_sts->dither_sts, side))
*opmode |= MDSS_MDP_DSPP_OP_DST_DITHER_EN; /* DITHER_EN */
- if (pp_sts->gamut_sts & PP_STS_ENABLE) {
+ if (pp_sts_is_enabled(pp_sts->gamut_sts, side)) {
*opmode |= MDSS_MDP_DSPP_OP_GAMUT_EN; /* GAMUT_EN */
if (pp_sts->gamut_sts & PP_STS_GAMUT_FIRST)
*opmode |= MDSS_MDP_DSPP_OP_GAMUT_PCC_ORDER;
}
- if (pp_sts->pgc_sts & PP_STS_ENABLE)
+ if (pp_sts_is_enabled(pp_sts->pgc_sts, side))
*opmode |= MDSS_MDP_DSPP_OP_ARGC_LUT_EN;
}
@@ -1505,8 +1524,12 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
pp_sts->pgc_sts &= ~PP_STS_ENABLE;
else if (pgc_config->flags & MDP_PP_OPS_ENABLE)
pp_sts->pgc_sts |= PP_STS_ENABLE;
+ pp_sts_set_split_bits(&pp_sts->pgc_sts, pgc_config->flags);
}
+ pp_dspp_opmode_config(ctl, dspp_num, pp_sts, mdata->mdp_rev, &opmode);
+
+flush_exit:
if (ad_hw) {
mutex_lock(&ad->lock);
ad_flags = ad->reg_sts;
@@ -1516,13 +1539,11 @@ static int pp_dspp_setup(u32 disp_num, struct mdss_mdp_mixer *mixer)
pp_ad_init_write(ad_hw, ad, ctl);
if (ad_flags & PP_AD_STS_DIRTY_CFG)
pp_ad_cfg_write(ad_hw, ad);
- pp_ad_bypass_config(ad, &ad_bypass);
+ pp_ad_bypass_config(ad, ctl, ad_hw->num, &ad_bypass);
writel_relaxed(ad_bypass, ad_hw->base);
mutex_unlock(&ad->lock);
}
- pp_dspp_opmode_config(pp_sts, &opmode, mdata->mdp_rev);
-flush_exit:
writel_relaxed(opmode, base + MDSS_MDP_REG_DSPP_OP_MODE);
if (dspp_num == MDSS_MDP_DSPP3)
@@ -1868,6 +1889,12 @@ int mdss_mdp_pa_v2_config(struct mdp_pa_v2_cfg_data *config,
(config->block >= MDP_BLOCK_MAX))
return -EINVAL;
+ if ((config->pa_v2_data.flags & MDSS_PP_SPLIT_MASK) ==
+ MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
@@ -2157,6 +2184,11 @@ int mdss_mdp_pcc_config(struct mdp_pcc_cfg_data *config,
(config->block >= MDP_BLOCK_MAX))
return -EINVAL;
+ if ((config->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
@@ -2276,6 +2308,11 @@ int mdss_mdp_igc_lut_config(struct mdp_igc_lut_data *config,
if (config->len != IGC_LUT_ENTRIES)
return -EINVAL;
+ if ((config->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
@@ -2475,6 +2512,11 @@ int mdss_mdp_argc_config(struct mdp_pgc_lut_data *config,
(PP_BLOCK(config->block) >= MDP_BLOCK_MAX))
return -EINVAL;
+ if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = PP_BLOCK(config->block) - MDP_LOGICAL_BLOCK_DISP_0;
@@ -2646,6 +2688,11 @@ int mdss_mdp_dither_config(struct mdp_dither_cfg_data *config,
if (config->flags & MDP_PP_OPS_READ)
return -ENOTSUPP;
+ if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
mdss_pp_res->dither_disp_cfg[disp_num] = *config;
@@ -2696,6 +2743,11 @@ int mdss_mdp_gamut_config(struct mdp_gamut_cfg_data *config,
if (pp_gm_has_invalid_lut_size(config))
return -EINVAL;
+ if ((config->flags & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&mdss_pp_mutex);
disp_num = config->block - MDP_LOGICAL_BLOCK_DISP_0;
@@ -2837,18 +2889,19 @@ static u32 pp_hist_read(char __iomem *v_addr,
/* Assumes that relevant clocks are enabled */
static int pp_hist_enable(struct pp_hist_col_info *hist_info,
- struct mdp_histogram_start_req *req,
- u32 shift_bit, char __iomem *ctl_base)
+ struct mdp_histogram_start_req *req)
{
unsigned long flag;
int ret = 0;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103;
+ u32 intr_mask = is_hist_v2 ? 1 : 3;
mutex_lock(&hist_info->hist_mutex);
/* check if it is idle */
if (hist_info->col_en) {
pr_info("%s Hist collection has already been enabled %d",
- __func__, (u32) ctl_base);
+ __func__, (u32) hist_info->base);
ret = -EINVAL;
goto exit;
}
@@ -2858,15 +2911,23 @@ static int pp_hist_enable(struct pp_hist_col_info *hist_info,
hist_info->hist_cnt_sent = 0;
hist_info->hist_cnt_time = 0;
spin_lock_irqsave(&hist_info->hist_lock, flag);
- hist_info->read_request = false;
- hist_info->col_state = HIST_RESET;
+ hist_info->read_request = 0;
+ if (is_hist_v2)
+ hist_info->col_state = HIST_IDLE;
+ else
+ hist_info->col_state = HIST_RESET;
hist_info->col_en = true;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
- hist_info->is_kick_ready = true;
- mdss_mdp_hist_intr_req(&mdata->hist_intr, 3 << shift_bit, true);
- writel_relaxed(req->frame_cnt, ctl_base + 8);
- /* Kick out reset start */
- writel_relaxed(1, ctl_base + 4);
+ mdss_mdp_hist_intr_req(&mdata->hist_intr,
+ intr_mask << hist_info->intr_shift, true);
+ if (is_hist_v2) {
+ /* if hist v2, make sure HW is unlocked */
+ writel_relaxed(0, hist_info->base);
+ } else {
+ writel_relaxed(req->frame_cnt, hist_info->base + 8);
+ /* Kick out reset start */
+ writel_relaxed(1, hist_info->base + 4);
+ }
exit:
mutex_unlock(&hist_info->hist_mutex);
return ret;
@@ -2875,8 +2936,6 @@ exit:
#define MDSS_MAX_HIST_BIN_SIZE 16777215
int mdss_mdp_hist_start(struct mdp_histogram_start_req *req)
{
- u32 done_shift_bit;
- char __iomem *ctl_base;
struct pp_hist_col_info *hist_info;
int i, ret = 0;
u32 disp_num, dspp_num = 0;
@@ -2884,6 +2943,7 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req)
u32 frame_size;
struct mdss_mdp_pipe *pipe;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103;
if (!mdss_is_ready())
return -EPROBE_DEFER;
@@ -2917,6 +2977,12 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req)
ret = -EINVAL;
goto hist_exit;
}
+ if (is_hist_v2 && (PP_LOCAT(req->block) == MDSS_PP_SSPP_CFG)) {
+ pr_warn("No histogram on SSPP\n");
+ ret = -EINVAL;
+ goto hist_exit;
+ }
+
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
if (PP_LOCAT(req->block) == MDSS_PP_SSPP_CFG) {
@@ -2938,23 +3004,21 @@ int mdss_mdp_hist_start(struct mdp_histogram_start_req *req)
pr_warn("Invalid Hist pipe (%d)", i);
goto hist_stop_clk;
}
- done_shift_bit = (pipe->num * 4);
hist_info = &pipe->pp_res.hist;
- ctl_base = pipe->base +
+ hist_info->intr_shift = (pipe->num * 4);
+ hist_info->base = pipe->base +
MDSS_MDP_REG_VIG_HIST_CTL_BASE;
- ret = pp_hist_enable(hist_info, req,
- done_shift_bit, ctl_base);
+ ret = pp_hist_enable(hist_info, req);
mdss_mdp_pipe_unmap(pipe);
}
} else if (PP_LOCAT(req->block) == MDSS_PP_DSPP_CFG) {
for (i = 0; i < mixer_cnt; i++) {
dspp_num = mixer_id[i];
- done_shift_bit = (dspp_num * 4) + 12;
hist_info = &mdss_pp_res->dspp_hist[dspp_num];
- ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) +
+ hist_info->intr_shift = (dspp_num * 4) + 12;
+ hist_info->base = mdss_mdp_get_dspp_addr_off(dspp_num) +
MDSS_MDP_REG_DSPP_HIST_CTL_BASE;
- ret = pp_hist_enable(hist_info, req,
- done_shift_bit, ctl_base);
+ ret = pp_hist_enable(hist_info, req);
mdss_pp_res->pp_disp_flags[disp_num] |=
PP_FLAGS_DIRTY_HIST_COL;
}
@@ -2965,16 +3029,18 @@ hist_exit:
return ret;
}
-static int pp_hist_disable(struct pp_hist_col_info *hist_info,
- u32 done_bit, char __iomem *ctl_base)
+static int pp_hist_disable(struct pp_hist_col_info *hist_info)
{
int ret = 0;
unsigned long flag;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103;
+ u32 intr_mask = is_hist_v2 ? 1 : 3;
mutex_lock(&hist_info->hist_mutex);
if (hist_info->col_en == false) {
- pr_debug("Histogram already disabled (%d)", (u32) ctl_base);
+ pr_debug("Histogram already disabled (%d)",
+ (u32) hist_info->base);
ret = -EINVAL;
goto exit;
}
@@ -2983,9 +3049,13 @@ static int pp_hist_disable(struct pp_hist_col_info *hist_info,
hist_info->col_en = false;
hist_info->col_state = HIST_UNKNOWN;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
- hist_info->is_kick_ready = false;
- mdss_mdp_hist_intr_req(&mdata->hist_intr, done_bit, false);
- writel_relaxed(BIT(1), ctl_base);/* cancel */
+ mdss_mdp_hist_intr_req(&mdata->hist_intr,
+ intr_mask << hist_info->intr_shift, false);
+ /* if hist v2, make sure HW is unlocked */
+ if (is_hist_v2)
+ writel_relaxed(0, hist_info->base);
+ else
+ writel_relaxed(BIT(1), hist_info->base);/* cancel */
ret = 0;
exit:
mutex_unlock(&hist_info->hist_mutex);
@@ -2995,8 +3065,7 @@ exit:
int mdss_mdp_hist_stop(u32 block)
{
int i, ret = 0;
- char __iomem *ctl_base;
- u32 dspp_num, disp_num, done_bit;
+ u32 dspp_num, disp_num;
struct pp_hist_col_info *hist_info;
u32 mixer_cnt, mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
struct mdss_mdp_pipe *pipe;
@@ -3038,12 +3107,8 @@ int mdss_mdp_hist_stop(u32 block)
pr_warn("Invalid Hist pipe (%d)", i);
continue;
}
- done_bit = 3 << (pipe->num * 4);
hist_info = &pipe->pp_res.hist;
- ctl_base = pipe->base +
- MDSS_MDP_REG_VIG_HIST_CTL_BASE;
- ret = pp_hist_disable(hist_info, done_bit,
- ctl_base);
+ ret = pp_hist_disable(hist_info);
mdss_mdp_pipe_unmap(pipe);
if (ret)
goto hist_stop_clk;
@@ -3051,12 +3116,8 @@ int mdss_mdp_hist_stop(u32 block)
} else if (PP_LOCAT(block) == MDSS_PP_DSPP_CFG) {
for (i = 0; i < mixer_cnt; i++) {
dspp_num = mixer_id[i];
- done_bit = 3 << ((dspp_num * 4) + 12);
hist_info = &mdss_pp_res->dspp_hist[dspp_num];
- ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) +
- MDSS_MDP_REG_DSPP_HIST_CTL_BASE;
- ret = pp_hist_disable(hist_info, done_bit,
- ctl_base);
+ ret = pp_hist_disable(hist_info);
if (ret)
goto hist_stop_clk;
mdss_pp_res->pp_disp_flags[disp_num] |=
@@ -3223,6 +3284,8 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
unsigned long flag;
struct mdss_pipe_pp_res *res;
struct mdss_mdp_pipe *pipe;
+ struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103;
mutex_lock(&hist_info->hist_mutex);
if ((hist_info->col_en == 0) ||
@@ -3233,7 +3296,6 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
spin_lock_irqsave(&hist_info->hist_lock, flag);
/* wait for hist done if cache has no data */
if (hist_info->col_state != HIST_READY) {
- hist_info->read_request = true;
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
timeout = HIST_WAIT_TIMEOUT(hist_info->frame_cnt);
mutex_unlock(&hist_info->hist_mutex);
@@ -3273,9 +3335,11 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
}
if (hist_info->col_state != HIST_READY) {
ret = -ENODATA;
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
+ hist_info->col_state = HIST_READY;
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
pr_debug("%s: state is not ready: %d",
__func__, hist_info->col_state);
- goto hist_collect_exit;
}
} else {
spin_unlock_irqrestore(&hist_info->hist_lock, flag);
@@ -3286,11 +3350,12 @@ static int pp_hist_collect(struct mdp_histogram_data *hist,
v_base = ctl_base + 0x1C;
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);
sum = pp_hist_read(v_base, hist_info);
+ /* if hist_v2 unlock HW when done reading */
+ if (is_hist_v2)
+ writel_relaxed(0, ctl_base);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false);
spin_lock_irqsave(&hist_info->hist_lock, flag);
- if (!expect_sum || sum == expect_sum)
- hist_info->read_request = false;
- else
+ if (expect_sum && sum != expect_sum)
ret = -ENODATA;
hist_info->col_state = HIST_IDLE;
}
@@ -3302,8 +3367,9 @@ hist_collect_exit:
int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
{
- int i, j, off, ret = 0;
+ int i, j, off, ret = 0, temp_ret = 0;
struct pp_hist_col_info *hist_info;
+ struct pp_hist_col_info *hists[MDSS_MDP_INTF_MAX_LAYERMIXER];
u32 dspp_num, disp_num;
char __iomem *ctl_base;
u32 hist_cnt, mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
@@ -3314,6 +3380,7 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
u32 exp_sum = 0;
struct mdss_mdp_pipe *pipe;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ unsigned long flag;
if ((PP_BLOCK(hist->block) < MDP_LOGICAL_BLOCK_DISP_0) ||
(PP_BLOCK(hist->block) >= MDP_BLOCK_MAX))
@@ -3334,20 +3401,41 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
ret = -EPERM;
goto hist_collect_exit;
}
+
if (PP_LOCAT(hist->block) == MDSS_PP_DSPP_CFG) {
- hist_info = &mdss_pp_res->dspp_hist[disp_num];
for (i = 0; i < hist_cnt; i++) {
dspp_num = mixer_id[i];
- hist_info = &mdss_pp_res->dspp_hist[dspp_num];
+ hists[i] = &mdss_pp_res->dspp_hist[dspp_num];
+ }
+ for (i = 0; i < hist_cnt; i++) {
+ spin_lock_irqsave(&hists[i]->hist_lock, flag);
+ /* mark that collect is ready to handle completions */
+ hists[i]->read_request = 1;
+ spin_unlock_irqrestore(&hists[i]->hist_lock, flag);
+ }
+ for (i = 0; i < hist_cnt; i++) {
+ dspp_num = mixer_id[i];
ctl_base = mdss_mdp_get_dspp_addr_off(dspp_num) +
MDSS_MDP_REG_DSPP_HIST_CTL_BASE;
exp_sum = (mdata->mixer_intf[dspp_num].width *
mdata->mixer_intf[dspp_num].height);
- ret = pp_hist_collect(hist, hist_info, ctl_base,
- exp_sum);
if (ret)
- goto hist_collect_exit;
+ temp_ret = ret;
+ ret = pp_hist_collect(hist, hists[i], ctl_base,
+ exp_sum);
}
+ for (i = 0; i < hist_cnt; i++) {
+ /* reset read requests and re-intialize completions */
+ spin_lock_irqsave(&hists[i]->hist_lock, flag);
+ hists[i]->read_request = 0;
+ INIT_COMPLETION(hists[i]->comp);
+ spin_unlock_irqrestore(&hists[i]->hist_lock, flag);
+ }
+ if (ret || temp_ret) {
+ ret = ret ? ret : temp_ret;
+ goto hist_collect_exit;
+ }
+
if (hist->bin_cnt != HIST_V_SIZE) {
pr_err("User not expecting size %d output",
HIST_V_SIZE);
@@ -3363,19 +3451,19 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
}
memset(hist_concat, 0, HIST_V_SIZE * sizeof(u32));
for (i = 0; i < hist_cnt; i++) {
- dspp_num = mixer_id[i];
- hist_info = &mdss_pp_res->dspp_hist[dspp_num];
- mutex_lock(&hist_info->hist_mutex);
+ mutex_lock(&hists[i]->hist_mutex);
for (j = 0; j < HIST_V_SIZE; j++)
- hist_concat[j] += hist_info->data[j];
- mutex_unlock(&hist_info->hist_mutex);
+ hist_concat[j] += hists[i]->data[j];
+ mutex_unlock(&hists[i]->hist_mutex);
}
hist_data_addr = hist_concat;
} else {
- hist_data_addr = hist_info->data;
+ hist_data_addr = hists[0]->data;
}
- hist_info = &mdss_pp_res->dspp_hist[disp_num];
- hist_info->hist_cnt_sent++;
+
+ for (i = 0; i < hist_cnt; i++)
+ hists[i]->hist_cnt_sent++;
+
} else if (PP_LOCAT(hist->block) == MDSS_PP_SSPP_CFG) {
hist_cnt = MDSS_PP_ARG_MASK & hist->block;
@@ -3411,14 +3499,50 @@ int mdss_mdp_hist_collect(struct mdp_histogram_data *hist)
continue;
}
hist_info = &pipe->pp_res.hist;
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
+ hist_info->read_request = 1;
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ }
+ for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) {
+ if (!PP_ARG(i, hist->block))
+ continue;
+ pipe_cnt++;
+ pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ if (IS_ERR_OR_NULL(pipe) ||
+ pipe->num > MDSS_MDP_SSPP_VIG2) {
+ pr_warn("Invalid Hist pipe (%d)", i);
+ continue;
+ }
+ hist_info = &pipe->pp_res.hist;
ctl_base = pipe->base +
MDSS_MDP_REG_VIG_HIST_CTL_BASE;
+ if (ret)
+ temp_ret = ret;
ret = pp_hist_collect(hist, hist_info, ctl_base,
exp_sum);
mdss_mdp_pipe_unmap(pipe);
- if (ret)
- goto hist_collect_exit;
}
+ for (i = pipe_num; i < MDSS_PP_ARG_NUM; i++) {
+ if (!PP_ARG(i, hist->block))
+ continue;
+ pipe_cnt++;
+ pipe = mdss_mdp_pipe_get(mdata, BIT(i));
+ if (IS_ERR_OR_NULL(pipe) ||
+ pipe->num > MDSS_MDP_SSPP_VIG2) {
+ pr_warn("Invalid Hist pipe (%d)", i);
+ continue;
+ }
+ hist_info = &pipe->pp_res.hist;
+ spin_lock_irqsave(&hist_info->hist_lock, flag);
+ hist_info->read_request = 0;
+ INIT_COMPLETION(hist_info->comp);
+ spin_unlock_irqrestore(&hist_info->hist_lock, flag);
+ }
+ if (ret || temp_ret) {
+ ret = ret ? ret : temp_ret;
+ goto hist_collect_exit;
+ }
+
if (pipe_cnt != 0 &&
(hist->bin_cnt != (HIST_V_SIZE * pipe_cnt))) {
pr_err("User not expecting size %d output",
@@ -3472,6 +3596,8 @@ void mdss_mdp_hist_intr_done(u32 isr)
struct pp_hist_col_info *hist_info = NULL;
struct mdss_mdp_pipe *pipe;
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
+ bool is_hist_v2 = mdata->mdp_rev >= MDSS_MDP_HW_REV_103;
+ bool need_complete = false;
isr &= 0x333333;
while (isr != 0) {
if (isr & 0xFFF000) {
@@ -3513,9 +3639,18 @@ void mdss_mdp_hist_intr_done(u32 isr)
/* Histogram Done Interrupt */
if (hist_info && (isr_blk & 0x1) && (hist_info->col_en)) {
spin_lock(&hist_info->hist_lock);
- hist_info->col_state = HIST_READY;
+ if (!is_hist_v2)
+ hist_info->col_state = HIST_READY;
+ if (hist_info->read_request == 1) {
+ hist_info->read_request++;
+ if (is_hist_v2) {
+ hist_info->col_state = HIST_READY;
+ writel_relaxed(1, hist_info->base);
+ }
+ need_complete = true;
+ }
spin_unlock(&hist_info->hist_lock);
- if (hist_info->read_request)
+ if (need_complete)
complete(&hist_info->comp);
}
/* Histogram Reset Done Interrupt */
@@ -3543,6 +3678,53 @@ static struct msm_fb_data_type *mdss_get_mfd_from_index(int index)
return out;
}
+static int pp_num_to_side(struct mdss_mdp_ctl *ctl, u32 num)
+{
+ u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
+ u32 mixer_num;
+
+ if (!ctl || !ctl->mfd)
+ return -EINVAL;
+ mixer_num = mdss_mdp_get_ctl_mixers(ctl->mfd->index, mixer_id);
+ if (mixer_num < 2)
+ return MDSS_SIDE_NONE;
+ else if (mixer_id[1] == num)
+ return MDSS_SIDE_RIGHT;
+ else if (mixer_id[0] == num)
+ return MDSS_SIDE_LEFT;
+ else
+ pr_err("invalid, not on any side");
+ return -EINVAL;
+}
+
+static inline void pp_sts_set_split_bits(u32 *sts, u32 bits)
+{
+ u32 tmp = *sts;
+ tmp &= ~MDSS_PP_SPLIT_MASK;
+ tmp |= bits & MDSS_PP_SPLIT_MASK;
+ *sts = tmp;
+}
+
+static inline bool pp_sts_is_enabled(u32 sts, int side)
+{
+ bool ret = false;
+ /*
+ * If there are no sides, or if there are no split mode bits set, the
+ * side can't be disabled via split mode.
+ *
+ * Otherwise, if the side being checked opposes the split mode
+ * configuration, the side is disabled.
+ */
+ if ((side == MDSS_SIDE_NONE) || !(sts & MDSS_PP_SPLIT_MASK))
+ ret = true;
+ else if ((sts & MDSS_PP_SPLIT_RIGHT_ONLY) && (side == MDSS_SIDE_RIGHT))
+ ret = true;
+ else if ((sts & MDSS_PP_SPLIT_LEFT_ONLY) && (side == MDSS_SIDE_LEFT))
+ ret = true;
+
+ return ret && (sts & PP_STS_ENABLE);
+}
+
static int mdss_ad_init_checks(struct msm_fb_data_type *mfd)
{
u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
@@ -3638,7 +3820,7 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
struct mdss_ad_info *ad;
struct msm_fb_data_type *bl_mfd;
int lin_ret = -1, inv_ret = -1, ret = 0;
- u32 ratio_temp, shift = 0;
+ u32 ratio_temp, shift = 0, last_ops;
ret = mdss_mdp_get_ad(mfd, &ad);
if (ret)
@@ -3651,6 +3833,11 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
bl_mfd = mfd;
}
+ if ((init_cfg->ops & MDSS_PP_SPLIT_MASK) == MDSS_PP_SPLIT_MASK) {
+ pr_warn("Can't set both split bits\n");
+ return -EINVAL;
+ }
+
mutex_lock(&ad->lock);
if (init_cfg->ops & MDP_PP_AD_INIT) {
memcpy(&ad->init, &init_cfg->params.init,
@@ -3693,6 +3880,22 @@ int mdss_mdp_ad_config(struct msm_fb_data_type *mfd,
ad->sts |= PP_AD_STS_DIRTY_CFG;
}
+ last_ops = ad->ops & MDSS_PP_SPLIT_MASK;
+ ad->ops = init_cfg->ops & MDSS_PP_SPLIT_MASK;
+ /*
+ * if there is a change in the split mode config, the init values
+ * need to be re-written to hardware (if they have already been
+ * written or if there is data pending to be written). Check for
+ * pending data (DIRTY_INIT) is not checked here since it will not
+ * affect the outcome of this conditional (i.e. if init hasn't
+ * already been written (*_STATE_INIT is set), this conditional will
+ * only evaluate to true (and set the DIRTY bit) if the DIRTY bit has
+ * already been set).
+ */
+ if ((last_ops ^ ad->ops) && (ad->state & PP_AD_STATE_INIT))
+ ad->sts |= PP_AD_STS_DIRTY_INIT;
+
+
if (!ret && (init_cfg->ops & MDP_PP_OPS_DISABLE)) {
ad->sts &= ~PP_STS_ENABLE;
mutex_unlock(&ad->lock);
@@ -3778,7 +3981,7 @@ int mdss_mdp_ad_input(struct msm_fb_data_type *mfd,
mutex_unlock(&ad->lock);
mutex_lock(&mfd->bl_lock);
MDSS_BRIGHT_TO_BL(bl, bl, mfd->panel_info->bl_max,
- MDSS_MAX_BL_BRIGHTNESS);
+ mfd->panel_info->brightness_max);
mdss_fb_set_backlight(mfd, bl);
mutex_unlock(&mfd->bl_lock);
mutex_lock(&ad->lock);
@@ -3847,8 +4050,9 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad,
u32 temp;
u32 frame_start, frame_end, procs_start, procs_end, tile_ctrl;
u32 num;
+ int side;
char __iomem *base;
- bool is_calc, is_dual_pipe;
+ bool is_calc, is_dual_pipe, split_mode;
u32 mixer_id[MDSS_MDP_INTF_MAX_LAYERMIXER];
u32 mixer_num;
mixer_num = mdss_mdp_get_ctl_mixers(ctl->mfd->index, mixer_id);
@@ -3859,6 +4063,7 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad,
base = ad_hw->base;
is_calc = ad->calc_hw_num == ad_hw->num;
+ split_mode = !!(ad->ops & MDSS_PP_SPLIT_MASK);
writel_relaxed(ad->init.i_control[0] & 0x1F,
base + MDSS_MDP_REG_AD_CON_CTRL_0);
@@ -3884,7 +4089,10 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad,
writel_relaxed(ad->init.format, base + MDSS_MDP_REG_AD_CTRL_0);
writel_relaxed(ad->init.auto_size, base + MDSS_MDP_REG_AD_CTRL_1);
- temp = ad->init.frame_w << 16;
+ if (split_mode)
+ temp = mdata->mixer_intf[ad_hw->num].width << 16;
+ else
+ temp = ad->init.frame_w << 16;
temp |= ad->init.frame_h & 0xFFFF;
writel_relaxed(temp, base + MDSS_MDP_REG_AD_FRAME_SIZE);
@@ -3896,17 +4104,26 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad,
pp_ad_cfg_lut(base + MDSS_MDP_REG_AD_LUT_CC, ad->init.color_corr_lut);
if (mdata->mdp_rev >= MDSS_MDP_HW_REV_103) {
- if (is_dual_pipe) {
+ if (is_dual_pipe && !split_mode) {
num = ad_hw->num;
+ side = pp_num_to_side(ctl, num);
tile_ctrl = 0x5;
- if (is_calc) {
+ if ((ad->calc_hw_num + 1) == num)
+ tile_ctrl |= 0x10;
+
+ if (side <= MDSS_SIDE_NONE) {
+ WARN(1, "error finding sides, %d", side);
+ frame_start = 0;
+ procs_start = frame_start;
+ frame_end = 0;
+ procs_end = frame_end;
+ } else if (side == MDSS_SIDE_LEFT) {
frame_start = 0;
procs_start = 0;
frame_end = mdata->mixer_intf[num].width +
MDSS_AD_MERGED_WIDTH;
procs_end = mdata->mixer_intf[num].width;
} else {
- tile_ctrl |= 0x10;
procs_start = ad->init.frame_w -
(mdata->mixer_intf[num].width);
procs_end = ad->init.frame_w;
@@ -3921,8 +4138,13 @@ static void pp_ad_init_write(struct mdss_mdp_ad *ad_hw, struct mdss_ad_info *ad,
frame_end = 0xFFFF;
procs_start = 0x0;
procs_end = 0xFFFF;
- tile_ctrl = 0x1;
+ if (split_mode)
+ tile_ctrl = 0x0;
+ else
+ tile_ctrl = 0x1;
}
+
+
writel_relaxed(frame_start, base + MDSS_MDP_REG_AD_FRAME_START);
writel_relaxed(frame_end, base + MDSS_MDP_REG_AD_FRAME_END);
writel_relaxed(procs_start, base + MDSS_MDP_REG_AD_PROCS_START);
@@ -3986,12 +4208,17 @@ static void pp_ad_vsync_handler(struct mdss_mdp_ctl *ctl, ktime_t t)
}
#define MDSS_PP_AD_BYPASS_DEF 0x101
-static void pp_ad_bypass_config(struct mdss_ad_info *ad, u32 *opmode)
+static void pp_ad_bypass_config(struct mdss_ad_info *ad,
+ struct mdss_mdp_ctl *ctl, u32 num, u32 *opmode)
{
- if (ad->reg_sts & PP_STS_ENABLE)
+ int side = pp_num_to_side(ctl, num);
+
+ if (pp_sts_is_enabled(ad->reg_sts | (ad->ops & MDSS_PP_SPLIT_MASK),
+ side)) {
*opmode = 0;
- else
+ } else {
*opmode = MDSS_PP_AD_BYPASS_DEF;
+ }
}
static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd,
@@ -4006,6 +4233,8 @@ static int pp_ad_setup_hw_nums(struct msm_fb_data_type *mfd,
/* default to left mixer */
ad->calc_hw_num = mixer_id[0];
+ if ((mixer_num > 1) && (ad->ops & MDSS_PP_SPLIT_RIGHT_ONLY))
+ ad->calc_hw_num = mixer_id[1];
return 0;
}
@@ -4313,6 +4542,7 @@ int mdss_mdp_ad_addr_setup(struct mdss_data_type *mdata, u32 *ad_offsets)
mdata->ad_off[i].base = mdata->mdp_base + ad_offsets[i];
mdata->ad_off[i].num = i;
mdata->ad_cfgs[i].num = i;
+ mdata->ad_cfgs[i].ops = 0;
mdata->ad_cfgs[i].reg_sts = 0;
mdata->ad_cfgs[i].calc_itr = 0;
mdata->ad_cfgs[i].last_str = 0xFFFFFFFF;
@@ -4421,7 +4651,10 @@ static int is_valid_calib_vig_addr(char __iomem *ptr)
pipe = mdss_res->vig_pipes + counter;
base = pipe->base;
- if (ptr == base + MDSS_MDP_REG_SSPP_SRC_FORMAT) {
+ if (ptr == base + MDSS_MDP_REG_VIG_OP_MODE) {
+ ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
+ break;
+ } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_FORMAT) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR) {
@@ -4430,6 +4663,9 @@ static int is_valid_calib_vig_addr(char __iomem *ptr)
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
+ } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) {
+ ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
+ break;
} else if ((ptr == base + MDSS_MDP_REG_VIG_QSEED2_SHARP)) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
@@ -4477,6 +4713,9 @@ static int is_valid_calib_rgb_addr(char __iomem *ptr)
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
+ } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) {
+ ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
+ break;
/* IGC range */
} else if ((ptr >= base + MDSS_MDP_REG_IGC_RGB_BASE) &&
(ptr <= base + MDSS_MDP_REG_IGC_RGB_BASE +
@@ -4508,6 +4747,9 @@ static int is_valid_calib_dma_addr(char __iomem *ptr)
} else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN) {
ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
break;
+ } else if (ptr == base + MDSS_MDP_REG_SSPP_SRC_OP_MODE) {
+ ret = MDP_PP_OPS_READ | MDP_PP_OPS_WRITE;
+ break;
/* IGC range */
} else if ((ptr >= base + MDSS_MDP_REG_IGC_DMA_BASE) &&
(ptr <= base + MDSS_MDP_REG_IGC_DMA_BASE +
diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h
index 48ff538406ce..59ff4959f201 100644
--- a/drivers/video/msm/mdss/mdss_panel.h
+++ b/drivers/video/msm/mdss/mdss_panel.h
@@ -287,6 +287,7 @@ struct mdss_panel_info {
u32 type;
u32 wait_cycle;
u32 pdest;
+ u32 brightness_max;
u32 bl_max;
u32 bl_min;
u32 fb_num;
diff --git a/drivers/video/msm/mdss/splash.h b/drivers/video/msm/mdss/splash.h
new file mode 100644
index 000000000000..dc8a473cb56c
--- /dev/null
+++ b/drivers/video/msm/mdss/splash.h
@@ -0,0 +1,5279 @@
+/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#ifndef __SPLASH_H_
+#define __SPLASH_H_
+
+#define SPLASH_IMAGE_WIDTH 113
+#define SPLASH_IMAGE_HEIGHT 124
+#define SPLASH_IMAGE_FORMAT MDP_BGR_888
+#define SPLASH_IMAGE_BPP 3
+
+char splash_bgr888_image[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x29, 0x19, 0x31, 0x31,
+ 0x29, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x31, 0x31,
+ 0x29, 0x4a, 0x52, 0x4a, 0x6b, 0x5a, 0x73, 0x4a, 0x52, 0x4a, 0x10, 0x29,
+ 0x19, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x31, 0x31, 0x29, 0x6b, 0x5a, 0x73, 0x6b, 0x7b, 0x73, 0x6b, 0x5a,
+ 0x4a, 0x31, 0x31, 0x29,
+ 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x6b, 0x5a, 0x4a, 0x6b, 0x5a,
+ 0x73, 0x3a, 0x31, 0x4a,
+ 0x31, 0x31, 0x29, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x3a, 0x31,
+ 0x4a, 0x31, 0x31, 0x29,
+ 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x21, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08,
+ 0x10, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x08, 0x08, 0x10,
+ 0x08, 0x08, 0x10, 0x10,
+ 0x29, 0x19, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10,
+ 0x4a, 0x52, 0x4a, 0x08,
+ 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x08,
+ 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x29, 0x19, 0x4a, 0x52, 0x4a, 0x3a, 0x31, 0x4a,
+ 0x08, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0x9c, 0xa5, 0x94, 0x9c, 0x7b, 0x94, 0x08, 0x08, 0x10, 0x08,
+ 0x08, 0x10, 0x10, 0x29,
+ 0x19, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x10, 0x21, 0x00,
+ 0x08, 0x08, 0x10, 0x6b, 0x7b, 0x73, 0x9c, 0x7b, 0x94, 0x9c, 0xa5, 0x94,
+ 0xce, 0xad, 0xad, 0xa5,
+ 0xb5, 0xb5, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0xa5, 0x9c, 0xad, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0x9c,
+ 0x7b, 0x94, 0x10, 0x29,
+ 0x19, 0x3a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x94, 0x9c, 0xa5, 0x94, 0xa5, 0xb5, 0xb5,
+ 0xa5, 0xb5, 0xb5, 0xce,
+ 0xde, 0xce, 0xc5, 0xad, 0xd6, 0x9c, 0xa5, 0x94, 0x3a, 0x10, 0x21, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x9c, 0x7b, 0x94, 0xce, 0xad, 0xad, 0xce, 0xe6, 0xef, 0xce,
+ 0xe6, 0xef, 0xe6, 0xde,
+ 0xde, 0xa5, 0x9c, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xce, 0xde, 0xce, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xde, 0xce, 0x6b,
+ 0x7b, 0x73, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x29, 0x19, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0x9c,
+ 0xa5, 0x94, 0xce, 0xde,
+ 0xce, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff,
+ 0xff, 0xf7, 0xff, 0x9c,
+ 0xa5, 0x94, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0xef, 0xf7, 0xe6, 0xff,
+ 0xf7, 0xff, 0xef, 0xde,
+ 0xef, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xf7, 0xff, 0x10,
+ 0x29, 0x19, 0x08, 0x08,
+ 0x10, 0x4a, 0x52, 0x4a, 0xce, 0xad, 0xad, 0xff, 0xff, 0xff, 0x4a, 0x52,
+ 0x4a, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0xce, 0xad, 0xad,
+ 0xef, 0xf7, 0xff, 0xce,
+ 0xde, 0xce, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x94, 0x31,
+ 0x31, 0x29, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xef,
+ 0xf7, 0xe6, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x6b, 0x7b, 0x73, 0x08, 0x08, 0x10, 0xff, 0xff,
+ 0xff, 0x6b, 0x7b, 0x73,
+ 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10,
+ 0xce, 0xde, 0xce, 0xff,
+ 0xff, 0xff, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31,
+ 0x31, 0x29, 0x4a, 0x52,
+ 0x4a, 0xa5, 0xb5, 0xb5, 0xff, 0xff, 0xff, 0x9c, 0x7b, 0x94, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0xe6, 0xde,
+ 0xde, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0x08, 0x08,
+ 0x10, 0xff, 0xff, 0xff,
+ 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x9c,
+ 0x7b, 0x94, 0xff, 0xff, 0xff, 0x3a, 0x10, 0x21, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x08, 0x08, 0x10, 0x6b, 0x5a, 0x73, 0xff, 0xff, 0xff, 0xa5, 0xb5,
+ 0xb5, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x4a, 0x52,
+ 0x4a, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0xe6, 0xde, 0xde, 0x08, 0x31, 0x5a, 0x10, 0x7b, 0x9c, 0x10, 0x7b, 0x9c,
+ 0x10, 0x7b, 0x9c, 0x10,
+ 0x52, 0x7b, 0x31, 0x31, 0x29, 0xef, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xa5, 0x94, 0xff, 0xff,
+ 0xff, 0xa5, 0x9c, 0xad,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x21, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x08, 0x08, 0x10, 0xff, 0xf7, 0xff, 0x4a, 0x52, 0x4a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x31, 0x5a, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef,
+ 0x00, 0xbd, 0xef, 0x00,
+ 0x9c, 0xd6, 0x08, 0xa5, 0xad, 0x08, 0xad, 0xd6, 0x10, 0xce, 0xce, 0x6b,
+ 0x7b, 0x9c, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0x6b, 0x5a, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0xce, 0xde, 0xce, 0xff, 0xf7,
+ 0xff, 0x10, 0x29, 0x19,
+ 0x10, 0x5a, 0x9c, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd,
+ 0xef, 0x08, 0xa5, 0xad, 0x08, 0x31, 0x5a, 0x10, 0x29, 0x19, 0xff, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xe6, 0xde, 0xde, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a,
+ 0x73, 0xef, 0xf7, 0xe6,
+ 0x19, 0x7b, 0xbd, 0x19, 0x7b, 0xbd, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef,
+ 0x10, 0xc5, 0xef, 0x00,
+ 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x3a, 0xde, 0xef, 0x19,
+ 0xbd, 0xf7, 0x3a, 0xde,
+ 0xef, 0x3a, 0xde, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad,
+ 0xd6, 0x19, 0x94, 0xce,
+ 0xa5, 0xb5, 0xb5, 0x4a, 0x5a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10,
+ 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6,
+ 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x19,
+ 0xbd, 0xf7, 0x3a, 0xde,
+ 0xef, 0x3a, 0xde, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xe6,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xd6, 0x08, 0x31, 0x3a,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10,
+ 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6,
+ 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10,
+ 0xc5, 0xef, 0x6b, 0xe6,
+ 0xef, 0x3a, 0xde, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5,
+ 0xef, 0x10, 0xe6, 0xef,
+ 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7,
+ 0x19, 0x7b, 0xbd, 0x00,
+ 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x31, 0x5a, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6,
+ 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10,
+ 0xc5, 0xef, 0x10, 0xe6,
+ 0xef, 0x3a, 0xde, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xe6,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c,
+ 0x00, 0xbd, 0xef, 0x10,
+ 0x73, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x52, 0x7b, 0x19, 0x7b, 0xbd, 0x00, 0x9c, 0xd6,
+ 0x19, 0x94, 0xce, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10,
+ 0xe6, 0xef, 0x19, 0xbd,
+ 0xf7, 0x6b, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xe6,
+ 0xef, 0x10, 0xe6, 0xef,
+ 0x10, 0xc5, 0xef, 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6,
+ 0x19, 0x94, 0xce, 0x00,
+ 0x9c, 0xd6, 0x10, 0x52, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x31, 0x5a,
+ 0x08, 0xa5, 0xad, 0x08,
+ 0xad, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10,
+ 0xc5, 0xef, 0x10, 0xc5,
+ 0xef, 0x10, 0xe6, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xc5,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x19, 0xbd, 0xf7, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce,
+ 0x08, 0xad, 0xd6, 0x00,
+ 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x31, 0x3a, 0x10,
+ 0x52, 0x7b, 0x10, 0x7b, 0x9c, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x10, 0xc5,
+ 0xef, 0x3a, 0xde, 0xef, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6,
+ 0xef, 0x08, 0xad, 0xd6,
+ 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce,
+ 0x00, 0x9c, 0xd6, 0x00,
+ 0x9c, 0xd6, 0x19, 0x7b, 0xbd, 0x19, 0x7b, 0xbd, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10, 0x9c, 0x7b, 0x73, 0x6b, 0x5a, 0x73, 0x10, 0x29,
+ 0x19, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x73, 0xa5, 0xad, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x00,
+ 0x84, 0xbd, 0x08, 0xa5,
+ 0xad, 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x73,
+ 0x7b, 0x10, 0x5a, 0x9c,
+ 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6,
+ 0x00, 0x9c, 0xd6, 0x00,
+ 0x84, 0xbd, 0x3a, 0xa5, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0x6b,
+ 0x5a, 0x73, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x6b, 0x7b,
+ 0x9c, 0x9c, 0x7b, 0x73,
+ 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x31, 0x29, 0xc5, 0xad, 0xd6, 0x52, 0xa5, 0xa5, 0x10,
+ 0x5a, 0x9c, 0x10, 0x7b,
+ 0x9c, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x19, 0x94,
+ 0xce, 0x00, 0x9c, 0xd6,
+ 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce,
+ 0x00, 0x84, 0xbd, 0x19,
+ 0x7b, 0xbd, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xa5,
+ 0xb5, 0xb5, 0xa5, 0xb5,
+ 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21,
+ 0x00, 0x6b, 0x7b, 0x73,
+ 0x9c, 0x7b, 0x94, 0x6b, 0x7b, 0x73, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0xa5, 0xd6, 0xad, 0xc5,
+ 0xad, 0xd6, 0x4a, 0x7b,
+ 0x9c, 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x00, 0x84,
+ 0xbd, 0x00, 0x9c, 0xd6,
+ 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd,
+ 0x19, 0x7b, 0xbd, 0x73,
+ 0xa5, 0xad, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xe6,
+ 0xde, 0xde, 0xce, 0xde,
+ 0xce, 0xce, 0xad, 0xad, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x4a, 0x52, 0x4a, 0x6b, 0x7b, 0x73, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x4a, 0xce,
+ 0xad, 0xad, 0xce, 0xde,
+ 0xce, 0xa5, 0xb5, 0xb5, 0x73, 0xa5, 0xad, 0x10, 0x73, 0x7b, 0x10, 0x5a,
+ 0x9c, 0x10, 0x7b, 0x9c,
+ 0x10, 0x5a, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c,
+ 0x19, 0x7b, 0xbd, 0xa5,
+ 0xb5, 0xb5, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xef,
+ 0xde, 0xef, 0xef, 0xf7,
+ 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xa5, 0x9c, 0xad, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0xe6, 0xde,
+ 0xde, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xad, 0xad, 0x9c, 0xad,
+ 0xce, 0x52, 0xa5, 0xa5,
+ 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x52, 0xa5, 0xa5,
+ 0x73, 0xa5, 0xad, 0xc5,
+ 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xef,
+ 0xde, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7,
+ 0xe6, 0x08, 0x08, 0x10,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xef, 0xf7,
+ 0xe6, 0xef, 0xf7, 0xe6, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce, 0xa5, 0xb5,
+ 0xb5, 0xce, 0xde, 0xce,
+ 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce,
+ 0xce, 0xad, 0xad, 0xc5,
+ 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xe6,
+ 0xde, 0xde, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xa5, 0xb5, 0xb5, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x21, 0x00, 0x00,
+ 0x00, 0x00, 0x9c, 0xa5,
+ 0x94, 0xff, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xc5, 0xad,
+ 0xd6, 0xa5, 0xb5, 0xb5,
+ 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6, 0xa5, 0xb5, 0xb5, 0xce, 0xad, 0xad,
+ 0xc5, 0xad, 0xd6, 0xa5,
+ 0xd6, 0xad, 0xc5, 0xad, 0xd6, 0xce, 0xde, 0xce, 0xef, 0xde, 0xef, 0xef,
+ 0xf7, 0xe6, 0xff, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x08,
+ 0x08, 0x10, 0x10, 0x29,
+ 0x19, 0xef, 0xde, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xc5, 0xad, 0xd6, 0xa5, 0xd6, 0xad, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce,
+ 0xa5, 0xb5, 0xb5, 0xce,
+ 0xde, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xef, 0xf7, 0xe6, 0xef,
+ 0xf7, 0xff, 0xff, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde,
+ 0x08, 0x08, 0x10, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x21,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6,
+ 0xa5, 0xb5, 0xb5, 0xce,
+ 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xff,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6,
+ 0xe6, 0xde, 0xde, 0xef,
+ 0xf7, 0xe6, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xa5, 0x9c, 0xad, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08,
+ 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0xce, 0xde, 0xce, 0xef, 0xde,
+ 0xef, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef,
+ 0xf7, 0xff, 0x31, 0x31,
+ 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0xce, 0xad,
+ 0xad, 0xef, 0xf7, 0xe6,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xef,
+ 0xde, 0xef, 0xef, 0xf7,
+ 0xe6, 0x9c, 0xa5, 0x94, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31,
+ 0x29, 0xa5, 0xb5, 0xb5,
+ 0xce, 0xde, 0xce, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xef, 0xde, 0xef, 0xef,
+ 0xf7, 0xe6, 0xe6, 0xde,
+ 0xde, 0xce, 0xe6, 0xef, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6,
+ 0xef, 0xf7, 0xe6, 0xef,
+ 0xde, 0xef, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xe6, 0xde, 0xde, 0xce,
+ 0xde, 0xce, 0xce, 0xde,
+ 0xce, 0xe6, 0xde, 0xde, 0xce, 0xad, 0xad, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x10, 0x21, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x6b, 0x5a, 0x4a,
+ 0x73, 0xa5, 0xad, 0xce, 0xad, 0xad, 0xc5, 0xad, 0xd6, 0xe6, 0xde, 0xde,
+ 0xe6, 0xde, 0xde, 0xff,
+ 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef,
+ 0xde, 0xef, 0xce, 0xde,
+ 0xce, 0xef, 0xde, 0xef, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xde, 0xef,
+ 0xce, 0xde, 0xce, 0xc5,
+ 0xad, 0xd6, 0xce, 0xde, 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5,
+ 0xb5, 0xb5, 0xc5, 0xad,
+ 0xd6, 0xa5, 0xb5, 0xb5, 0xc5, 0xad, 0xd6, 0xef, 0xf7, 0xff, 0x4a, 0x52,
+ 0x4a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x10, 0x29, 0x19,
+ 0x6b, 0x5a, 0x73, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xce, 0xde, 0xce,
+ 0xef, 0xf7, 0xe6, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xef, 0xde, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xce, 0xde, 0xce, 0xc5,
+ 0xad, 0xd6, 0xce, 0xde,
+ 0xce, 0xce, 0xad, 0xad, 0xce, 0xde, 0xce, 0xa5, 0xb5, 0xb5, 0xce, 0xde,
+ 0xce, 0xff, 0xf7, 0xff,
+ 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a,
+ 0x31, 0x31, 0x29, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0xa5, 0x9c, 0xad, 0xce, 0xde, 0xce, 0xce, 0xe6, 0xef,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xce, 0xde, 0xce, 0xc5, 0xad,
+ 0xd6, 0xce, 0xad, 0xad,
+ 0xe6, 0xde, 0xde, 0xff, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x31, 0x31, 0x29, 0x3a, 0x10, 0x21, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31,
+ 0x29, 0x00, 0x00, 0x00,
+ 0x10, 0x21, 0x00, 0x6b, 0x5a, 0x73, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xce, 0xde, 0xce,
+ 0xce, 0xde, 0xce, 0x9c, 0xad, 0xce, 0xef, 0xf7, 0xe6, 0xa5, 0x9c, 0xad,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x31, 0x4a, 0x10,
+ 0x29, 0x19, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52,
+ 0x4a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6,
+ 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xf7, 0xff,
+ 0xef, 0xf7, 0xff, 0xef, 0xde, 0xef, 0xce, 0xe6, 0xad, 0xc5, 0xad, 0xd6,
+ 0xff, 0xff, 0xff, 0x10,
+ 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x31,
+ 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x29,
+ 0x19, 0x08, 0x08, 0x10,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xde, 0xef,
+ 0xce, 0xde, 0xce, 0xce,
+ 0xde, 0xce, 0xce, 0xad, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ 0x21, 0x00, 0x08, 0x08,
+ 0x10, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x4a, 0x52, 0x4a,
+ 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xf7, 0xe6,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xde, 0xef, 0xce, 0xde, 0xce, 0xef, 0xf7, 0xff, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x3a, 0x10,
+ 0x21, 0x31, 0x31, 0x29, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10, 0x10, 0x29,
+ 0x19, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x29, 0x19,
+ 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x7b, 0x94,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0x4a,
+ 0x52, 0x4a, 0x00, 0x00,
+ 0x00, 0x10, 0x29, 0x19, 0x4a, 0x52, 0x4a, 0x31, 0x31, 0x29, 0x08, 0x08,
+ 0x10, 0x3a, 0x31, 0x4a,
+ 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xce, 0xde,
+ 0xce, 0x3a, 0x10, 0x21, 0x10, 0x29, 0x19, 0x3a, 0x31, 0x4a, 0x3a, 0x10,
+ 0x21, 0x10, 0x21, 0x00,
+ 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x9c, 0x7b, 0x94, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0x3a, 0x10, 0x21, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29,
+ 0x08, 0x08, 0x10, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xe6, 0xde, 0xde,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4a, 0x52,
+ 0x4a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x31,
+ 0x31, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31, 0x29, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xe6, 0xde, 0xde, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0x9c, 0x7b, 0x94,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x29, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xce,
+ 0xe6, 0xef, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xa5, 0xb5, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x31, 0x29, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf7, 0xff, 0xef,
+ 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xce, 0xde, 0xce, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x31,
+ 0x31, 0x29, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0xce, 0xad, 0xad, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xde, 0xef, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10,
+ 0x21, 0x00, 0x3a, 0x31, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xce,
+ 0xe6, 0xef, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xce, 0xe6, 0xef, 0xe6, 0xde, 0xde, 0xef,
+ 0xf7, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x3a,
+ 0x31, 0x4a, 0x08, 0x08,
+ 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x4a, 0x52, 0x4a, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xe6,
+ 0xde, 0xde, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x4a, 0x52,
+ 0x4a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x10, 0x21, 0x4a, 0x52, 0x4a, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce,
+ 0xe6, 0xef, 0xe6, 0xde,
+ 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x10, 0x29, 0x19, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x3a, 0x31, 0x4a, 0x6b,
+ 0x7b, 0x73, 0x08, 0x00,
+ 0x00, 0x10, 0x29, 0x19, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xe6, 0xde,
+ 0xde, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x29, 0x19, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x29, 0x19, 0x6b, 0x5a,
+ 0x73, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0xff, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xe6, 0xde, 0xde, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x31, 0x31, 0x29, 0x08, 0x00, 0x00, 0x31, 0x31, 0x29, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xff, 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xe6, 0xde, 0xde, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x4a, 0x31, 0x31,
+ 0x29, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xce, 0xe6,
+ 0xef, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff, 0xff, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa5,
+ 0xb5, 0xb5, 0x00, 0x00,
+ 0x00, 0x10, 0x21, 0x00, 0x3a, 0x31, 0x4a, 0x10, 0x29, 0x19, 0x3a, 0x08,
+ 0x00, 0x08, 0x08, 0x10,
+ 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x31, 0x31, 0x29, 0x10, 0x29, 0x19,
+ 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x10,
+ 0x7b, 0x9c, 0x10, 0xc5,
+ 0xef, 0x10, 0xc5, 0xef, 0x10, 0x7b, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x4a, 0x52, 0x4a,
+ 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde,
+ 0xde, 0xef, 0xf7, 0xe6,
+ 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x6b, 0x5a,
+ 0x4a, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x3a, 0x10, 0x21, 0x6b,
+ 0x5a, 0x73, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19,
+ 0x94, 0xce, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad,
+ 0xd6, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x4a, 0x5a, 0x73, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xe6, 0xde, 0xde,
+ 0xef, 0xde, 0xef, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xce,
+ 0xe6, 0xef, 0xce, 0xe6,
+ 0xef, 0x08, 0x10, 0x42, 0x10, 0x29, 0x19, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x31, 0x29, 0x4a, 0x52, 0x4a, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19,
+ 0x7b, 0xbd, 0x00, 0x9c,
+ 0xd6, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0x9c, 0xd6, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x4a, 0x52, 0x4a,
+ 0xef, 0xde, 0xef, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff,
+ 0xe6, 0xde, 0xde, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0x08, 0xad, 0xd6, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x08, 0x08, 0x10, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x31, 0x31, 0x29, 0x10, 0x29, 0x19, 0x3a,
+ 0x10, 0x21, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x84,
+ 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x31,
+ 0x31, 0x29, 0xe6, 0xde, 0xde, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0xe6, 0xef, 0x08,
+ 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x10, 0xe6, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x08, 0x08,
+ 0x10, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x29, 0x19, 0x10, 0x29, 0x19, 0x00,
+ 0x00, 0x00, 0x00, 0x84,
+ 0xbd, 0x10, 0xe6, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x19, 0x7b,
+ 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef,
+ 0x10, 0x5a, 0x9c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x29, 0x19, 0xce, 0xde, 0xce, 0xff,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe6, 0xde, 0xde, 0xef, 0xde, 0xef,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xce,
+ 0xe6, 0xef, 0x00, 0x9c,
+ 0xd6, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad,
+ 0xd6, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x08, 0x31,
+ 0x3a, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x10, 0xc5, 0xef, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x84,
+ 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x00, 0xbd, 0xef,
+ 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x00,
+ 0xbd, 0xef, 0x08, 0x31, 0x3a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x9c, 0xa5,
+ 0x94, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xce, 0xe6,
+ 0xef, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0x94, 0xce,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x19, 0x94, 0xce, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0xbd, 0x00,
+ 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x19,
+ 0x94, 0xce, 0x00, 0x84,
+ 0xbd, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x4a, 0x52, 0x4a, 0xff, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xef, 0xde,
+ 0xef, 0x9c, 0xde, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0x84, 0xbd, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x21, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x10, 0x5a, 0x9c, 0x08, 0xad, 0xd6, 0x00, 0xbd, 0xef, 0x10, 0xc5,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x19,
+ 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19,
+ 0x7b, 0xbd, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x31,
+ 0x29, 0xce, 0xe6, 0xef,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xce, 0xe6,
+ 0xef, 0xce, 0xad, 0xad, 0x9c, 0xad, 0xce, 0x19, 0x94, 0xce, 0x08, 0xad,
+ 0xef, 0x00, 0xbd, 0xef,
+ 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x10, 0x5a, 0x9c, 0x08, 0x08, 0x10,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x08,
+ 0x10, 0x10, 0x73, 0x7b, 0x00, 0x9c, 0xd6, 0x00, 0xbd, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0xbd, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x10, 0xce,
+ 0xce, 0x08, 0x31, 0x3a, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x08, 0x10, 0xce, 0xde, 0xce, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xc5, 0xad, 0xd6, 0xce, 0xde, 0xce, 0x9c, 0xad, 0xce, 0x08, 0xad,
+ 0xd6, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd,
+ 0x10, 0x7b, 0x9c, 0x08,
+ 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x08, 0x08, 0x10, 0x08, 0x31, 0x3a, 0x08,
+ 0x31, 0x3a, 0x10, 0x5a,
+ 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0xbd,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x7b, 0x94,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xa5, 0xb5, 0xb5, 0xce, 0xde, 0xce, 0x9c, 0xad,
+ 0xce, 0x19, 0x94, 0xce,
+ 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6,
+ 0x00, 0x9c, 0xd6, 0x00,
+ 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19,
+ 0x7b, 0xbd, 0x00, 0x84,
+ 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08,
+ 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xef, 0x10, 0x52,
+ 0x7b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x6b,
+ 0x7b, 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7,
+ 0xe6, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xce, 0xad, 0xad, 0xce, 0xde,
+ 0xce, 0xa5, 0xb5, 0xb5,
+ 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef,
+ 0x19, 0x94, 0xce, 0x00,
+ 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00,
+ 0x84, 0xbd, 0x00, 0x84,
+ 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x10, 0xc5, 0xef, 0x19,
+ 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x19,
+ 0x94, 0xce, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x08,
+ 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xd6,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xce, 0xde, 0xce, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xce, 0xde,
+ 0xce, 0xc5, 0xad, 0xd6,
+ 0xa5, 0xb5, 0xb5, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xd6,
+ 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x00,
+ 0x9c, 0xd6, 0x00, 0x9c,
+ 0xd6, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x84, 0xbd, 0x00, 0x9c,
+ 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xef,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x5a, 0x73, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xce, 0xde, 0xce,
+ 0xa5, 0xb5, 0xb5, 0x9c, 0xad, 0xce, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6,
+ 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19,
+ 0x94, 0xce, 0x08, 0xad,
+ 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad,
+ 0xef, 0x00, 0xbd, 0xef,
+ 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x10, 0x7b, 0xe6, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x00, 0xbd,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x08,
+ 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08,
+ 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x10, 0xe6, 0xef, 0x00, 0xbd, 0xef, 0x08, 0x31, 0x3a,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
+ 0x52, 0x4a, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0xff,
+ 0xe6, 0xde, 0xde, 0xc5, 0xad, 0xd6, 0x29, 0x5a, 0x4a, 0x00, 0x84, 0xbd,
+ 0x19, 0x94, 0xce, 0x08,
+ 0xad, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x08, 0xad, 0xd6, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x9c, 0xa5,
+ 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7,
+ 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xff, 0xf7, 0xff,
+ 0xef, 0xf7, 0xff, 0xce, 0xde, 0xce, 0x4a, 0x52, 0x4a, 0x08, 0x31, 0x5a,
+ 0x00, 0x84, 0xbd, 0x00,
+ 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x08, 0xad,
+ 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0xbd, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0xd6, 0x19, 0x94,
+ 0xce, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x10,
+ 0xc5, 0xef, 0x10, 0x5a, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x9c, 0x7b,
+ 0x94, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4a, 0x52, 0x4a, 0x08, 0x00, 0x00,
+ 0x08, 0x31, 0x5a, 0x00,
+ 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xef, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x94,
+ 0xce, 0x00, 0x9c, 0xd6,
+ 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0x73, 0x7b, 0xe6,
+ 0xde, 0xde, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0x31, 0x31, 0x29, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10,
+ 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x84, 0xbd,
+ 0x00, 0x9c, 0xd6, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xd6, 0x10, 0xc5, 0xef, 0x19,
+ 0x94, 0xce, 0x9c, 0xad,
+ 0xce, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xef, 0xf7, 0xff, 0xef, 0xde, 0xef, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08,
+ 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08,
+ 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x00, 0xbd, 0xf7,
+ 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x84, 0xbd,
+ 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x08, 0xad,
+ 0xd6, 0x10, 0x7b, 0x9c, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff,
+ 0xff, 0xef, 0xf7, 0xff,
+ 0xff, 0xff, 0xff, 0x9c, 0xa5, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x08,
+ 0xad, 0xd6, 0x00, 0xbd,
+ 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00,
+ 0xbd, 0xef, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xd6, 0x00, 0x84, 0xbd, 0x10, 0x52, 0x7b, 0xef, 0xf7,
+ 0xe6, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xff, 0xff, 0xf7, 0xff, 0xef, 0xf7, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf7, 0xff,
+ 0xce, 0xde, 0xce, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x52, 0x7b, 0x00,
+ 0x84, 0xbd, 0x19, 0x94,
+ 0xce, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x19, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xef, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xd6, 0x10, 0x52,
+ 0x7b, 0x3a, 0x31, 0x4a,
+ 0xef, 0xf7, 0xe6, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6,
+ 0xff, 0xff, 0xff, 0xef,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xef, 0xf7, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xce, 0xde, 0xce,
+ 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x10,
+ 0x52, 0x7b, 0x00, 0x84,
+ 0xbd, 0x00, 0x9c, 0xd6, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x00, 0xbd, 0xf7,
+ 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x19, 0x94, 0xce, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef,
+ 0x10, 0xc5, 0xef, 0x00,
+ 0xbd, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x00, 0x9c,
+ 0xd6, 0x10, 0x7b, 0x9c,
+ 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x4a, 0x5a, 0x73, 0xce, 0xde, 0xce,
+ 0xef, 0xf7, 0xe6, 0xff,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xef, 0xf7,
+ 0xe6, 0xef, 0xf7, 0xff, 0xef, 0xf7, 0xe6, 0xa5, 0xb5, 0xb5, 0x6b, 0x5a,
+ 0x73, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x31, 0x3a, 0x10, 0x52,
+ 0x7b, 0x19, 0x7b, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x10, 0xc5,
+ 0xef, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x7b, 0xbd, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6,
+ 0x19, 0x94, 0xce, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xf7, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x00, 0xbd,
+ 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x19,
+ 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xd6, 0x19, 0x94, 0xce,
+ 0x00, 0x84, 0xbd, 0x10, 0x52, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x08, 0x10, 0x10, 0x29, 0x19, 0x31, 0x31, 0x29, 0x31,
+ 0x31, 0x29, 0x08, 0x08,
+ 0x10, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x31,
+ 0x3a, 0x10, 0x52, 0x7b, 0x00, 0x84, 0xbd, 0x00, 0x9c, 0xd6, 0x08, 0xad,
+ 0xef, 0x10, 0xc5, 0xef,
+ 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad,
+ 0xef, 0x00, 0x84, 0xbd,
+ 0x19, 0x94, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x84, 0xbd,
+ 0x19, 0x94, 0xce, 0x00,
+ 0x9c, 0xd6, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xef, 0x08, 0xad, 0xd6, 0x08,
+ 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x00, 0x9c, 0xd6, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x08, 0x31, 0x3a,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x94,
+ 0xce, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7,
+ 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd, 0xf7, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x10,
+ 0xc5, 0xef, 0x00, 0xbd,
+ 0xef, 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x19, 0x94, 0xce, 0x00, 0x9c,
+ 0xd6, 0x00, 0x84, 0xbd,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x7b, 0x9c, 0x00,
+ 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x19,
+ 0x94, 0xce, 0x00, 0x9c,
+ 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad, 0xd6, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xd6,
+ 0x08, 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef,
+ 0x10, 0xc5, 0xef, 0x08,
+ 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x00, 0xbd, 0xf7, 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x10, 0xc5, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd,
+ 0xf7, 0x08, 0xad, 0xd6,
+ 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x52, 0x7b,
+ 0x08, 0x31, 0x5a, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x3a, 0x10, 0x52, 0x7b, 0x00, 0x84,
+ 0xbd, 0x00, 0x84, 0xbd,
+ 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x10, 0xc5, 0xef,
+ 0x00, 0xbd, 0xf7, 0x10,
+ 0xc5, 0xef, 0x08, 0xad, 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xf7, 0x08, 0xad,
+ 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x00, 0x9c, 0xd6, 0x00, 0x9c,
+ 0xd6, 0x10, 0x5a, 0x9c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10,
+ 0x5a, 0x9c, 0x00, 0x84,
+ 0xbd, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94,
+ 0xce, 0x00, 0x84, 0xbd,
+ 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef,
+ 0x19, 0x94, 0xce, 0x08,
+ 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xef, 0x19,
+ 0xbd, 0xf7, 0x00, 0xbd,
+ 0xef, 0x19, 0xbd, 0xf7, 0x08, 0xad, 0xef, 0x00, 0xbd, 0xf7, 0x10, 0xc5,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c,
+ 0x10, 0x52, 0x7b, 0x08,
+ 0x31, 0x3a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x10, 0x52,
+ 0x7b, 0x00, 0x84, 0xbd,
+ 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x08, 0xad, 0xd6, 0x08, 0xad, 0xef,
+ 0x19, 0xbd, 0xf7, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x00,
+ 0xbd, 0xef, 0x08, 0xad,
+ 0xef, 0x19, 0x94, 0xce, 0x08, 0xad, 0xef, 0x00, 0x84, 0xbd, 0x00, 0x84,
+ 0xbd, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x7b, 0x10, 0x5a, 0x9c, 0x10, 0x7b,
+ 0x9c, 0x10, 0x5a, 0xbd,
+ 0x00, 0x84, 0xbd, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd,
+ 0x00, 0x84, 0xbd, 0x00,
+ 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08,
+ 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c,
+ 0x10, 0x52, 0x7b, 0x10,
+ 0x52, 0x7b, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x08, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x21, 0x00, 0x08,
+ 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31,
+ 0x3a, 0x10, 0x52, 0x7b,
+ 0x10, 0x5a, 0x9c, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x08, 0xad, 0xef,
+ 0x08, 0xad, 0xd6, 0x08,
+ 0xad, 0xef, 0x08, 0xad, 0xef, 0x10, 0xc5, 0xef, 0x08, 0xad, 0xef, 0x08,
+ 0xad, 0xef, 0x19, 0x94,
+ 0xce, 0x00, 0x9c, 0xd6, 0x00, 0x84, 0xbd, 0x19, 0x7b, 0xbd, 0x10, 0x5a,
+ 0x9c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c,
+ 0x10, 0x5a, 0x9c, 0x10,
+ 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x00,
+ 0x84, 0xbd, 0x19, 0x94,
+ 0xce, 0x00, 0x9c, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x08, 0xad,
+ 0xef, 0x08, 0xad, 0xd6,
+ 0x19, 0x94, 0xce, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c,
+ 0x10, 0x52, 0x7b, 0x10,
+ 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x08, 0x10, 0x08, 0x08, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x31, 0x3a,
+ 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x19, 0x7b, 0xbd,
+ 0x00, 0x84, 0xbd, 0x00,
+ 0x9c, 0xd6, 0x08, 0xad, 0xd6, 0x19, 0x94, 0xce, 0x00, 0x9c, 0xd6, 0x19,
+ 0x94, 0xce, 0x00, 0x9c,
+ 0xd6, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a,
+ 0x9c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c, 0x10,
+ 0x5a, 0x9c, 0x10, 0x5a,
+ 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x00, 0x84, 0xbd, 0x19, 0x7b,
+ 0xbd, 0x00, 0x84, 0xbd,
+ 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x10, 0x5a, 0x9c,
+ 0x10, 0x52, 0x7b, 0x10,
+ 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c,
+ 0x00, 0x84, 0xbd, 0x00,
+ 0x84, 0xbd, 0x19, 0x7b, 0xbd, 0x00, 0x84, 0xbd, 0x00, 0x84, 0xbd, 0x00,
+ 0x84, 0xbd, 0x00, 0x84,
+ 0xbd, 0x19, 0x7b, 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52,
+ 0x7b, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x08, 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x10, 0x5a,
+ 0x9c, 0x10, 0x73, 0x7b,
+ 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b,
+ 0x10, 0x52, 0x7b, 0x10,
+ 0x52, 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a,
+ 0x10, 0x52, 0x7b, 0x10,
+ 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x00,
+ 0x84, 0xbd, 0x00, 0x84,
+ 0xbd, 0x10, 0x7b, 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x08, 0x31,
+ 0x5a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31,
+ 0x5a, 0x10, 0x52, 0x7b,
+ 0x08, 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x08, 0x31, 0x5a,
+ 0x10, 0x52, 0x7b, 0x08,
+ 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x31, 0x5a, 0x08,
+ 0x31, 0x5a, 0x10, 0x52, 0x7b, 0x10, 0x5a, 0x9c, 0x10, 0x5a, 0x9c, 0x10,
+ 0x5a, 0x9c, 0x10, 0x5a,
+ 0x9c, 0x10, 0x5a, 0x9c, 0x10, 0x52, 0x7b, 0x10, 0x52, 0x7b, 0x08, 0x31,
+ 0x5a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a,
+ 0x08, 0x31, 0x5a, 0x08,
+ 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x31, 0x3a, 0x08, 0x31, 0x5a, 0x08,
+ 0x31, 0x5a, 0x10, 0x52,
+ 0x7b, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x5a, 0x08, 0x31, 0x3a, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+};
+
+#endif